zoukankan      html  css  js  c++  java
  • Linux SPI框架(下)

     

    分类: Linux驱动程序 3006人阅读 评论(2) 收藏 举报

    水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7737833  

         本节以spidev设备驱动为例,来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送,后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样,还是习惯先画一张图来描述数据传输的主要过程。

     

             在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。

    [cpp] view plaincopy
     
    1. static int __init spidev_init(void)  
    2. {  
    3.     int status;  
    4.   
    5.     /* Claim our 256 reserved device numbers.  Then register a class 
    6.      * that will key udev/mdev to add/remove /dev nodes.  Last, register 
    7.      * the driver which manages those device numbers. 
    8.      */  
    9.     BUILD_BUG_ON(N_SPI_MINORS > 256);  
    10.     /*将spidev作为字符设备注册*/  
    11.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
    12.     if (status < 0)  
    13.         return status;  
    14.   
    15.     /*创建spidev类*/  
    16.     spidev_class = class_create(THIS_MODULE, "spidev");  
    17.     if (IS_ERR(spidev_class)) {  
    18.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  
    19.         return PTR_ERR(spidev_class);  
    20.     }  
    21.   
    22.     /*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/  
    23.     status = spi_register_driver(&spidev_spi);  
    24.     if (status < 0) {  
    25.         class_destroy(spidev_class);  
    26.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  
    27.     }  
    28.     return status;  
    29. }  


    与相应的从设备匹配成功后,则调用spidev中的probe函数

    [cpp] view plaincopy
     
    1. static int spidev_probe(struct spi_device *spi)  
    2. {  
    3.     struct spidev_data  *spidev;  
    4.     int         status;  
    5.     unsigned long       minor;  
    6.   
    7.     /* Allocate driver data */  
    8.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  
    9.     if (!spidev)  
    10.         return -ENOMEM;  
    11.   
    12.     /* Initialize the driver data */  
    13.     spidev->spi = spi;//设定spi  
    14.     spin_lock_init(&spidev->spi_lock);  
    15.     mutex_init(&spidev->buf_lock);  
    16.   
    17.     INIT_LIST_HEAD(&spidev->device_entry);  
    18.   
    19.     /* If we can allocate a minor number, hook up this device. 
    20.      * Reusing minors is fine so long as udev or mdev is working. 
    21.      */  
    22.     mutex_lock(&device_list_lock);  
    23.     minor = find_first_zero_bit(minors, N_SPI_MINORS);//寻找没被占用的次设备号  
    24.     if (minor < N_SPI_MINORS) {  
    25.         struct device *dev;  
    26.         /*计算设备号*/  
    27.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);  
    28.         /*在spidev_class下创建设备*/  
    29.         dev = device_create(spidev_class, &spi->dev, spidev->devt,  
    30.                     spidev, "spidev%d.%d",  
    31.                     spi->master->bus_num, spi->chip_select);  
    32.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  
    33.     } else {  
    34.         dev_dbg(&spi->dev, "no minor number available! ");  
    35.         status = -ENODEV;  
    36.     }  
    37.     if (status == 0) {  
    38.         set_bit(minor, minors);//将minors的相应位置位,表示该位对应的次设备号已被占用  
    39.         list_add(&spidev->device_entry, &device_list);//将创建的spidev添加到device_list  
    40.     }  
    41.     mutex_unlock(&device_list_lock);  
    42.   
    43.     if (status == 0)  
    44.         spi_set_drvdata(spi, spidev);  
    45.     else  
    46.         kfree(spidev);  
    47.   
    48.     return status;  
    49. }  


    然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程,实际上spidev_read()和其是差不多的,只是前面的一些步骤不一样,可以参照上图。

    [cpp] view plaincopy
     
    1. static ssize_t  
    2. spidev_write(struct file *filp, const char __user *buf,  
    3.         size_t count, loff_t *f_pos)  
    4. {  
    5.     struct spidev_data  *spidev;  
    6.     ssize_t         status = 0;  
    7.     unsigned long       missing;  
    8.   
    9.     /* chipselect only toggles at start or end of operation */  
    10.     if (count > bufsiz)  
    11.         return -EMSGSIZE;  
    12.   
    13.     spidev = filp->private_data;  
    14.   
    15.     mutex_lock(&spidev->buf_lock);  
    16.     //将用户要发送的数据拷贝到spidev->buffer  
    17.     missing = copy_from_user(spidev->buffer, buf, count);  
    18.     if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()  
    19.         status = spidev_sync_write(spidev, count);  
    20.     } else  
    21.         status = -EFAULT;  
    22.     mutex_unlock(&spidev->buf_lock);  
    23.   
    24.     return status;  
    25. }  


     

    [cpp] view plaincopy
     
    1. static inline ssize_t  
    2. spidev_sync_write(struct spidev_data *spidev, size_t len)  
    3. {  
    4.     struct spi_transfer t = {//设置传输字段  
    5.             .tx_buf     = spidev->buffer,  
    6.             .len        = len,  
    7.         };  
    8.     struct spi_message   m;//创建message  
    9.   
    10.     spi_message_init(&m);  
    11.     spi_message_add_tail(&t, &m);//将transfer添加到message中  
    12.     return spidev_sync(spidev, &m);  
    13. }  


    我们来看看struct spi_transfer和struct spi_message是如何定义的

    [cpp] view plaincopy
     
    1. struct spi_transfer {  
    2.     /* it's ok if tx_buf == rx_buf (right?) 
    3.      * for MicroWire, one buffer must be null 
    4.      * buffers must work with dma_*map_single() calls, unless 
    5.      *   spi_message.is_dma_mapped reports a pre-existing mapping 
    6.      */  
    7.     const void  *tx_buf;//发送缓冲区  
    8.     void        *rx_buf;//接收缓冲区  
    9.     unsigned    len;    //传输数据的长度  
    10.   
    11.     dma_addr_t  tx_dma;  
    12.     dma_addr_t  rx_dma;  
    13.   
    14.     unsigned    cs_change:1; //该位如果为1,则表示当该transfer传输完后,改变片选信号  
    15.     u8      bits_per_word;//字比特数  
    16.     u16     delay_usecs;  //传输后的延时   
    17.     u32     speed_hz;  //指定的时钟  
    18.   
    19.     struct list_head transfer_list;//用于将该transfer链入message  
    20. };  


     

    [cpp] view plaincopy
     
    1. struct spi_message {  
    2.     struct list_head    transfers;//用于链接spi_transfer  
    3.   
    4.     struct spi_device   *spi;      //指向目的从设备  
    5.   
    6.     unsigned        is_dma_mapped:1;  
    7.   
    8.     /* REVISIT:  we might want a flag affecting the behavior of the 
    9.      * last transfer ... allowing things like "read 16 bit length L" 
    10.      * immediately followed by "read L bytes".  Basically imposing 
    11.      * a specific message scheduling algorithm. 
    12.      * 
    13.      * Some controller drivers (message-at-a-time queue processing) 
    14.      * could provide that as their default scheduling algorithm.  But 
    15.      * others (with multi-message pipelines) could need a flag to 
    16.      * tell them about such special cases. 
    17.      */  
    18.   
    19.     /* completion is reported through a callback */  
    20.     void            (*complete)(void *context);//用于异步传输完成时调用的回调函数  
    21.     void            *context;                  //回调函数的参数  
    22.     unsigned        actual_length;            //实际传输的长度  
    23.     int         status;  
    24.   
    25.     /* for optional use by whatever driver currently owns the 
    26.      * spi_message ...  between calls to spi_async and then later 
    27.      * complete(), that's the spi_master controller driver. 
    28.      */  
    29.     struct list_head    queue; //用于将该message链入bitbang等待队列  
    30.     void            *state;  
    31. };  


    继续跟踪源码,进入spidev_sync(),从这一步开始,read和write就完全一样了

    [cpp] view plaincopy
     
    1. <span style="font-size:12px;">static ssize_t  
    2. spidev_sync(struct spidev_data *spidev, struct spi_message *message)  
    3. {  
    4.     DECLARE_COMPLETION_ONSTACK(done);  
    5.     int status;  
    6.   
    7.     message->complete = spidev_complete;//设置回调函数  
    8.     message->context = &done;              
    9.   
    10.     spin_lock_irq(&spidev->spi_lock);  
    11.     if (spidev->spi == NULL)  
    12.         status = -ESHUTDOWN;  
    13.     else  
    14.         status = spi_async(spidev->spi, message);//调用spi核心层的函数spi_async()  
    15.     spin_unlock_irq(&spidev->spi_lock);  
    16.   
    17.     if (status == 0) {  
    18.         wait_for_completion(&done);  
    19.         status = message->status;  
    20.         if (status == 0)  
    21.             status = message->actual_length;  
    22.     }  
    23.     return status;  
    24. }</span>  


     

    [cpp] view plaincopy
     
    1. static inline int  
    2. spi_async(struct spi_device *spi, struct spi_message *message)  
    3. {  
    4.     message->spi = spi;  
    5.     /*调用master的transfer函数将message放入等待队列*/  
    6.     return spi->master->transfer(spi, message);  
    7. }  

    s3c24xx平台下的transfer函数是在bitbang_start()函数中定义的,为bitbang_transfer()

    [cpp] view plaincopy
     
    1. int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)  
    2. {  
    3.     struct spi_bitbang  *bitbang;  
    4.     unsigned long       flags;  
    5.     int         status = 0;  
    6.   
    7.     m->actual_length = 0;  
    8.     m->status = -EINPROGRESS;  
    9.   
    10.     bitbang = spi_master_get_devdata(spi->master);  
    11.   
    12.     spin_lock_irqsave(&bitbang->lock, flags);  
    13.     if (!spi->max_speed_hz)  
    14.         status = -ENETDOWN;  
    15.     else {  
    16.         list_add_tail(&m->queue, &bitbang->queue);//将message添加到bitbang的等待队列  
    17.         queue_work(bitbang->workqueue, &bitbang->work);//调度运行work  
    18.     }  
    19.     spin_unlock_irqrestore(&bitbang->lock, flags);  
    20.   
    21.     return status;  
    22. }  

    这里可以看到transfer函数不负责实际的数据传输,而是将message添加到等待队列中。同样在spi_bitbang_start()中,有这样一个定义INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函数会被调度运行,类似于底半部机制

    [cpp] view plaincopy
     
    1. static void bitbang_work(struct work_struct *work)  
    2. {  
    3.     struct spi_bitbang  *bitbang =  
    4.         container_of(work, struct spi_bitbang, work);//获取bitbang  
    5.     unsigned long       flags;  
    6.   
    7.     spin_lock_irqsave(&bitbang->lock, flags);  
    8.     bitbang->busy = 1;  
    9.     while (!list_empty(&bitbang->queue)) {//等待队列不为空  
    10.         struct spi_message  *m;  
    11.         struct spi_device   *spi;  
    12.         unsigned        nsecs;  
    13.         struct spi_transfer *t = NULL;  
    14.         unsigned        tmp;  
    15.         unsigned        cs_change;  
    16.         int         status;  
    17.         int         (*setup_transfer)(struct spi_device *,  
    18.                         struct spi_transfer *);  
    19.         /*取出等待队列中的的第一个message*/  
    20.         m = container_of(bitbang->queue.next, struct spi_message,  
    21.                 queue);  
    22.         list_del_init(&m->queue);//将message从队列中删除  
    23.         spin_unlock_irqrestore(&bitbang->lock, flags);  
    24.   
    25.         /* FIXME this is made-up ... the correct value is known to 
    26.          * word-at-a-time bitbang code, and presumably chipselect() 
    27.          * should enforce these requirements too? 
    28.          */  
    29.         nsecs = 100;  
    30.   
    31.         spi = m->spi;  
    32.         tmp = 0;  
    33.         cs_change = 1;  
    34.         status = 0;  
    35.         setup_transfer = NULL;  
    36.   
    37.         /*遍历message中的所有传输字段,逐一进行传输*/  
    38.         list_for_each_entry (t, &m->transfers, transfer_list) {  
    39.   
    40.             /* override or restore speed and wordsize */  
    41.             if (t->speed_hz || t->bits_per_word) {  
    42.                 setup_transfer = bitbang->setup_transfer;  
    43.                 if (!setup_transfer) {  
    44.                     status = -ENOPROTOOPT;  
    45.                     break;  
    46.                 }  
    47.             }  
    48.             /*调用setup_transfer根据transfer中的信息进行时钟、字比特数的设定*/  
    49.             if (setup_transfer) {  
    50.                 status = setup_transfer(spi, t);  
    51.                 if (status < 0)  
    52.                     break;  
    53.             }  
    54.   
    55.             /* set up default clock polarity, and activate chip; 
    56.              * this implicitly updates clock and spi modes as 
    57.              * previously recorded for this device via setup(). 
    58.              * (and also deselects any other chip that might be 
    59.              * selected ...) 
    60.              */  
    61.             if (cs_change) {//使能外设的片选  
    62.                 bitbang->chipselect(spi, BITBANG_CS_ACTIVE);  
    63.                 ndelay(nsecs);  
    64.             }  
    65.             cs_change = t->cs_change;//这里确定进行了这个字段的传输后是否要改变片选状态  
    66.             if (!t->tx_buf && !t->rx_buf && t->len) {  
    67.                 status = -EINVAL;  
    68.                 break;  
    69.             }  
    70.   
    71.             /* transfer data.  the lower level code handles any 
    72.              * new dma mappings it needs. our caller always gave 
    73.              * us dma-safe buffers. 
    74.              */  
    75.             if (t->len) {  
    76.                 /* REVISIT dma API still needs a designated 
    77.                  * DMA_ADDR_INVALID; ~0 might be better. 
    78.                  */  
    79.                 if (!m->is_dma_mapped)  
    80.                     t->rx_dma = t->tx_dma = 0;  
    81.                 /*调用针对于平台的传输函数txrx_bufs*/  
    82.                 status = bitbang->txrx_bufs(spi, t);  
    83.             }  
    84.             if (status > 0)  
    85.                 m->actual_length += status;  
    86.             if (status != t->len) {  
    87.                 /* always report some kind of error */  
    88.                 if (status >= 0)  
    89.                     status = -EREMOTEIO;  
    90.                 break;  
    91.             }  
    92.             status = 0;  
    93.   
    94.             /* protocol tweaks before next transfer */  
    95.             /*如果要求在传输完一个字段后进行delay,则进行delay*/  
    96.             if (t->delay_usecs)  
    97.                 udelay(t->delay_usecs);  
    98.   
    99.             if (!cs_change)  
    100.                 continue;  
    101.               
    102.             /*最后一个字段传输完毕了,则跳出循环*/  
    103.             if (t->transfer_list.next == &m->transfers)  
    104.                 break;  
    105.   
    106.             /* sometimes a short mid-message deselect of the chip 
    107.              * may be needed to terminate a mode or command 
    108.              */  
    109.             ndelay(nsecs);  
    110.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  
    111.             ndelay(nsecs);  
    112.         }  
    113.   
    114.         m->status = status;  
    115.         m->complete(m->context);  
    116.   
    117.         /* restore speed and wordsize */  
    118.         if (setup_transfer)  
    119.             setup_transfer(spi, NULL);  
    120.   
    121.         /* normally deactivate chipselect ... unless no error and 
    122.          * cs_change has hinted that the next message will probably 
    123.          * be for this chip too. 
    124.          */  
    125.         if (!(status == 0 && cs_change)) {  
    126.             ndelay(nsecs);  
    127.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  
    128.             ndelay(nsecs);  
    129.         }  
    130.   
    131.         spin_lock_irqsave(&bitbang->lock, flags);  
    132.     }  
    133.     bitbang->busy = 0;  
    134.     spin_unlock_irqrestore(&bitbang->lock, flags);  
    135. }  


    只要bitbang->queue等待队列不为空,就表示相应的SPI主控制器上还有传输任务没有完成,因此bitbang_work()会被不断地调度执行。 bitbang_work()中的工作主要是两个循环,外循环遍历等待队列中的message,内循环遍历message中的transfer,在bitbang_work()中,传输总是以transfer为单位的。当选定了一个transfer后,便会调用transfer_txrx()函数,进行实际的数据传输,显然这个函数是针对于平台的SPI控制器而实现的,在s3c24xx平台中,该函数为s3c24xx_spi_txrx();

    [cpp] view plaincopy
     
    1. static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)  
    2. {  
    3.     struct s3c24xx_spi *hw = to_hw(spi);  
    4.   
    5.     dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d ",  
    6.         t->tx_buf, t->rx_buf, t->len);  
    7.   
    8.     hw->tx = t->tx_buf;//获取发送缓冲区  
    9.     hw->rx = t->rx_buf;//获取读取缓存区  
    10.     hw->len = t->len;  //获取数据长度  
    11.     hw->count = 0;  
    12.   
    13.     init_completion(&hw->done);//初始化完成量  
    14.   
    15.     /* send the first byte */  
    16.     /*只发送第一个字节,其他的在中断中发送(读取)*/  
    17.     writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);  
    18.   
    19.     wait_for_completion(&hw->done);  
    20.   
    21.     return hw->count;  
    22. }  


     

    [cpp] view plaincopy
     
    1. static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)  
    2. {  
    3.     /*如果tx不为空,也就是说当前是从主机向从机发送数据,则直接将tx[count]发送过去, 
    4.       如果tx为空,也就是说当前是从从机向主机发送数据,则向从机写入0*/  
    5.     return hw->tx ? hw->tx[count] : 0;  
    6. }  


    负责SPI数据传输的中断函数:

    [cpp] view plaincopy
     
    1. static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)  
    2. {  
    3.     struct s3c24xx_spi *hw = dev;  
    4.     unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);  
    5.     unsigned int count = hw->count;  
    6.   
    7.     /*冲突检测*/  
    8.     if (spsta & S3C2410_SPSTA_DCOL) {  
    9.         dev_dbg(hw->dev, "data-collision ");  
    10.         complete(&hw->done);  
    11.         goto irq_done;  
    12.     }  
    13.   
    14.     /*设备忙检测*/  
    15.     if (!(spsta & S3C2410_SPSTA_READY)) {  
    16.         dev_dbg(hw->dev, "spi not ready for tx? ");  
    17.         complete(&hw->done);  
    18.         goto irq_done;  
    19.     }  
    20.   
    21.     hw->count++;  
    22.   
    23.     if (hw->rx)//读取数据到缓冲区  
    24.         hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);  
    25.   
    26.     count++;  
    27.   
    28.     if (count < hw->len)//向从机写入数据  
    29.         writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);  
    30.     else//count == len,一个字段发送完成,唤醒完成量  
    31.         complete(&hw->done);  
    32.   
    33.  irq_done:  
    34.     return IRQ_HANDLED;  
    35. }  

    这里可以看到一点,即使tx为空,也就是说用户申请的是从从设备读取数据,也要不断地向从设备写入数据,只不过写入从设备的是无效数据(0),这样做得目的是为了维持SPI总线上的时钟。至此,SPI框架已分析完毕。

  • 相关阅读:
    每日作业报告
    每日作业报告
    每日作业报告
    每日作业报告
    每日作业报告
    vue路由跳转错误:Error: Redirected when going from "/login" to "/home" via a navigation guard.
    ubuntu20安装.net core SDK
    SpringBoot启动报错:Failed to configure a DataSource: 'url' attribute is not specified and no embedded
    ubuntu18.04安装rap2
    用例图基本用法
  • 原文地址:https://www.cnblogs.com/dolphi/p/3662410.html
Copyright © 2011-2022 走看看