全志V3s 不论焊接还是使用很方便,唯一缺点就是IO不够,偶然发现联德盛 W806 竟然自带SDIO 设备接口,当然肯定还有ESP32模块 也是带的(,这里并不适用),选择SDIO 优点是速度快,方便扩展,调试这个W806 简直很要命,资料给的不全,很多时候就是猜测;

官方资料这里不放出来,到出都能找到,这里说一下 《SDIO Fn1地址映射关系》 这个表,

发送buffer 这里 使用的是地址 0x15000 ,注意:也就是 HOST 发送数据到W806 的地址,千万不要使用0x5000 ,0x16000-0x17fff 这里为 W806 发送数据到HOST 的地址,具体设备参见以下程序,

W806 SDIO配置程序 :

程序使用了一部分固定内存,在gcc-csky.ld 中定义 ,这里为SDIO 设备私有地址:

__min_heap_size = 0x18000;
PROVIDE (__ram_end  = 0x20041D90);
PROVIDE (__heap_end = __ram_end);

#ifndef SDIO_SALVE_H_
#define SDIO_SALVE_H_#include "stdint.h"#define TLS_IO_AB_OFFSET  (0x40011400 - 0x40011200)/** io name */
enum tls_io_name {WM_IO_PA_00 = 0,    /**< gpio a0 */WM_IO_PA_01,        /**< gpio a1 */WM_IO_PA_02,        /**< gpio a2 */WM_IO_PA_03,        /**< gpio a3 */WM_IO_PA_04,        /**< gpio a4 */WM_IO_PA_05,        /**< gpio a5 */WM_IO_PA_06,        /**< gpio a6 */WM_IO_PA_07,        /**< gpio a7 */WM_IO_PA_08,        /**< gpio a8 */WM_IO_PA_09,        /**< gpio a9 */WM_IO_PA_10,        /**< gpio a10 */WM_IO_PA_11,        /**< gpio a11 */WM_IO_PA_12,        /**< gpio a12 */WM_IO_PA_13,        /**< gpio a13 */WM_IO_PA_14,        /**< gpio a14 */WM_IO_PA_15,        /**< gpio a15 */WM_IO_PB_00,        /**< gpio b0 */WM_IO_PB_01,        /**< gpio b1 */WM_IO_PB_02,        /**< gpio b2 */WM_IO_PB_03,        /**< gpio b3 */WM_IO_PB_04,        /**< gpio b4 */WM_IO_PB_05,        /**< gpio b5 */WM_IO_PB_06,        /**< gpio b6 */WM_IO_PB_07,        /**< gpio b7 */WM_IO_PB_08,        /**< gpio b8 */WM_IO_PB_09,        /**< gpio b9 */WM_IO_PB_10,        /**< gpio b10 */WM_IO_PB_11,        /**< gpio b11 */WM_IO_PB_12,        /**< gpio b12 */WM_IO_PB_13,        /**< gpio b13 */WM_IO_PB_14,        /**< gpio b14 */WM_IO_PB_15,        /**< gpio b15 */WM_IO_PB_16,        /**< gpio b16 */WM_IO_PB_17,        /**< gpio b17 */WM_IO_PB_18,        /**< gpio b18 */WM_IO_PB_19,        /**< gpio b19 */WM_IO_PB_20,        /**< gpio b20 */WM_IO_PB_21,        /**< gpio b21 */WM_IO_PB_22,        /**< gpio b22 */WM_IO_PB_23,        /**< gpio b23 */WM_IO_PB_24,        /**< gpio b24 */WM_IO_PB_25,        /**< gpio b25 */WM_IO_PB_26,        /**< gpio b26 */WM_IO_PB_27,        /**< gpio b27 */WM_IO_PB_28,        /**< gpio b28 */WM_IO_PB_29,        /**< gpio b29 */WM_IO_PB_30,        /**< gpio b30 */WM_IO_PB_31         /**< gpio b31 */
};#define  tls_reg_write32(reg,val)   WRITE_REG(*(uint32_t*)(reg),val)
#define  tls_reg_read32(reg)        READ_REG(*(uint32_t*)(reg))/****************************************************************************** sdio/hspi sram partition******************************************************************************//*see gcc_csky.ld in directory ld/w800*/
extern unsigned int __heap_end;
extern unsigned int __heap_start;/* HSPI txbuf zone */
#define SLAVE_HSPI_SDIO_ADDR        ((unsigned int)(&__heap_end))/** SDIO interrupt bit definition */
#define SDIO_WP_INT_SRC_CMD_DOWN         (1UL<<3)
#define SDIO_WP_INT_SRC_CMD_UP           (1UL<<2)
#define SDIO_WP_INT_SRC_DATA_DOWN        (1UL<<1)
#define SDIO_WP_INT_SRC_DATA_UP          (1UL<<0)/** Definition of send data  descriptor structure */
struct tls_hspi_tx_desc {volatile uint32_t valid_ctrl;uint32_t buf_info;uint32_t buf_addr[3];uint32_t next_desc_addr;
};/** Definition of receive data  descriptor structure */
struct tls_hspi_rx_desc {uint32_t valid_ctrl;uint32_t buf_addr;uint32_t next_desc_addr;
};extern void sdio_salve_init(void);struct buffer_bd{ // 4096 byteuint32_t buffer0[400];// 400*4 =1600 byteuint32_t buffer1[400];// 400*4 =1600 byteuint32_t buffer2[224];// 224*4 =896  byte
};typedef struct {struct buffer_bd rx_buffer[3];// 4096*3;struct tls_hspi_tx_desc wrapper_tx[3]; //6*4*3struct buffer_bd tx_buffer[3];// 4096*3;struct tls_hspi_rx_desc wrapper_rx[3];//3*3*3uint32_t  cis0_buffer[8];//4*8 =32 ,,,,,32*4 =128uint32_t  cis1_buffer[32+24];//56*4 =224,,,32*4 =128uint32_t  cmd_buffer[64];// 64*4 =256
}UsrMemDef_Type;// 0x626C #define SDIO_USER_MEM     ((UsrMemDef_Type *)SLAVE_HSPI_SDIO_ADDR)extern uint16_t  sdio_salve_send(uint8_t *data ,uint16_t len);
extern uint16_t sdio_salve_recv(uint8_t* data ,uint16_t rlen_max);#endif

