spi 驱动工作流程

来源:互联网 发布:java与xml 第三版 pdf 编辑:程序博客网 时间:2024/05/21 10:06
驱动位于内核目录下 device/spi/spidev.c

整个spi驱动的工作流程如下

应用层调用设备驱动文件   spidev_ioctl函数

spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        spidev_message(spidev, ioc, n_ioc);
               spidev_sync(spidev, &msg);
                    status = spi_async(spidev->spi, message);//将msg加入传输队列,同时调度工作队列handle函数处理msg队列
                              wait_for_completion(&done);//等待异步传输成功(异步又工作队列handle函数处理spi底层) 


//异步传输函数spi_async
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);//在transfer函数中会发出工作队列调度命令
}


// master->transfer函数指针指向的s3c64xx_spi_transfer函数
static int s3c64xx_spi_transfer(struct spi_device *spi,
      struct spi_message *msg)
{
 struct s3c64xx_spi_driver_data *sdd;
 unsigned long flags;

 sdd = spi_master_get_devdata(spi->master);

 spin_lock_irqsave(&sdd->lock, flags);

 if (sdd->state & SUSPND) {
  spin_unlock_irqrestore(&sdd->lock, flags);
  return -ESHUTDOWN;
 }

 msg->status = -EINPROGRESS;
 msg->actual_length = 0;

 list_add_tail(&msg->queue, &sdd->queue);//添加msg到待处理队列上

 queue_work(sdd->workqueue, &sdd->work);//发出工作队列调度处理命令

 spin_unlock_irqrestore(&sdd->lock, flags);

 return 0;
}


//工作队列处理函数(被调度函数)

static void s3c64xx_spi_work(struct work_struct *work)
{
 struct s3c64xx_spi_driver_data *sdd = container_of(work,
     struct s3c64xx_spi_driver_data, work);
 unsigned long flags;

 /* Acquire DMA channels */
 while (!acquire_dma(sdd))
  msleep(10);

 spin_lock_irqsave(&sdd->lock, flags);

 while (!list_empty(&sdd->queue)
    && !(sdd->state & SUSPND)) {

  struct spi_message *msg;

  msg = container_of(sdd->queue.next, struct spi_message, queue);//从队列取msg

  list_del_init(&msg->queue);//删除队列上取出的msg单元

  /* Set Xfer busy flag */
  sdd->state |= SPIBUSY;

  spin_unlock_irqrestore(&sdd->lock, flags);

  handle_msg(sdd, msg);//spi底层发送接收处理函数    里面处理完会唤醒 msg->complete(msg->context);前面的休眠操作同步,具体可以到该函数里面去看

  spin_lock_irqsave(&sdd->lock, flags);

  sdd->state &= ~SPIBUSY;
 }

 spin_unlock_irqrestore(&sdd->lock, flags);

 /* Free DMA channels */
 s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
 s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
}


//异步处理的流程
s3c64xx_spi_transfer
            queue_work
                   s3c64xx_spi_work
                             handle_msg
                                            msg->complete //唤醒同步前面等待
        整个驱动对 工作队列 ,wait_for_completion等内核api做了比较好的运用,可以好好的学习下
0 0