SPI

 SPI协议概括

SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROMFLASH,实实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200.

SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。

1SDO     – 主设备数据输出,从设备数据输入

2SDI      – 主设备数据输入,从设备数据输出

3SCLK   – 时钟信号,由主设备产生

4CS        – 从设备使能信号,由主设备控制

其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。

接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDISDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。

要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。

在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。

AT91RM9200SPI接口主要由4个引脚构成:SPICLKMOSIMISO /SS,其中SPICLK是整个SPI总线的公用时钟,MOSIMISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。在一个SPI通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。

SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,NPCS0~34-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。

 SPI协议举例

SPI
是一个环形总线结构,由sscs)、scksdisdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
       
假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。
       
那么第一个上升沿来的时候 数据将会是sdo=1;寄存器=0101010x。下降沿到来的时候,sdi上的电平将所存到寄存器中去,那么这时寄存器=0101010sdi,这样在 8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi时序。

举例:
      
假设主机和从机初始化就绪:并且主机的sbuff=0xaa,从机的sbuff=0x55,下面将分步对spi8个时钟周期的数据情况演示一遍:假设上升沿发送数据

SPI协议简介 - Filter - 涂~墙
 
SPI协议简介 - Filter - 涂~墙


这样就完成了两个寄存器8位的交换,上面的上表示上升沿、下表示下降沿,sdisdo相对于主机而言的。其中ss引脚作为主机的时候,从机可以把它拉底被动选为从机,作为从机的是时候,可以作为片选脚用。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的命令准备数据,主机在下一个8位时钟周期才把数据读回来。

      SPI 总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束 中断标志;写冲突保护;总线竞争保护等。下图示出SPI总线工作的四种方式,其中使用的最为广泛的是SPI0SPI3方式 (实线表示):

               

SPI协议简介 - Filter - 涂~墙
                                                      SPI总线四种工作方式

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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值