赞
踩
Linux主机驱动和外设驱动分离思想
SPI驱动总线架构:SPI核心层(x),SPI控制器驱动层(x),SPI设备驱动层(√)
2 Linux SPI驱动总体架构
在2.6的linux内核中,SPI的驱动架构可以分为如下三个层次:SPI 核心层、SPI控制器驱动层和SPI设备驱动层。
Linux 中SPI驱动代码位于drivers/spi目录。
2.1 SPI核心层
SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
Linux中,SPI核心层的代码位于driver/spi/ spi.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
2.2 SPI控制器驱动层
SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。
在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述。
3.SPI相关的数据结构
3.1 struct spi_master 用于描述一个SPI控制器
- //在include/liunx/spi/spi.h文件中,在数据结构struct spi_master定义如下:
-
- struct spi_master {
- struct device dev;
- s16 bus_num;
- u16 num_chipselect;
- int (*setup)(struct spi_device *spi);
- int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
- void (*cleanup)(struct spi_device *spi);
- };
bus_num为该控制器对应的SPI总线号。
num_chipselect 控制器支持的片选数量,即能支持多少个spi设备
setup函数是设置SPI总线的模式,时钟等的初始化函数, 针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用。
transfer函数是实现SPI总线读写方法的函数。实现数据的双向传输,可能会睡眠
cleanup 注销的时候调用
3.2 SPI设备驱动层
SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
SPI设备驱动层可以用两个模块来描述,struct spi_driver和struct spi_device。
相关的数据结构如下:
struct spi_driver 用来描述一个SPI设备的驱动信息
- struct spi_driver {
- int (*probe)(struct spi_device *spi);
- int (*remove)(struct spi_device *spi);
- void (*shutdown)(struct spi_device *spi);
- int (*suspend)(struct spi_device *spi, pm_message_t mesg);
- int (*resume)(struct spi_device *spi);
- struct device_driver driver;
- };
Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,而不是像I2C那样通过与从设备进行对话的方式。
struct spi_device 用来描述一个SPI总线上的从设备
通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。
spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等
- struct spi_device {
- struct device dev;
- struct spi_master *master;
- u32 max_speed_hz;
- u8 chip_select;
- u8 mode;
- u8 bits_per_word;
- int irq;
- void *controller_state;
- void *controller_data;
- char modalias[32];
- };
spi_device的板信息用spi_board_info结构体来描述:
spi_board_info参数
- .modalias = "rc522", //初始化设备的名称
- .platform_data = NULL,
- .max_speed_hz = 10*1000*1000, //初始化传输速率
- .bus_num = 2, //控制器编号
- .chip_select = 0, //控制器片选的编号
- .mode = SPI_MODE_0, //spi的模式 CPOL=0, CPHA=0 此处选择具体数据传输模式
- .controller_data = &spi2_csi[0], //片选IO的信息
spi2_board_info设备描述结构体,设备注册函数spi_register_board_info
而这个info在init函数调用的时候会初始化:
spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。
这个代码会把spi_board_info注册到链表board_list上。
spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。
至此spi_device就构建并注册完成了!
5 spi_driver的构建与注册
driver有几个重要的结构体:spi_driver、spi_transfer、spi_message
driver有几个重要的函数 :spi_message_init、spi_message_add_tail、spi_sync
//spi_driver的构建
- static struct spi_driver m25p80_driver = {
-
- .driver = {
- .name ="m25p80",
- .bus =&spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = m25p_probe,
- .remove =__devexit_p(m25p_remove),
-
- };
//spidriver的注册
spi_register_driver(&m25p80_driver);
在有匹配的spi_device时,会调用m25p_probe
probe里完成了spi_transfer、spi_message的构建;
spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用
在SPI总线上是通过封装一系列的spi_transfer到一个spi_message中,然后将spi_message提交到SPI子系统去。
下面是spi_transfer结构:
- struct spi_transfer {
- const void*tx_buf; //驱动提供的发送缓冲区dma,
- void *rx_buf; //接收缓冲区
- unsigned len; //长度一般是8位
- dma_addr_ttx_dma; //发送dma,controller使用
- dma_addr_t rx_dma; //接收dma
- unsigned cs_change:1; //片选位
- u8 bits_per_word; //每字长度
- u16 delay_usecs; //延迟
- u32 speed_hz; //速度
- struct list_headtransfer_list; //transfer 链表
- };
在spi_transfer中时常更改的域也许只有len,tx_buf和rx_buf。剩下的当以0来初始化。
单个spi_transfer可表示一次读,一次写或者是一次读写。在SPIcontroller驱动下,所有操作常是全双工的。向spi_transfer中rx_buf传递一个NULL,这就是一次只写操作,会丢弃MISO线上的数据。同样向tx_buf传递一个NULL,这就是一次只读操作了。spi_transfer中len域代表(已经多少字节数据流过总线了)howmany bytes to clock the bus。
spi_message结构:
- struct spi_message {
- struct list_head transfers;
- struct spi_device *spi;
- unsigned is_dma_mapped:1;
- void (*complete)(void*context);
- void *context;
- unsigned actual_length;
- int status;
- struct list_head queue;
- void *state;
- };
transfer这个spi_message所包含有的spi_transfer链表头。
is_dma_mappedspi_transfer中tx_dma和rx_dma是否已经mapped。
complete回调函数
context 提供给complete的可选参数
actual_lengthspi_message已经传输了的字节数
status 出错与否,错误时返回errorcode
queue 和state 供controller驱动内部使用
在每次使用spi_message可以使用函数void spi_message_init(structspi_message *m);来初始化。
向spi_message添加transfers可以使用spi_message_add_tail()函数:
void spi_message_add_tail(structspi_transfer *t, struct spi_message *m);
一旦你准备好了spi_message,就可以使用spi_async()来向SPI系统提交了:
int spi_async(struct spi_device *spi,struct spi_message *message);
因为是异步的,一提交就立马返回了,这也就是说需要同步机制(complete就是了)。他确保不会睡眠,可安全的在中断handler或其他不可休眠的代码中调用。稍后会念念他的好的。
使用spi_async()需要注意的是,在complete()未返回前不要轻易访问你一提交的spi_transfer中的buffer。也不能释放SPI系统正在使用的buffer。一旦你的complete返回了,这些buffer就又是你的了。
使用完成回调机制稍显复杂,可以使用SPI系统提供的另一个同步版本:spi_sync():
int spi_sync(struct spi_device *spi,struct spi_message *message);
因为是同步的,spi_sync提交完spi_message后不会立即返回,会一直等待其被处理。一旦返回就可以重新使用buffer了。spi_sync()在drivers/spi/spi.c中实现,其调用了spi_async(),并休眠直至complete返回。
- 1 #include <linux/init.h>
- 2 #include <linux/module.h>
- 3 #include <linux/ioctl.h>
- 4 #include <linux/fs.h>
- 5 #include <linux/device.h>
- 6 #include <linux/err.h>
- 7 #include <linux/list.h>
- 8 #include <linux/errno.h>
- 9 #include <linux/mutex.h>
- 10 #include <linux/slab.h>
- 11 #include <linux/compat.h>
- 12 #include <linux/spi/spi.h>
- 13 #include <linux/spi/spidev.h>
- 14 #include <asm/uaccess.h>
- 15 #include <linux/gpio.h>
- 16 #include <mach/gpio.h>
- 17 #include <plat/gpio-cfg.h>
- 18 #include <linux/delay.h>
- 19 #include <linux/miscdevice.h>
- 20
- 21 struct spi_device *my_spi;
- 22
- 23 #define RC522_RESET_PIN EXYNOS4_GPK1(0)
- 24
- 25 void my_rc522_reset() // 驱动初始化 (IO部分)
- 26 {
- 27 //printk("************************ %s\n", __FUNCTION__);
- 28 if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
- 29 pr_err("failed to request GPK1_0 for RC522 reset control\n");
- 30
- 31 s3c_gpio_se

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。