一 SPI协议概括
SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200.
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。
(1)SDO – 主设备数据输出,从设备数据输入
(2)SDI – 主设备数据输入,从设备数据输出
(3)SCLK – 时钟信号,由主设备产生
(4)CS – 从设备使能信号,由主设备控制
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。
接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。
要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及 /SS,其中SPICLK是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。在一个SPI通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。
SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3接4-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。
二 SPI协议举例
SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。
那么第一个上升沿来的时候 数据将会是sdo=1;寄存器=0101010x。下降沿到来的时候,sdi上的电平将所存到寄存器中去,那么这时寄存器=0101010sdi,这样在 8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi时序。
举例:
假设主机和从机初始化就绪:并且主机的sbuff=0xaa,从机的sbuff=0x55,下面将分步对spi的8个时钟周期的数据情况演示一遍:假设上升沿发送数据


这样就完成了两个寄存器8位的交换,上面的上表示上升沿、下表示下降沿,sdi、sdo相对于主机而言的。其中ss引脚作为主机的时候,从机可以把它拉底被动选为从机,作为从机的是时候,可以作为片选脚用。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的命令准备数据,主机在下一个8位时钟周期才把数据读回来。
SPI 总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束 中断标志;写冲突保护;总线竞争保护等。下图示出SPI总线工作的四种方式,其中使用的最为广泛的是SPI0和SPI3方式 (实线表示):

SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。
SPI总线包括1根串行同步时钟信号线以及2根数据线。
二.LINUX中SPI驱动的 基本数据结构
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来描述。
Linux的SPI子系统采用主机驱动和外设驱动分离的思想,首先主机SPI控制器是一种平台设备,因此它以platform的方式注册进内核,外设的信息是以boardinfo形式静态定义的,在创建spi_master时,会根据外设的bus_num和主机的bus_num是否相等,来选择是否将该外设挂接在该SPI主控制器下。
SPI主要有这几个数据结构:spi_master,spi_driver,spi_device,bus_type,class,spi_board_info,spi_message,spi_transfer.
struct spi_master用了描述主控制器代表一个SPI接口,或者叫一个SPI主机控制器,一个接口对应一条SPI总线,master->bus_num则记录了这个总线号
struct spi_master {
struct device dev;
struct list_head list; //用于挂接到链表头board_list上
/* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2. software
* would normally use bus_num=2 for that controller.
*/
s16 bus_num; /*总线编号,从零开始.系统会用这个值去和系统中board_list链表中加入的每一个boardinfo结构(每个boardinfo结构都是一个spi_board_info的集合,每一个spi_board_info都是对应一个SPI(从)设备的描述)中的每一个spi_board_info中的bus_num进行匹配,如果匹配上就说明这个spi_board_info描述的SPI(从)设备是链接在此总线上的,因此就会调用spi_new_device去创建一个spi_device*/
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect; //支持的外设数量,从设备的片选号不能大于这个数量
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment; //dma对其要求,改变spi_device的特性如:传输模式,字长,时钟频率
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
i/l/s/spi.h
u16 mode_bits;
/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT: this may be called when transfers to another
* device are active. DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int (*setup)(struct spi_device *spi); //添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送并且调用注册的回调函数complete
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg); //传输函数
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);
};
struct spi_device用来描述一个SPI从设备
struct spi_device {
struct device dev; //内嵌标准device结构
struct spi_master *master; //SPI主控制器
u32 max_speed_hz; //时钟速率
u8 chip_select; //设备编号
u8 mode; //模式
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
u8 bits_per_word; //每个字节的比特数
int irq; //中断号
void *controller_state; //控制状态
void *controller_data; //私有数据
char modalias[SPI_NAME_SIZE]; //设备名,在和从设备驱动匹配时会用到
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
struct spi_driver用来描述一个SPI从设备的驱动,它的形式和struct platform_driver是一致的
struct spi_driver {
const struct spi_device_id *id_table; //匹配的设备表
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;
};
该结构体的初始化函数在spi_register_driver里
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver); //注册标准的driver,此时会去匹配bus设备链表上支持的device,找到会调用相应函数
}
EXPORT_SYMBOL_GPL(spi_register_driver);
SPI子系统初始化的第一步就是将SPI总线注册进内核,并且在/sys下创建一个spi_master的类,以后注册的从设备都将挂接在该总线下。下列函数位于/driver/spi/spi.c中
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //初始化缓存
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type); //注册spi总线,此步骤之后就会在/sys/bus目录下生成spi子目录
if (status < 0)
goto err1;
status = class_register(&spi_master_class); //注册spi类,此步骤之后会在/sys/class目录下生成spi_master子目录
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
spi_bus_type结构体
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.pm = &spi_pm, // 和电源管理相关的一些函数的结构封装
};
EXPORT_SYMBOL_GPL(spi_bus_type);
spi_master_class结构体
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
来看挂接在SPI总线下的从设备和从设备驱动是如何匹配的,也就是spi_match_device函数
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/*在驱动查找设备ID,找到返回1,否则返回0*/
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0; //比较设备别名和驱动名称,匹配返回真
}
SPI是以platform平台设备的方式注册进内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。
初始化的入口函数在/drivers/spi/spi_s3c24xx.c里
static struct platform_driver s3c24xx_spi_driver = {
.remove = __exit_p(s3c24xx_spi_remove),
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.pm = S3C24XX_SPI_PMOPS,
},
};
static int __init s3c24xx_spi_init(void)
{
return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
}
platform_driver_probe()会调用platform_driver_register()来注册驱动,然后在注册的过程中寻求匹配的platform_device,一旦匹配成功,便会调用probe函数,也就是s3c24xx_spi_probe(),在看这个函数之前,还得介绍几个相关的数据结构。
struct s3c2410_spi_info是一个板级结构,也是在移植时就定义好的,在初始化spi_master时用到,platform_device-->dev-->platform_data会指向这个结构。
struct s3c2410_spi_info {
int pin_cs; /* simple gpio cs */
unsigned int num_cs; /* total chipselects */
int bus_num; /* bus number to use. */
unsigned int use_fiq:1; /* use fiq */
void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
};
struct s3c24xx_spi用来具体描述s3c24xx平台上一个SPI控制器
struct s3c24xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *regs;
int irq;
int len;
int count;
struct fiq_handler fiq_handler;
enum spi_fiq_mode fiq_mode;
unsigned char fiq_inuse;
unsigned char fiq_claimed;
void (*set_cs)(struct s3c2410_spi_info *spi,
int cs, int pol);
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
struct clk *clk;
struct resource *ioarea;
struct spi_master *master;
struct spi_device *curdev;
struct device *dev;
struct s3c2410_spi_info *pdata;
};
struct spi_bitbang结构用于控制实际的数据传输
struct spi_bitbang {
struct workqueue_struct *workqueue; //工作队列
struct work_struct work;
spinlock_t lock;
struct list_head queue;
u8 busy;
u8 use_dma;
u8 flags; /* extra spi->mode support */
struct spi_master *master;
/* setup_transfer() changes clock and/or wordsize to match settings
* for this transfer; zeroes restore defaults from spi_device.
*/
int (*setup_transfer)(struct spi_device *spi,
struct spi_transfer *t); // 用于设置设备传输时的时钟,字长等
void (*chipselect)(struct spi_device *spi, int is_on);
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0
/* txrx_bufs() may handle dma mapping for transfers that don't
* already have one (transfer.{tx,rx}_dma is zero), or use PIO
*/
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t); // 针对于平台的传输控制函数
/* txrx_word[SPI_MODE_*]() just looks like a shift register */
u32 (*txrx_word[4])(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);
};
下面来看s3c24xx_spi_probe()函数的实现,代码在driver/spi/spi_s3c24xx.c里
static int __init s3c24xx_spi_probe(struct platform_device *pdev){
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
struct spi_master *master;
struct resource *res;
int err = 0;
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); //创建spi_master,大小为 s truct spi_master+struct s3c24xx_spi并将spi_master->private_data指向s3c24xx_spi
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
hw = spi_master_get_devdata(master); //才master获取s3c24xx_spi
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
hw->pdata = pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied\n");
err = -ENOENT;
goto err_no_pdata;
}
platform_set_drvdata(pdev, hw); //设置平台的私有数据为s3c24xx_spi
init_completion(&hw->done); //初始化等待队列
/* initialise fiq handler */
s3c24xx_spi_initfiq(hw);
/* setup the master state. */
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = pdata->bus_num;
/* setup the state for the bitbang driver */
/*设置bitbang的所属master和控制传输的相关函数*/
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->master->setup = s3c24xx_spi_setup;
hw->master->cleanup = s3c24xx_spi_cleanup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_no_iores;
}hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* setup any gpio we can */
if (!pdata->set_cs) {
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs;
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
hw->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, resource_size(res));
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); //申请中断
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_no_irq;
}
hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* setup any gpio we can */
if (!pdata->set_cs) {
if (pdata->pin_cs < 0) {
dev_err(&pdev->dev, "No chipselect pin\n");
goto err_register;
}
err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev)); //申请GPIO口
if (err) {
dev_err(&pdev->dev, "Failed to get gpio for cs\n");
goto err_register;
}
hw->set_cs = s3c24xx_spi_gpiocs; //设定片选信号
gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang); //注册SPI控制器
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
if (hw->set_cs == s3c24xx_spi_gpiocs)
gpio_free(pdata->pin_cs);
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
err_no_iomap:
release_resource(hw->ioarea);
kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
spi_master_put(hw->master);
err_nomem:
return err;
}
下面看一下spi_bitbang_start函数,定义在drivers/spi/spi_bitbang.c里
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work, bitbang_work); //初始化工作队列,处理函数为bitbang_work
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->mode_bits)
bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
} else if (!bitbang->master->setup)
return -EINVAL;
if (bitbang->master->transfer == spi_bitbang_transfer &&
!bitbang->setup_transfer)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;
bitbang->workqueue = create_singlethread_workqueue(
dev_name(bitbang->master->dev.parent)); //调用create_singlethread_workqueue创建单个工作线程
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
status = spi_register_master(bitbang->master); //创建spi_master
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
下一个关键函数就是spi_register_master(),用于注册spi_master,定义在drivers/spi/spi.c里
int spi_register_master(struct spi_master *master){
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = master->dev.parent;
struct boardinfo *bi;
int status = -ENODEV;
int dynamic = 0;
if (!dev)
return -ENODEV;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == 0)
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
/* FIXME switch to an IDR based scheme, something like
* I2C now uses, so we can't run out of "dynamic" IDs
*/
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev); //添加spi_master
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info); //
mutex_unlock(&board_lock);
status = 0;
/* Register devices from the device tree */
of_register_spi_devices(master);
done:
return status;
}
追踪 spi_match_master_to_boardinfo
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (master->bus_num != bi->bus_num)
return;
dev = spi_new_device(master, bi);
if (!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
最后一步就是将相应的从设备注册进内核
struct spi_device *spi_new_device(struct spi_master *master,struct spi_board_info *chip)
{
struct spi_device *proxy;
int status;
/* NOTE: caller did any chip->bus_num checks necessary.
*
* Also, unless we change the return value convention to use
* error-or-pointer (not NULL-or-pointer), troubleshootability
* suggests syslogged diagnostics are best here (ugh).
*/
proxy = spi_alloc_device(master);
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
status = spi_add_device(proxy); //将新设备添加进内核
if (status < 0) {
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
int i;
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
if (!bi)
return -ENOMEM;
for (i = 0; i < n; i++, bi++, info++) {
struct spi_master *master;
memcpy(&bi->board_info, info, sizeof(*info));
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list); //将boardinfo添加到链表board_list
list_for_each_entry(master, &spi_master_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
}
return 0;
}
以上的spi_board_info是板级信息,是在移植时就写好的,并且要将其注册
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE]; //名字
const void *platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz; //最高传输速率
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num; //所属的spi_master编号
u16 chip_select; //片选号
/* mode becomes spi_device.mode, and is essential for chips
spi设备文件的自动产生代码分析
spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送,后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。
在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。来看一下spidev_init函数,在drivers/spi/spidev.c里定义
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
/* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handles the rest.
*/
};
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); /*将spidev作为字符设备注册*/
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); /*创建spidev类*/
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); /*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
struct spidev_data {
dev_t devt; //设备号
spinlock_t spi_lock;
struct spi_device *spi;
struct list_head device_entry; //spi设备链表device_list挂接点
/* buffer is NULL unless this device is open (users > 0) */
struct mutex buf_lock;
unsigned users;
u8 *buffer;
};
与相应的从设备匹配成功后,则调用spidev中的probe函数
static int __devinit spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status; unsigned long minor;
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
if (!spidev) return -ENOMEM;
/* Initialize the driver data */
spidev->spi = spi;
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
/* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS); //寻找一个最小的次设备号
if (minor < N_SPI_MINORS) {
struct device *dev;
/*下面用于在sys/class/spidev下产生类似于spidev%d.%d形式的节点,这样,udev等工具就可以自动在dev下创建相应设备号的设备节点*/
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) {
set_bit(minor, minors); //将minors的相应位置位,表示该位对应的次设备号已被占用
list_add(&spidev->device_entry, &device_list); //将创建的spidev添加到全局链表device_list ,以便在open函数中使用
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev); //设置spi->dev->p = spidev
else
kfree(spidev);
return status;
}
当我们利用板级信息添加一个设备的时候,该driver如果匹配到这个设备,那么就会自动为其创建设备节点,封装spidev_data
信息,并且挂到全局设备链表device_list。来看一下刚才注册的字符设备的统一操作集:
static const struct file_operations spidev_fops = {.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It'll simplify things
* too, except for the locking.
*/
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,
};
看一下open函数
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;
mutex_lock(&device_list_lock);
/*遍历spi设备链表device_list,根据设备号找到spidev_data*/
list_for_each_entry(spidev, &device_list, device_entry) {
if (spidev->devt == inode->i_rdev) {
status = 0;
break;
}
}
if (status == 0) {
if (!spidev->buffer) {
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spidev->buffer) {
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
status = -ENOMEM;
}
}
if (status == 0) {
spidev->users++; //设备用户量加1
filp->private_data = spidev; //传到filp的私有成员,在read,write的时候可以从其得到该结构
nonseekable_open(inode, filp);
}
} else
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock);
return status;
}
接下来开read函数
static ssize_t
spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
return -EMSGSIZE;
spidev = filp->private_data;
mutex_lock(&spidev->buf_lock);
status = spidev_sync_read(spidev, count);
if (status > 0) {
unsigned long missing;
missing = copy_to_user(buf, spidev->buffer, status);
if (missing == status)
status = -EFAULT;
else
status = status - missing;
}
mutex_unlock(&spidev->buf_lock);
return status;
}
在spidev_read函数里调用了spidev_sync_read
static inline ssize_t
spidev_sync_read(struct spidev_data *spidev, size_t len)
{
/*临时的传输操作结构*/
struct spi_transfer t = {
.rx_buf = spidev->buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m); //初始化该结构
spi_message_add_tail(&t, &m); //加到请求队列尾
return spidev_sync(spidev, &m);
}
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf; //发送缓冲区
void *rx_buf; //接受缓冲区
unsigned len; //传输数据的长度
dma_addr_t tx_dma;
dma_addr_t rx_dma;
unsigned cs_change:1; //该位为1,则表示当transfer传输完后,改变片选信号
u8 bits_per_word; //字比特数
u16 delay_usecs; //传输后的延时
u32 speed_hz; //指定的时钟
struct list_head transfer_list;
};
看一下其中的spi_message结构体
struct spi_message {
struct list_head transfers; //用于链接spi_transfer
struct spi_device *spi; //指向目的从设备
unsigned is_dma_mapped:1;
/* REVISIT: we might want a flag affecting the behavior of the
* last transfer allowing things like "read 16 bit length L"
* immediately followed by "read L bytes". Basically imposing
* a specific message scheduling algorithm.
*
* Some controller drivers (message-at-a-time queue processing)
* could provide that as their default scheduling algorithm. But
* others (with multi-message pipelines) could need a flag to
* tell them about such special cases.
*/
/* completion is reported through a callback */
void (*complete)(void *context); //用于异步传输完成时调用的回调函数
void *context; //回调函数的参数
unsigned actual_length; //实际传输的长度
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue; //用于将该message链入bitbang等待队列
void *state;
};
spidev_sync函数
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete; //设置完成量
message->context = &done;
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL)
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message); //spi_async就是在Linux核心中提供的传输函数
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done); //等待完成,如果传输正确,函数返回值为传输的字节数
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
继续追踪看spi_async函数
int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
int ret;
unsigned long flags;
spin_lock_irqsave(&master->bus_lock_spinlock, flags);
if (master->bus_lock_flag)
ret = -EBUSY;
else
ret = __spi_async(spi, message);
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
return ret;
}
继续追踪
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
/* Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switches direction) or where
* either MOSI or MISO is missing. They can also be caused by
* software limitations.
*/
if ((master->flags & SPI_MASTER_HALF_DUPLEX)
|| (spi->mode & SPI_3WIRE)) {
struct spi_transfer *xfer;
unsigned flags = master->flags;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
if (xfer->rx_buf && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
return -EINVAL;
}
}
message->spi = spi;
message->status = -EINPROGRESS;
return master->transfer(spi, message); //最后调用master->transfer具体的平台相关结构
}
spidev_message函数的功能是将用户空间传来的数据段包装成spi_message并继续向下传递
static int spidev_message(struct spidev_data *spidev,
struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total;
u8 *buf;
int status = -EFAULT;
spi_message_init(&msg);
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi传输指针内存
if (k_xfers == NULL)
return -ENOMEM;
/* Construct spi_message, copying any tx data to bounce buffer.
* We walk the array of user-provided transfers, using each one
* to initialize a kernel version of the same transfer.
*/
buf = spidev->buffer; //获取spidev_data的缓冲区
total = 0;
/*n=xfers为spi_ioc_transfer个数,u_tmp = u_xfers为要处理的spi_ioc_transfer指针*/
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
n;
n--, k_tmp++, u_tmp++) {
k_tmp->len = u_tmp->len; //设置传输信息的长度
total += k_tmp->len; //累加传输信息的总长度
if (total > bufsiz) {
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) { //接受缓冲区有数据
k_tmp->rx_buf = buf; //缓冲区指向buf
if (!access_ok(VERIFY_WRITE, (u8 __user *)
(uintptr_t) u_tmp->rx_buf,
u_tmp->len))
goto done;
}
if (u_tmp->tx_buf) { //发送缓冲区有数据
k_tmp->tx_buf = buf; //缓冲区指向buf
if (copy_from_user(buf, (const u8 __user *) //用户空间复制数据到buf
(uintptr_t) u_tmp->tx_buf,
u_tmp->len))
goto done;
}
buf += k_tmp->len; //缓冲区指针移动一个传输信息的长度
k_tmp->cs_change = !!u_tmp->cs_change; //设置cs_change
k_tmp->bits_per_word = u_tmp->bits_per_word; //设置bits_per_word 一个字多少位
k_tmp->delay_usecs = u_tmp->delay_usecs; //设置delay_usecs 毫秒级延时
k_tmp->speed_hz = u_tmp->speed_hz; //设置speed_hz 速率
#ifdef VERBOSE
dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
spi_message_add_tail(k_tmp, &msg);
}
status = spidev_sync(spidev, &msg); //进行数据传输
if (status < 0)
goto done;
/* copy any rx data out of bounce buffer */
buf = spidev->buffer; //获取spidev_data缓冲区指针
/*for循环的作用是将spi_ioc_transfer批量转换为spi传递结构体spi_transfer,然后添加进spi传递事务队列*/
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { //批量从内核空间复制spi_ioc_transfer到用户空间
if (u_tmp->rx_buf) { //判断是否存在接收缓冲区
if (__copy_to_user((u8 __user *)
(uintptr_t) u_tmp->rx_buf, buf,
u_tmp->len)) {
status = -EFAULT;
goto done;
}
}
buf += u_tmp->len; //buf指针位置调整指向下一个spi_ioc_transfer
}
status = total; //status等于实际传输的数据长度
done:
kfree(k_xfers); //释放k_xfers
return status; //返回实际传输的数据长度
}
static long
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;
/* Check type and command number */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) //判断控制命令的类型
return -ENOTTY;
/* Check access direction once here; don't repeat below.
* IOC_DIR is from the user perspective, while access_ok is
* from the kernel perspective; so they look reversed.
*/
if (_IOC_DIR(cmd) & _IOC_READ) //判断控制命令的方向是否为读read
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判断控制命令的方向是否为写write
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd)); //判断传输数据大小
if (err)
return -EFAULT;
/* guard against device removal before, or while,
* we issue this ioctl.
*/
spidev = filp->private_data; //从文件私有数据中获取spidev_data
spin_lock_irq(&spidev->spi_lock); //上自旋锁
spi = spi_dev_get(spidev->spi); //获取spi设备
spin_unlock_irq(&spidev->spi_lock); //解自旋锁
if (spi == NULL) //获取spi设备失败
return -ESHUTDOWN;
/* use the buffer lock here for triple duty:
* - prevent I/O (from us) so calling spi_setup() is safe;
* - prevent concurrent SPI_IOC_WR_* from morphing
* data fields while SPI_IOC_RD_* reads them;
* - SPI_IOC_MESSAGE needs the buffer locked "normally".
*/
mutex_lock(&spidev->buf_lock); //上互斥锁
switch (cmd) {
/* read requests */
case SPI_IOC_RD_MODE: //设置spi读模式
retval = __put_user(spi->mode & SPI_MODE_MASK,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST: //设置spi读最低有效位
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD: //设置spi读每个字含多个个位
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ: //设置spi读最大速率
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
/* write requests */
case SPI_IOC_WR_MODE: //设置spi写模式
retval = __get_user(tmp, (u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u8)tmp;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST: //设置spi写最低有效位
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode; //获取spi设备模式
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "%csb first\n",
tmp ? 'l' : 'm');
}
break;
case SPI_IOC_WR_BITS_PER_WORD: //设置spi写每个字含多个个位
retval = __get_user(tmp, (__u8 __user *)arg); //获取用户空间数据
if (retval == 0) {
u8 save = spi->bits_per_word; //获取spi设备 每个字含多少位
spi->bits_per_word = tmp; //更新新的spi设备 每个字含多少位
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->bits_per_word = save; //还原spi设备 每个字含多少位
else
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ: //设置spi写最大速率
retval = __get_user(tmp, (__u32 __user *)arg); //用户空间获取数据
if (retval == 0) {
u32 save = spi->max_speed_hz; //获取spi设备最大速率
spi->max_speed_hz = tmp; //更新新的spi设备最大速率
retval = spi_setup(spi); //配置spi设备
if (retval < 0)
spi->max_speed_hz = save;
else
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
}
break;
default:
/* segmented and/or full-duplex I/O request */
/*命令必须为写方向的命令,且传输数据必须是SPI_IOC_MESSAGE()修饰的命令*/
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
|| _IOC_DIR(cmd) != _IOC_WRITE) {
retval = -ENOTTY;
break;
}
tmp = _IOC_SIZE(cmd); //计算传输数据大小
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { //判断是否为spi_ioc_transfer对齐
retval = -EINVAL;
break;
}
n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出spi_ioc_transfer数据的个数
if (n_ioc == 0)
break;
/* copy into scratch area */
ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指针ioc内存
if (!ioc) {
retval = -ENOMEM;
break;
}
if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //从用户空间复制到内核空间
kfree(ioc);
retval = -EFAULT;
break;
}
/* translate to spi_message, execute */
retval = spidev_message(spidev, ioc, n_ioc);
kfree(ioc); //释放ioc内存
break;
}
mutex_unlock(&spidev->buf_lock); //解互斥锁
spi_dev_put(spi); //增加spi设备的引用计数
return retval;
}