sdio_slave.c


#include "sdio_salve.h"
#include "wm_hal.h"
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "string.h"
#include "wm_osal.h"struct tls_hspi_rx_desc *curr_rx_desc;
struct tls_hspi_tx_desc *curr_tx_desc ;#ifndef BIT
#define BIT(x) (1UL << (x))
#endif
static void io_cfg_option4(enum tls_io_name name)
{uint8_t  pin;uint16_t offset;if (name >= WM_IO_PB_00){pin    = name - WM_IO_PB_00;offset = TLS_IO_AB_OFFSET;}else{pin    = name;offset = 0;}WRITE_REG(*(uint32_t*)(HR_GPIO_AF_SEL + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_SEL + offset)) | BIT(pin));  /* gpio function */WRITE_REG(*(uint32_t*)(HR_GPIO_AF_S1  + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_S1  + offset)) | BIT(pin));WRITE_REG(*(uint32_t*)(HR_GPIO_AF_S0  + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_S0  + offset)) | BIT(pin));
}
//sdio_rxbd内存映射
void hspi_rx_init(UsrMemDef_Type *memp)
{curr_rx_desc =memp->wrapper_rx;for(int i=0;i<3;i++){memp->wrapper_rx[i].valid_ctrl =BIT(31);memp->wrapper_rx[i].buf_addr =(uint32_t)memp->tx_buffer[i].buffer0;if(i!=2){memp->wrapper_rx[i].next_desc_addr =(uint32_t)&memp->wrapper_rx[i+1];}else{memp->wrapper_rx[i].next_desc_addr =(uint32_t)&memp->wrapper_rx[0];}}
}void hspi_tx_init(UsrMemDef_Type *memp)
{curr_tx_desc =memp->wrapper_tx;for(int i=0;i<3;i++){memp->wrapper_tx[i].valid_ctrl =0;memp->wrapper_tx[i].buf_info =0;memp->wrapper_tx[i].buf_addr[0] =(uint32_t)memp->rx_buffer[i].buffer0;//虽然没有使用3块内存,但是我们预留了4096bytememp->wrapper_tx[i].buf_addr[1] =0;//(uint32_t)memp->rx_buffer[i].buffer1;memp->wrapper_tx[i].buf_addr[2] =0;//(uint32_t)memp->rx_buffer[i].buffer2;if(i!=2){memp->wrapper_tx[i].next_desc_addr =(uint32_t)&memp->wrapper_tx[i+1];}else{memp->wrapper_tx[i].next_desc_addr =(uint32_t)&memp->wrapper_tx[0];}}
}#define        TRANS_SIZE_EMAX         256uint32_t g_form_len=0;
__attribute__((isr)) void SDIO_IRQHandler(void)
{uint32_t int_src = tls_reg_read32(HR_SDIO_INT_SRC);if(int_src&SDIO_WP_INT_SRC_DATA_DOWN){//接收到来自HOST 的数据g_form_len=SDIO_SALVE->SDIO_T_COUNT;//获取SDIO 接收到数据长度}tls_reg_write32(HR_SDIO_INT_SRC, 0xf); //clear interrputprintf("int_src =%08x\r\n",int_src);
}__attribute__((isr)) void HSPI_IRQHandler(void)
{}void cis_init(UsrMemDef_Type *memp )
{uint32_t *CIS0 =(uint32_t*)memp->cis0_buffer;*CIS0++ =0x000C0221;//为了识别 SDIO 卡,CISTPL_FUNCID 元组应存在于所有 CIS 区域中*CIS0++ =0x00000422;//功能扩展 ,支持 0800 个块大小字节*CIS0++ =0x04203208;//,25 Mb/Sec (0x32)*CIS0++ =0x53470296;// 厂商设备代码 ,0x20 开头*CIS0++ =0xffffffff;//end////5*4 =20 byteuint32_t *CIS1 =(uint32_t*)memp->cis1_buffer;*CIS1++ =0x000C0221;//为了识别 SDIO 卡,CISTPL_FUNCID 元组应存在于所有 CIS 区域中*CIS1++ =0x01012a22;//功能扩展 ,bit[31-24] :0x01:表示设备支持低功耗支持CLK停止功能*CIS1++ =0x00000020;// 20 表示支持2.0 的SDIO ,序列号32bit,*CIS1++ =0x00000000;// 可用的CSA空间 32bit ,0x00000000 byte*CIS1++ =0x08000300;//bit[15-8] 0x03:表示CSA区域只读,不可写,或格式化 800 个块大小*CIS1++ =0x00FF8000;// ocr 32bit*CIS1++ =0x010f0a08;//08:8mA ,0a:平均10mA,0f:最大16mA,最小电流:1mA 0x01*CIS1++ =0x00000101;*CIS1++ =0x00000000;*CIS1++ =0x00000000;*CIS1++ =0x00000000;*CIS1++ =0x00000000;
/*
struct tuple_user{uint8_t code;//0x91uint8_t len;//64+26 =0x5Au8 TPLSDIO_STD_ID;//0x01uint8_t TPLSDIO_STD_TYPE; //0x00uint8_t version[64];uint32_t up_cmd_start;uint32_t dn_cmd_start;uint32_t b_cmd_size;uint32_t waddr_start;uint32_t raddr_start;uint32_t b_addr_size;
};*/*CIS1++ =0x00015A91;uint32_t databuf =0;char version[64] ;memset(version,0,64);sprintf(version,"This is HLK-W806 build:%s -%s",__DATE__,__TIME__);uint8_t *str =(uint8_t*)version;for(int i=0;i<16;i++,str+=4){databuf =str[3];databuf<<=8;databuf |=str[2];databuf<<=8;databuf |=str[1];databuf<<=8;databuf |=str[0];*CIS1++ =databuf;}*CIS1++ =0x4000;//上行命令地址*CIS1++ =0x4000+128;//下行命令地址*CIS1++ =128;//命令大小*CIS1++ =0x15000;//接收地址*CIS1++ =0x16000;//发送地址*CIS1++ =TRANS_SIZE_EMAX;//一次接收大小,太大也是浪费用不着 max 4096*CIS1++ =0xffffffff;// stop -1byte//(20+16)*4=144 byte
}void sdio_salve_init(void)
{//初始化引脚map :io_cfg_option4(WM_IO_PB_06);/*CK*/io_cfg_option4(WM_IO_PB_07);/*CMD*/io_cfg_option4(WM_IO_PB_08);/*D0*/io_cfg_option4(WM_IO_PB_09);/*D1*/io_cfg_option4(WM_IO_PB_10);/*D2*/io_cfg_option4(WM_IO_PB_11);/*D3*///初始化接收 发送链表UsrMemDef_Type *memp =SDIO_USER_MEM;memset((uint8_t*)memp,0,sizeof(UsrMemDef_Type));hspi_rx_init(memp);hspi_tx_init(memp);NVIC_ClearPendingIRQ((IRQn_Type)SDIO_IRQn);NVIC_EnableIRQ((IRQn_Type)SDIO_IRQn);/* hspi data down(rx) */SDIO_WRAPPER->SDIO_TX_BD_ADDR =(uint32_t)memp->wrapper_rx;//SDIO- TX 链接寄存器SDIO_WRAPPER->SDIO_TX_BD_LINK_EN =0x1;//SDIO_WRAPPER->SDIO_TX_EN =0x1;//SDIO_WRAPPER->SDIO_RX_BD_ADDR =(uint32_t)memp->wrapper_tx;//SDIO -RX 链接寄存器SDIO_WRAPPER->SDIO_RX_BD_LINK_EN =0x1;///* hspi cmd down */tls_reg_write32(HR_SDIO_CMD_ADDR, (uint32_t)memp->cmd_buffer);tls_reg_write32(HR_SDIO_CMD_SIZE, 256);tls_reg_write32(HR_SDIO_DOWNCMDVALID, 0x1);/* enable sdio module register */tls_reg_write32(HR_SDIO_INT_MASK, 0x00);cis_init(memp);//初始话 SDIO-CIS 寄存器 uint32_t SDIO_CIS0_ADDR =(uint32_t) memp->cis0_buffer;uint32_t SDIO_CIS1_ADDR =(uint32_t) memp->cis1_buffer;//设置CIS0 寄存器指针地址SDIO_SALVE->CIS0_ADDR =SDIO_CIS0_ADDR-0x01000;SDIO_SALVE->CIS1_ADDR =SDIO_CIS1_ADDR-0x02000;printf("HR_SDIO_CIS0 =%08x  ," ,SDIO_CIS0_ADDR);printf("HR_SDIO_CIS1 =%08x \r\n" ,SDIO_CIS1_ADDR);/* set sdio ready */
#ifdef      SPEED_50Mhztls_reg_write32(HR_SDIO_PROG, 0x02FD);//high速设备 SDIO_SALVE->CIA_REG =0x06011232;//支持高速 ;
#else //25Mhztls_reg_write32(HR_SDIO_PROG, 0x03FD);//低速设备 SDIO_SALVE->CIA_REG =0x02011232;//不支持高速
#endifSDIO_SALVE->AHB_T_COUNT=TRANS_SIZE_EMAX;//发送大小SDIO_SALVE->FN1_ENA_REG =0x1;}uint16_t  sdio_salve_send(uint8_t *data ,uint16_t len)
{struct tls_hspi_tx_desc *tx_desc = curr_tx_desc;while (tx_desc->valid_ctrl & BIT(31)){ //等待硬件发送完毕,自动清除该变量vTaskDelay(500 / portTICK_PERIOD_MS);}if(len>TRANS_SIZE_EMAX){ //设置传输尺寸,不可变len=TRANS_SIZE_EMAX;}memcpy((char*)tx_desc->buf_addr[0],data,len);tx_desc->buf_info = len << 12; //设置传输大小tx_desc->valid_ctrl = (1UL << 31);tls_reg_write32(HR_SDIO_RXEN, 0x01);//启动传输命令tx_desc = (struct tls_hspi_tx_desc *) tx_desc->next_desc_addr;//设置下次一次传输地址curr_tx_desc = tx_desc;
return  len;
}uint16_t sdio_salve_recv(uint8_t* data ,uint16_t rlen_max)
{uint16_t len=0;struct tls_hspi_rx_desc *rx_desc = curr_rx_desc;if(g_form_len>0){//获取SDIO 接收到数据长度len =g_form_len;g_form_len=0;memcpy(data,(char*)rx_desc->buf_addr,TRANS_SIZE_EMAX);  rx_desc->valid_ctrl = BIT(31);/* 设置hspi/sdio tx enable寄存器,让sdio硬件知道有可用的tx descriptor */SDIO_WRAPPER->SDIO_TX_EN =0x1;rx_desc = (struct tls_hspi_rx_desc *) rx_desc->next_desc_addr; //next ..curr_rx_desc =rx_desc;}
return len;
}

V3s SDIO 驱动部分 使用 Git 上俄罗斯网友的参考 ,GitHub - balmerdx/sdio_linux_fpga: Communication channel from FPGA (Alterra EP4CE10) and Linux (Lichee PI Allwinner V3S)

经过修改如下:

/** Author:  Dmitriy 'Balmer' Poskryakov* Created: 2020* 2020 Balmer - use for own SDIO UART** Based on Nicolas Pitre sdio_uart.c** Based on drivers/serial/8250.c and drivers/serial/serial_core.c* by Russell King.** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or (at* your option) any later version.* */#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/circ_buf.h>
#include <linux/kfifo.h>
#include <linux/slab.h>
#include <linux/cdev.h>#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/uaccess.h>
#include "sdio_uart_user.h"#define MY_REG_RX 0
#define MY_REG_TX 0
#define MY_REG_STATUS 1int debug = 0;module_param(debug, int, S_IRUGO);
//data ready
#define BIT_STATUS_DR 0x01
//transmit empty
#define BIT_STATUS_THRE 0x01#define UART_NR     4   /* Number of UARTs this driver can handle */#define FIFO_SIZE   PAGE_SIZE
#define WAKEUP_CHARS    256struct sdio_uart_port {unsigned int      index;struct sdio_func  *func;struct mutex      func_lock;struct task_struct    *in_sdio_uart_irq;spinlock_t            write_lock; //actually read/write lockstruct kfifo      read_fifo;struct device   *file_dev;struct cdev c_dev;//uint32_t up_cmd_start;uint32_t dn_cmd_start;uint32_t b_cmd_size;uint32_t waddr_start;uint32_t raddr_start;uint32_t b_addr_size;
};static dev_t balmer_dev_t = 0;
struct class *balmer_class = 0;static struct sdio_uart_port *sdio_uart_table[UART_NR];
static DEFINE_SPINLOCK(sdio_uart_table_lock);static void sdio_uart_port_remove(struct sdio_uart_port *port)
{struct sdio_func *func;spin_lock(&sdio_uart_table_lock);sdio_uart_table[port->index] = NULL;spin_unlock(&sdio_uart_table_lock);/** We're killing a port that potentially still is in use by* the tty layer. Be careful to prevent any further access* to the SDIO function and arrange for the tty layer to* give up on that port ASAP.* Beware: the lock ordering is critical.*/mutex_lock(&port->func_lock);func = port->func;sdio_claim_host(func);port->func = NULL;mutex_unlock(&port->func_lock);sdio_release_irq(func);sdio_disable_func(func);sdio_release_host(func);
}static int sdio_uart_claim_func(struct sdio_uart_port *port)
{mutex_lock(&port->func_lock);if (unlikely(!port->func)) {mutex_unlock(&port->func_lock);return -ENODEV;}if (likely(port->in_sdio_uart_irq != current))sdio_claim_host(port->func);mutex_unlock(&port->func_lock);return 0;
}static inline void sdio_uart_release_func(struct sdio_uart_port *port)
{if (likely(port->in_sdio_uart_irq != current))sdio_release_host(port->func);
}static inline u8 sdio_in(struct sdio_uart_port *port, int offset)
{u8 c;c = sdio_readb(port->func, offset, NULL);return c;
}static inline void sdio_out(struct sdio_uart_port *port, int offset, u8 value)
{sdio_writeb(port->func, value, offset, NULL);
}
#define     FUNC1_INT1          0x04 //bit0
#define     FUNC1_RLEN_BIT_H7   0x1c //bit6:0
#define     FUNC1_RLEN_BIT_L5   0x1d //bit7:3
/** This handles the interrupt from one port.*/
static void sdio_uart_irq(struct sdio_func *func)
{uint8_t *data;uint16_t temp=0;struct sdio_uart_port *port = sdio_get_drvdata(func);/** In a few places sdio_uart_irq() is called directly instead of* waiting for the actual interrupt to be raised and the SDIO IRQ* thread scheduled in order to reduce latency.  However, some* interaction with the tty core may end up calling us back* (serial echo, flow control, etc.) through those same places* causing undesirable effects.  Let's stop the recursion here.*/if (unlikely(port->in_sdio_uart_irq == current))return;port->in_sdio_uart_irq = current;temp =sdio_readb(port->func, FUNC1_INT1, NULL);//see HLK-W801 kit  ,SDIO DEVif(temp&0x1){
//      temp =sdio_readb(port->func, FUNC1_RLEN_BIT_H7, NULL);//read this auto clear interrput
//      temp =(temp&0x7f)<<5;
//      temp|=0x1f&(sdio_readb(port->func, FUNC1_RLEN_BIT_L5, NULL)>>3); sdio_writeb(port->func, 0x1, FUNC1_INT1, NULL);data =(char *)kzalloc(port->b_addr_size, GFP_KERNEL);sdio_memcpy_fromio(port->func, data, port->raddr_start, port->b_addr_size);kfifo_in_locked(&port->read_fifo, data, port->b_addr_size, &port->write_lock);kfree(data);}port->in_sdio_uart_irq = NULL;printk("int");
}
/*
static int sdio_receive_inline(struct sdio_func *func, char* buf, int size)
{struct sdio_uart_port *port = sdio_get_drvdata(func);int ret;unsigned int addr = port->raddr_start;if (unlikely(port->in_sdio_uart_irq == current))return 0;port->in_sdio_uart_irq = current;
//    ret = sdio_readsb(port->func, buf, addr, size);ret = sdio_memcpy_fromio(port->func, buf, addr, size); port->in_sdio_uart_irq = NULL;return (ret==0)?size:0;
}
*/
static int sdio_write_inline(struct sdio_func *func, const char* buf, int size)
{struct sdio_uart_port *port = sdio_get_drvdata(func);int ret;unsigned int addr = port->waddr_start;if (unlikely(port->in_sdio_uart_irq == current))return 0;port->in_sdio_uart_irq = current;  ret = sdio_memcpy_toio(port->func, addr, (char*)buf, size);//write addr add+++port->in_sdio_uart_irq = NULL;return (ret==0)?size:0;
}static int sdio_uart_activate(struct sdio_uart_port *port)
{int ret;kfifo_reset(&port->read_fifo);ret = sdio_uart_claim_func(port);if (ret)return ret;ret = sdio_enable_func(port->func);if (ret)goto err1;ret = sdio_claim_irq(port->func, sdio_uart_irq);// Register interruptif (ret)goto err2;// Kick the IRQ handler once while we're still holding the host locksdio_uart_irq(port->func);sdio_uart_release_func(port);if(debug)printk("SDIO Driver: sdio_uart_activate complete\n");return 0;
err2:sdio_disable_func(port->func);
err1:sdio_uart_release_func(port);return ret;
}static void sdio_uart_shutdown(struct sdio_uart_port *port)
{int ret;ret = sdio_uart_claim_func(port);if (ret)return;sdio_release_irq(port->func);sdio_disable_func(port->func);sdio_uart_release_func(port);
}static int sdio_uart_write(struct sdio_uart_port *port, const char *buf,int count)
{int ret;if (!port->func){return -ENODEV;}ret = sdio_uart_claim_func(port);if (!ret) {ret = sdio_write_inline(port->func, buf, count);sdio_uart_release_func(port);} return ret;
}
/*
static int sdio_uart_read(struct sdio_uart_port *port, char *buf, int count)
{int ret;if (!port->func){return -ENODEV;}ret = sdio_uart_claim_func(port);if (!ret){ret = sdio_receive_inline(port->func, buf, count);sdio_uart_release_func(port);}return ret;
}
*/
/*
static int sdio_uart_write_room(struct sdio_uart_port *port)
{return FIFO_SIZE - kfifo_len(&port->xmit_fifo);
}
*/static int sdio_file_open(struct inode *inode, struct file *file)
{struct sdio_uart_port *port;if(debug)printk("SDIO Driver: open()\n");port = container_of(inode->i_cdev, struct sdio_uart_port, c_dev);file->private_data = port;return sdio_uart_activate(port);
}static int sdio_file_close(struct inode *inode, struct file *file)
{struct sdio_uart_port *port; port = container_of(inode->i_cdev, struct sdio_uart_port, c_dev);if(debug)printk("SDIO Driver: close() portidx=%u\n", port->index);sdio_uart_shutdown(port);return 0;
}static ssize_t sdio_file_read(struct file *file, char __user *buf, size_t len, loff_t *off)
{char *data;struct sdio_uart_port *port = file->private_data;ssize_t read_size = 0;if(kfifo_len(&port->read_fifo)==0){return 0;}data =(char *)kzalloc(len, GFP_KERNEL);if(data){read_size = kfifo_out_locked(&port->read_fifo, data, len, &port->write_lock);
//      read_size =sdio_uart_read(port,data,len);if(read_size!=0){if (copy_to_user(buf, data, read_size)!=0){read_size =-EFAULT;}   }kfree(data);}return read_size;
}static ssize_t sdio_file_write(struct file *file, const char __user *buf, size_t len, loff_t *off)
{struct sdio_uart_port *port = file->private_data;ssize_t write_size = 0;char *data =kzalloc(len, GFP_KERNEL);if(copy_from_user(data, buf, len)==0){write_size=sdio_uart_write(port, data, len);}kfree(data);return write_size;
}static long sdio_file_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{struct sdio_uart_port *port = file->private_data;void __user *uarg = (void __user *) arg;long rc = -EFAULT;struct USER_SDIO_CMD52 user_cmd52;int ret;int func_number =0;switch(cmd){case USER_SDIO_CMD_b: //cmd52ret=copy_from_user(&user_cmd52,uarg, 12);if(ret){return -EFAULT;}ret = sdio_uart_claim_func(port);if (!ret) {func_number =port->func->num;port->func->num =user_cmd52.w_r_flag&BIT(31) ?1:0;if((user_cmd52.w_r_flag&0x1)==1){    sdio_writeb(port->func, user_cmd52.value, user_cmd52.s_addr, &ret);}else if((user_cmd52.w_r_flag&0x1)==0){user_cmd52.value =sdio_readb(port->func,user_cmd52.s_addr,&ret);}else{rc = -EFAULT;ret =1;}port->func->num =func_number;//backif(ret!=0){rc = -EFAULT;}else{ret =copy_to_user(uarg,&user_cmd52,12);if(ret){rc = -EFAULT;}else{rc=0;}}sdio_uart_release_func(port);}break;case USER_SDIO_CMD_s: ret=copy_from_user(&user_cmd52,uarg, 12);if(ret){return -EFAULT;}if(user_cmd52.s_addr==0x1){port->waddr_start =user_cmd52.value;rc=0;}else if(user_cmd52.s_addr==0x2){port->raddr_start =user_cmd52.value;rc=0;}else{rc=-EFAULT;}break;default:rc = -ENOSYS;break;}return rc;
}static const struct file_operations sdio_uart_proc_fops = {.owner     = THIS_MODULE,.open        = sdio_file_open,.release    = sdio_file_close,.read      = sdio_file_read,.write      = sdio_file_write,.unlocked_ioctl = sdio_file_ioctl,
};static int sdio_init(struct sdio_uart_port *port)
{struct sdio_func *func =port->func;int err;/* 3 1. init SDIO bus */sdio_claim_host(func);err = sdio_enable_func(func);if (err) {printk("%s: sdio_enable_func FAIL(%d)!\n", __func__, err);goto release;}err = sdio_set_block_size(func, port->b_addr_size);//set transmit byteif (err) {printk("%s: sdio_set_block_size FAIL(%d)!\n", __func__, err);goto release;}
release:sdio_release_host(func);return err;
}
/*
struct tuple_user{uint8_t code;//0x91uint8_t len;//64+26 =0x5Au8 TPLSDIO_STD_ID;//0x01uint8_t TPLSDIO_STD_TYPE; //0x00uint8_t version[64];uint32_t up_cmd_start;uint32_t dn_cmd_start;uint32_t b_cmd_size;uint32_t waddr_start;uint32_t raddr_start;uint32_t b_addr_size;
};*/
#define little_endian_32bit(x) \(uint32_t)x[3]<<24|\(uint32_t)x[2]<<16|\(uint32_t)x[1]<<8|\(uint32_t)x[0]
static int find_tuple(struct sdio_uart_port *port)
{uint8_t *data=0;char  str[64];struct sdio_func_tuple *tpl =port->func->tuples;for (; tpl; tpl = tpl->next) {//printk("tpl->code =%02x" ,tpl->code);if (tpl->code != 0x91)//We have customized the SDIO type herecontinue;if (tpl->size < 88)continue;if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */break;}if (!tpl) {printk("%s: can't find tuple 0x91 subtuple 0 for UART class\n",sdio_func_id(port->func));return -EINVAL;}data =&tpl->data[2];memcpy(str,data,64); str[63] =0;if(debug)printk("%s: version :%s",sdio_func_id(port->func),str);data+=64;port->up_cmd_start =little_endian_32bit(data);data+=4;port->dn_cmd_start =little_endian_32bit(data);data+=4;port->b_cmd_size   =little_endian_32bit(data);data+=4;port->waddr_start  =little_endian_32bit(data);data+=4;port->raddr_start  =little_endian_32bit(data);data+=4;port->b_addr_size  =little_endian_32bit(data);data+=4;if(debug){printk("up_cmd_start =%08x ,dn_cmd_start =%08x ",port->up_cmd_start,port->dn_cmd_start);printk("b_cmd_size =%08x ,waddr_start =%08x",port->b_cmd_size,port->waddr_start);printk("raddr_start =%08x ,b_addr_size =%08x",port->raddr_start,port->b_addr_size);printk("sdio:func->number =%d ",port->func->num);}
return 0;
}static int sdio_uart_probe(struct sdio_func *func,const struct sdio_device_id *id)
{struct sdio_uart_port *port;int ret,index;dev_t dev_idx;if(debug)printk("sdio_uart_probe start");port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);if (!port)return -ENOMEM;if (func->class == SDIO_CLASS_UART) {printk("%s: balmer own driver init UART over SDIO\n",sdio_func_id(func));   } else {ret = -EINVAL;goto err1;}port->func = func;if(0!=find_tuple(port)){ret = -EINVAL;goto err1;}sdio_set_drvdata(func, port);//joinmutex_init(&port->func_lock);
//  mutex_init(&port->read_lock);    //  mutex_lock(&port->read_lock);
//  mutex_unlock(&port->read_lock);spin_lock(&sdio_uart_table_lock);ret=1;for (index = 0; index < UART_NR; index++) {if (!sdio_uart_table[index]) {port->index = index;sdio_uart_table[index] = port;ret = 0;break;}}spin_unlock(&sdio_uart_table_lock);if(ret){printk("%s :sdio_uart_table run out ",__func__);goto err1;}if(0!=sdio_init(port)){printk("%s: initialize SDIO Failed!\n", __func__);goto err1;}spin_lock_init(&port->write_lock);if (kfifo_alloc(&port->read_fifo, port->b_addr_size*3, GFP_KERNEL)){goto err1;}dev_idx = balmer_dev_t+port->index;port->file_dev = device_create(balmer_class, NULL, dev_idx, NULL, "balmerSDIO%d", port->index);if (IS_ERR(port->file_dev)){sdio_uart_port_remove(port);ret = PTR_ERR(port->file_dev);printk("%s: device_create Failed! ret =%d\n", __func__,ret);goto err1;}cdev_init(&port->c_dev, &sdio_uart_proc_fops);ret = cdev_add(&port->c_dev, dev_idx, 1);if (ret) {cdev_del(&port->c_dev);goto err2;ret = -EINVAL;}else{ret=0;}if(debug)printk("sdio_uart_probe all ok");return ret;
err2:device_destroy(balmer_class, dev_idx);
err1:kfree(port);sdio_set_drvdata(func, NULL);return ret;
}static void sdio_uart_remove(struct sdio_func *func)
{struct sdio_uart_port *port = sdio_get_drvdata(func);cdev_del(&port->c_dev);kfifo_free(&port->read_fifo);device_destroy(balmer_class, balmer_dev_t+port->index);sdio_uart_port_remove(port);kfree(port);if(debug)printk("sdio_uart_remove complete");port =NULL;
}static const struct sdio_device_id sdio_uart_ids[] = {{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART) },{ /* end: all zeroes */               },
};MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);static struct sdio_driver sdio_uart_driver = {.probe        = sdio_uart_probe,.remove      = sdio_uart_remove,.name       = "sdio_uart",.id_table  = sdio_uart_ids,
};static int __init sdio_uart_init(void)
{int ret;if(debug)printk("sdio_uart_init start");if ((ret = alloc_chrdev_region(&balmer_dev_t, 0, 1, "balmer_dev_t")) < 0)return ret;balmer_class = class_create(THIS_MODULE, "balmer_sdio");if (IS_ERR(balmer_class)){ret = PTR_ERR(balmer_class);err2:unregister_chrdev_region(balmer_dev_t, UART_NR);balmer_dev_t = 0;}else{ret = sdio_register_driver(&sdio_uart_driver);if (ret){class_destroy(balmer_class);balmer_class = 0;goto err2;}}if(debug)printk("sdio_uart_init =%d" ,ret);return ret;
}static void __exit sdio_uart_exit(void)
{if(balmer_class){class_destroy(balmer_class);balmer_class = 0;}if(balmer_dev_t){unregister_chrdev_region(balmer_dev_t, UART_NR);balmer_dev_t = 0;}sdio_unregister_driver(&sdio_uart_driver);if(debug)printk("sdio_uart_exit ");
}module_init(sdio_uart_init);
module_exit(sdio_uart_exit);//MODULE_AUTHOR("Nicolas Pitre");
MODULE_AUTHOR("horli.2022-4-12");
MODULE_LICENSE("GPL");

实测效果:

测试代码:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>#include "cmd.h"
#include "sdio_uart_user.h"int do_help(struct tty *ptty, cmdt_list *pcmdt_list, int list_size)
{int all;char *cmdname = NULL;cmdt **pcmdt;int array_size;int i, j;if(ptty->argc >= 2){cmdname = ptty->argv[1];all = 0;}else{all = 1;}for(i = 0; i < list_size; i++){array_size = pcmdt_list[i].cmdt_size;pcmdt = pcmdt_list[i].pcmdt;for(j = 0; j < array_size; j++){if(all){ptty->printf("%-20s - %s\r\n", pcmdt[j]->name, pcmdt[j]->help_t);}else{if(strcmp(cmdname, pcmdt[j]->name) == 0){ptty->printf("\r\n%s\r\n", pcmdt[j]->help_f);return 0;}}}}ptty->printf("\r\n");return 0;
}
int do_main_help(struct tty *ptty)
{cmdt_list *pcmdt_list;int list_size;int rt;pcmdt_list = cmd_main_list();list_size = cmd_main_listlen();rt = do_help(ptty, pcmdt_list, list_size);return rt;
}
int do_clear(struct tty *ptty)
{ptty->printf("\033[2J\033[0;0H");return 0;
}
cmdt cmd_main_help = {"help", do_main_help, "The help information about the command is displayed","help: Displays all current commands and their introduction\r\n""help cmd:Displays all current commands and their introduction\r\n","[name]"};
cmdt cmd_clear = {"clear", do_clear, "cls", "clear: Clear screen (not supported by some terminals)\r\n"," "};int do_cmdset(struct tty *ptty)
{struct USER_SDIO_CMD52 cmd52;int func_num =0;if(ptty->argc>2){if(strcmp(ptty->argv[1],"w")==0){cmd52.s_addr =1;}else if(strcmp(ptty->argv[1],"r")==0){cmd52.s_addr =2;}else{ptty->printf("undefine cmd!\r\n");return 0;    }cmd52.value = strtoul(ptty->argv[2],NULL,16);}else{ptty->printf("undefine cmd!\r\n");return 0;}int *fd = (int *)ptty->vpt;if(ioctl(*fd,USER_SDIO_CMD_s,&cmd52)==-1){ptty->printf(" ioctl fail !\r\n");}else{ptty->printf("cmd52 %s s_addr=0x%08x \r\n",ptty->argv[1],cmd52.value);}return 0;
}
cmdt cmd_cmdset = {"cmdset", do_cmdset, "cmdset [w/r]  [data{5000-5FFF,6000-6FFF,}]", "cmdset: write  SDIO w/r addr start\r\n"," "};int do_cmd52w(struct tty *ptty)
{struct USER_SDIO_CMD52 cmd52;int func_num =0;if(ptty->argc>3){func_num = atoi(ptty->argv[1]);cmd52.s_addr =strtoul(ptty->argv[2],NULL,16);cmd52.value = strtoul(ptty->argv[3],NULL,16);}else{ptty->printf("undefine cmd!\r\n");return 0;}cmd52.w_r_flag=1;cmd52.w_r_flag|=func_num&0x1?(1<<31):0;//funcint *fd = (int *)ptty->vpt;if(ioctl(*fd,USER_SDIO_CMD_b,&cmd52)==-1){ptty->printf("1 ioctl fail !\r\n");}else{ptty->printf("1 cmd52 addr=0x%08x ,data=0x%02x \r\n",cmd52.s_addr,cmd52.value);}return 0;
}
cmdt cmd_cmd52w = {"cmd52.w", do_cmd52w, "cmd52.w [func{0-1}] [addr{00000-1FFFF}] [data{00-FF}]", "cmd52.w: write function 1 SDIO byte\r\n"," "};int do_cmd52r(struct tty *ptty)
{struct USER_SDIO_CMD52 cmd52;int func_num =0;if(ptty->argc>2){func_num = atoi(ptty->argv[1]);cmd52.s_addr =strtoul(ptty->argv[2],NULL,16);}else{ptty->printf("undefine cmd!\r\n");return 0;}cmd52.w_r_flag=0;cmd52.w_r_flag|=func_num&0x1?(1<<31):0;//funcint *fd = (int *)ptty->vpt;if(ioctl(*fd,USER_SDIO_CMD_b,&cmd52)==-1){ptty->printf("r ioctl fail !\r\n");}else{ptty->printf("r cmd52 addr=0x%08x ,data=0x%02x \r\n",cmd52.s_addr,cmd52.value);}return 0;
}
cmdt cmd_cmd52r = {"cmd52.r", do_cmd52r, "cmd52.r [func{0-1}] [addr{00000-1FFFF}]", "cmd52.r: read function 1 SDIO byte\r\n"," "};int do_sdiow(struct tty *ptty)
{uint8_t start_data =0;uint16_t length =0;if(ptty->argc>2){start_data =strtoul(ptty->argv[1],NULL,16);length  =strtoul(ptty->argv[2],NULL,16);}else{ptty->printf("undefine cmd!\r\n");return 0;}uint8_t *write_buffer =malloc(4096);for(int i=0;i<4096;i++){write_buffer[i]=(start_data+i)%0xff;}int *fd = (int *)ptty->vpt;uint16_t bytes_len = write(*fd, write_buffer, length);free(write_buffer);ptty->printf("sdio write ,start=0x%02x ,wlen=0x%02x \r\n",start_data,bytes_len);return 0;
}
cmdt cmd_sdiow = {"sdio.w", do_sdiow, "sdio.w [start{00-FF}] [length{1-4096}]", "sdio.w: write function 1 SDIO n byte\r\n"," "};
int do_sdior(struct tty *ptty)
{uint16_t length =0;if(ptty->argc>1){length  =strtoul(ptty->argv[1],NULL,16);}else{ptty->printf("undefine cmd!\r\n");return 0;}uint8_t *read_buffer =malloc(4096);int *fd = (int *)ptty->vpt;uint16_t bytes_len = read(*fd, read_buffer, length);ptty->printf("sdio read ,rlen=0x%04x => list 32byte\r\n",bytes_len);uint8_t *p =read_buffer;for(int i=0;i<32;i++){ptty->printf("%02x ",*p++);}free(read_buffer);return 0;
}
cmdt cmd_sdior = {"sdio.r", do_sdior, "sdio.r [length{1-4096}]", "sdio.r: read function 1 SDIO n byte\r\n"," "};

W806 SDIO 设备 扩展 荔枝派 V3s IO 使用相关推荐

  1. 编译全志V3S(荔枝派zero)整个系统流程及后续故障答疑使用技巧等

    全志V3S(荔枝派zero)学习 购买到的硬件 声明一下用的主线Uboot + 主线linux,如果你是小白不幸买到了本产品建议弃坑既浪费时间,又浪费生命,香橙派树莓派是你更好的选择 Uboot 编译 ...

  2. 荔枝派Zero | 全志V3s 开发教程(一) 烧录系统镜像的方法以及如何烧录主线Linux系统镜像

    荔枝派Zero | 全志V3s 开发教程(一) 荔枝派Zero的开发教程: http://zero.lichee.pro/系统开发/type.html 刚开始尝试编译Camdroid官方SDK,最终浪 ...

  3. 1.荔枝派 zero(全志V3S)-编译及SD烧录

    上面是我创建的群聊,欢迎新朋友的加入. 之前买了个荔枝派,全志的A3S芯片.折腾了两天,写一下编译和SD烧录的过程. 目录 1.直接烧录镜像文件 2.uboot编译 3.kernel编译 4.root ...

  4. 7.荔枝派 zero(全志V3S)-编译内核驱动,生成KO文件

    上面是我的微信和QQ群,欢迎新朋友的加入. 在网上溜了一大圈,发现竟然没人写过荔枝派编译内核驱动的帖子. 或许是我输入的关键字不对 今天特意做了一下这个事情. 1.改kernel makefile 当 ...

  5. 全志V3S荔枝派zero10分钟制作TF启动卡,主线Linux,主线u-boot(超过10分钟博主直播吃奥利奥!!!)

    一,初学者(比如我)刚拿到zero时,一定是一脸懵逼的,无从下手的,通过几篇代佬们写的文章+官网资料,了解了几种启动方式 1.Camdriod 官方SDK 2.主线Uboot + Bsp 内核 3.主 ...

  6. 新手玩荔枝派 f1c100s nano折腾笔记(三)

    应用开发篇: minimal_f1c100s-framebuffer.zip 荔枝派 nano引脚图 使用OTG功能没办法挂载U盘,插入TF卡,开机自动TF卡启动,开机后插入TF有提示,但找不到盘. ...

  7. IOT-OS之RT-Thread(十五)--- SDIO设备对象管理 + AP6181(BCM43362) WiFi模块

    文章目录 一.AP6181 Wi-Fi模块简介 1.1 AP6181 硬件接口 1.2 AP6181 驱动层级 二 SDIO设备对象管理 2.1 SDIO Bus Driver 2.1.1 Host ...

  8. lichee nano 荔枝派入门——linux内核

    获取源代码 见<lichee nano 荔枝派入门--搭建环境> 个人建议使用如下命令克隆内核代码: # 下载默认带480*272LCD的版本 git clone --depth=1 -b ...

  9. 为荔枝派Zero制作Linux系统镜像[转]

    最近购入了一块LicheePi Zero开发板,使用全志V3s作为主控芯片.本文介绍如何从零开始为荔枝派制作一个可以启动的Linux系统镜像. 配置编译环境 本文所有编译工作均基于Ubuntu 20. ...

最新文章

  1. 4、 LIMIT:限制查询结果的条数
  2. [基础常识]申请免费SSL证书 - 阿里云云盾证书 - Digicert+Symantec 免费型DV SSL
  3. 正确入门Service Mesh:起源、发展和现状
  4. 正则匹配问号_爬虫之正则表达式
  5. modprobe: FATAL: Module xxx.ko not found in directory /lib/modules/$(uname -r)
  6. 【Elasticsearch】es 7.8.0 java 实现 BulkRequest 批量写入数据
  7. mock模拟的数据能增删改查吗_新课通知: React+Umi3+Typescript+Mock
  8. [Bzoj2152]聪聪可可
  9. [软件更新]卡巴斯基全功能安全软件2010简体中文版程序发布
  10. WiFi图标在任务栏里不见了,提示:适配器Qualcomn Atheros QCA9377 Wireless Network Adapter遇到驱动程序或硬件相关的问题
  11. python100例详解-Python 经典算法100及解析(小结)
  12. NanoHTTPD服务
  13. 使用Neo4j分析《权力的游戏》
  14. bigdecimal如何做除法_bigdecimal类型除法问题
  15. 网咖服务器cpu占用率高,CPU温度过高为什么会卡?
  16. 结直肠癌呈年轻化趋势,与肠道微生物群密不可分
  17. 哈夫曼树——荷马史诗(贪心+优先队列)
  18. java word 水印_Java 添加水印(文本水印、图片水印)到Word文档
  19. 运用Python完成学生成绩判定
  20. 教你利用VMM虚拟机安装LEDE旁路由实现软路由超强功能的方法教程

热门文章

  1. linux创建新的用户并登陆新用户
  2. 论文阅读Patient-specific reconstruction of volumetric computed tomography images from a single projectio
  3. OCR字符检测技术:常见的OCR字符识别算法
  4. xampp 中 mysql 的 乱码解决
  5. 高通平台 Sensor 调试技巧 01
  6. 深耕本地资源打造智慧城市
  7. 端口被占用的解决方法
  8. 技术创始人如何挑选非技术合伙人?
  9. vagrant的基础使用
  10. win10解决硬盘占用率100%问题