驱动位于内核目录下 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做了比较好的运用,可以好好的学习下