Allein.Cao原创作品,转载请注明出处:
http://blog.csdn.net/alleincao/article/details/7525977
内核版本:2.6.32.2
硬件:S3C2440
设备驱动是在core之上的模块,向上给应用程序提供file_operations接口,应用程序可以通过设备节点访问驱动程序,向下通过core向控制器模块发送数据,控制器模块将数据发送到物理总线上。
spidev.c是一个典型的设备驱动程序,前面提到在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,目的是最大程度保证代码的可移植性,我们以应用程序调用为主线,详细分析spi驱动的数据流流向。
首先来看
-
static struct spi_driver spidev_spi = {
-
.driver = {
-
.name = "spidev",
-
.owner = THIS_MODULE,
-
},
-
.probe = spidev_probe,
-
.remove = __devexit_p(spidev_remove),
-
};
-
static int __init spidev_init(void)
-
{
-
int status;
-
-
-
-
-
-
-
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
-
if (status < 0)
-
return status;
-
-
spidev_class = class_create(THIS_MODULE, "spidev");
-
if (IS_ERR(spidev_class)) {
-
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
-
return PTR_ERR(spidev_class);
-
}
-
-
status = spi_register_driver(&spidev_spi);
-
if (status < 0) {
-
class_destroy(spidev_class);
-
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
-
}
-
return status;
-
}
static struct spi_driver spidev_spi = { //spi_driver
.driver = {
.name = "spidev", //spi_bus_type上spi_despi_devie与spi_driver匹配依赖于此名字
.owner = THIS_MODULE,
},
.probe = spidev_probe, //probe函数
.remove = __devexit_p(spidev_remove),//编译为模块or编译到内核?内核则为NULL,模块为spidev_remove
};
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.
*/
//设备驱动与主设备号一一对应,所以当用户打开主设备号为SPIDEV_MAJOR的设备节点时会调用spidev_fops相关函数BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); //创建spidev_class类
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi); //注册spi_driver
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
}
return status;
}
spidev_probe函数的调用在spi_bus_type的match函数即spi_match_device函数调用之后进行,它的实现相对简单,主要是分配并初始化spidev_data结构体,将其添加到device_list链表,执行完此函数,所有spi_bus_type上spi_devie和spi_driver匹配完毕:
-
static int spidev_probe(struct spi_device *spi)
-
{
-
struct *spidev;
-
int status;
-
unsigned long minor;
-
-
-
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
-
if (!spidev)
-
return -ENOMEM;
-
-
-
spidev->spi = spi;
-
spin_lock_init(&spidev->spi_lock);
-
mutex_init(&spidev->buf_lock);
-
-
INIT_LIST_HEAD(&spidev->device_entry);
-
-
-
-
-
mutex_lock(&device_list_lock);
-
minor = find_first_zero_bit(minors, N_SPI_MINORS);
-
if (minor < N_SPI_MINORS) {
-
struct device *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);
-
list_add(&spidev->device_entry, &device_list);
-
}
-
mutex_unlock(&device_list_lock);
-
-
if (status == 0)
-
spi_set_drvdata(spi, spidev);
-
else
-
kfree(spidev);
-
-
return status;
-
}
static int spidev_probe(struct spi_device *spi) //其入口参数是spi_bus_type上的spi_device
{
struct *spidev;
int status;
unsigned long minor;
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); //每个spi_device对应一个spidev_data
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); //找到第一个0位,作为次设备号,见下面分析
if (minor < N_SPI_MINORS) {
struct device *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); //设置相应位
list_add(&spidev->device_entry, &device_list); //添加到device_list链表
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev); //设置私有数据spi_device–>dev->p->driver_data
else
kfree(spidev);
return status;
}
接下来,我们分析该驱动的调用流程,即file_operations中的成员函数:
-
static const struct file_operations spidev_fops = {
-
.owner = THIS_MODULE,
-
-
-
-
-
.write = spidev_write,
-
.read = spidev_read,
-
.unlocked_ioctl = spidev_ioctl,
-
.open = spidev_open,
-
.release = spidev_release,
-
};
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,
.open = spidev_open,
.release = spidev_release,
};
首先,我们来开open函数spidev_open,其主要目的就是根据用户打开的设备节点的设备号找到对应的spidev_data,也就自然找到对应的spi_device(通过spidev_data->spi,其在probe函数中赋值),这样就很自然地实现用户空间访问spi设备:
-
static int spidev_open(struct inode *inode, struct file *filp)
-
{
-
struct spidev_data *spidev;
-
int status = -ENXIO;
-
-
lock_kernel();
-
mutex_lock(&device_list_lock);
-
-
-
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++;
-
filp->private_data = spidev;
-
nonseekable_open(inode, filp);
-
}
-
} else
-
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
-
-
mutex_unlock(&device_list_lock);
-
unlock_kernel();
-
return status;
-
}
static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;
lock_kernel();
mutex_lock(&device_list_lock);
//根据设备号找到对应的spidev,很自然可以找到对应的spi_device
list_for_each_entry(spidev, &device_list, device_entry) {
if (spidev->devt == inode->i_rdev) {
status = 0;
break;
}
}
if (status == 0) {
if (!spidev->buffer) { //buffer没分配则分配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++;
filp->private_data = spidev; //保存私有数据,供后面的read write等函数直接调用
nonseekable_open(inode, filp);
}
} else
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock);
unlock_kernel();
return status;
}
接下来我们看其他函数,这里read函数和write函数很类似,我们以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;
-
-
-
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;
-
}
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; //取出open函数里设置的spidev_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_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);
-
}
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
spi_message_add_tail(&t, &m); //将spi_transfer添加到spi_message的transfers
return spidev_sync(spidev, &m); //见下面分析
}
接下来,我们跟踪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);
-
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;
-
}
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) //没有对应的spi_device则出错返回,最终依赖于spi_device进行数据收发
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message); //异步传输函数,位于spi.c中,见下面分析
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;
-
-
-
-
-
-
-
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);
-
}
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); //最终调用spi_master的transfer函数
}
通过前面控制器驱动中spi_bitbang_start函数分析,我们知道spi_master的transfer函数其实是spi_bitbang_transfer,该函数不执行数据的具体发送,仅仅是将message消息添加到spi_bitbang的队列中:
-
-
-
-
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
-
{
-
struct spi_bitbang *bitbang;
-
unsigned long flags;
-
int status = 0;
-
-
m->actual_length = 0;
-
m->status = -EINPROGRESS;
-
-
-
bitbang = spi_master_get_devdata(spi->master);
-
-
spin_lock_irqsave(&bitbang->lock, flags);
-
if (!spi->max_speed_hz)
-
status = -ENETDOWN;
-
else {
-
list_add_tail(&m->queue, &bitbang->queue);
-
queue_work(bitbang->workqueue, &bitbang->work);
-
}
-
spin_unlock_irqrestore(&bitbang->lock, flags);
-
-
return status;
-
}
/**
* spi_bitbang_transfer - default submit to transfer queue
*/
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
struct spi_bitbang *bitbang;
unsigned long flags;
int status = 0;
m->actual_length = 0;
m->status = -EINPROGRESS;
//得到spi_bitbang,它负责具体的数据发送,这里有个小技巧,在控制器驱动中我们知道spi_master->dev->p->driver_data恰好是struct s3c24xx_spi,而struct spi_bitbang恰好又是struct s3c24xx_spi的第一个成员,故此处可以得到struct spi_bitbang
bitbang = spi_master_get_devdata(spi->master);
spin_lock_irqsave(&bitbang->lock, flags);
if (!spi->max_speed_hz)
status = -ENETDOWN;
else {
list_add_tail(&m->queue, &bitbang->queue); //将message添加到bitbang的消息列表
queue_work(bitbang->workqueue, &bitbang->work); //添加工作结构体到工作队列
}
spin_unlock_irqrestore(&bitbang->lock, flags);
return status;
}
通过前面控制器驱动中spi_bitbang_start函数分析,structspi_bitbang中的工作函数是bitbang_work,我们来看其实现:
-
static void bitbang_work(struct work_struct *work)
-
{
-
struct spi_bitbang *bitbang = container_of(work, struct spi_bitbang, work);
-
unsigned long flags;
-
int do_setup = -1;
-
int (*setup_transfer)(struct spi_device *,
-
struct spi_transfer *);
-
-
setup_transfer = bitbang->setup_transfer;
-
-
spin_lock_irqsave(&bitbang->lock, flags);
-
bitbang->busy = 1;
-
while (!list_empty(&bitbang->queue)) {
-
struct spi_message *m;
-
struct spi_device *spi;
-
unsigned nsecs;
-
struct spi_transfer *t = NULL;
-
unsigned tmp;
-
unsigned cs_change;
-
int status;
-
-
m = container_of(bitbang->queue.next, struct spi_message,
-
queue);
-
list_del_init(&m->queue);
-
spin_unlock_irqrestore(&bitbang->lock, flags);
-
-
-
-
-
-
nsecs = 100;
-
-
spi = m->spi;
-
tmp = 0;
-
cs_change = 1;
-
status = 0;
-
-
-
list_for_each_entry (t, &m->transfers, transfer_list) {
-
-
-
if (t->speed_hz || t->bits_per_word)
-
do_setup = 1;
-
-
-
if (do_setup != 0) {
-
if (!setup_transfer) {
-
status = -ENOPROTOOPT;
-
break;
-
}
-
status = setup_transfer(spi, t);
-
if (status < 0)
-
break;
-
}
-
-
-
-
-
-
-
-
if (cs_change) {
-
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
-
ndelay(nsecs);
-
}
-
cs_change = t->cs_change;
-
if (!t->tx_buf && !t->rx_buf && t->len) {
-
status = -EINVAL;
-
break;
-
}
-
-
-
-
-
-
if (t->len) {
-
-
-
-
if (!m->is_dma_mapped)
-
t->rx_dma = t->tx_dma = 0;
-
status = bitbang->txrx_bufs(spi, t);
-
}
-
if (status > 0)
-
m->actual_length += status;
-
if (status != t->len) {
-
-
if (status >= 0)
-
status = -EREMOTEIO;
-
break;
-
}
-
status = 0;
-
-
-
if (t->delay_usecs)
-
udelay(t->delay_usecs);
-
-
if (!cs_change)
-
continue;
-
if (t->transfer_list.next == &m->transfers)
-
break;
-
-
-
-
-
ndelay(nsecs);
-
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
-
ndelay(nsecs);
-
}
-
-
m->status = status;
-
m->complete(m->context);
-
-
-
if (do_setup == 1)
-
setup_transfer(spi, NULL);
-
do_setup = 0;
-
-
-
-
-
-
if (!(status == 0 && cs_change)) {
-
ndelay(nsecs);
-
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
-
ndelay(nsecs);
-
}
-
-
spin_lock_irqsave(&bitbang->lock, flags);
-
}
-
bitbang->busy = 0;
-
spin_unlock_irqrestore(&bitbang->lock, flags);
-
}
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang = container_of(work, struct spi_bitbang, work);
unsigned long flags;
int do_setup = -1;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
setup_transfer = bitbang->setup_transfer; // s3c24xx_spi_setupxfer,在控制器驱动中赋值
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1; //置位忙标志
while (!list_empty(&bitbang->queue)) { //读取bitbang链表中的message进行发送
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
unsigned tmp;
unsigned cs_change;
int status;
m = container_of(bitbang->queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
//逐个取出消息中的每个spi_transfer,每个message通过链表可以包括多个spi_transfer
list_for_each_entry (t, &m->transfers, transfer_list) {
/* override speed or wordsize? */
if (t->speed_hz || t->bits_per_word) //每个spi_tranfer的发送接收频率完全可能不同
do_setup = 1;
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
status = setup_transfer(spi, t); //根据spi_tranfer要求更新控制器预分频寄存器状态以改变通讯频率,见下面分析
if (status < 0)
break;
}
/* set up default clock polarity, and activate chip;
* this implicitly updates clock and spi modes as
* previously recorded for this device via setup().
* (and also deselects any other chip that might be
* selected ...)
*/
if (cs_change) {
bitbang->chipselect(spi, BITBANG_CS_ACTIVE); // 激活CS信号,通过s3c24xx_spi_chipsel函数实现,见下面分析
ndelay(nsecs);
}
cs_change = t->cs_change; //发送完当前spi_tranfer之后,需要改变CS引脚状态?
if (!t->tx_buf && !t->rx_buf && t->len) {
status = -EINVAL;
break;
}
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
if (t->len) {
/* REVISIT dma API still needs a designated
* DMA_ADDR_INVALID; ~0 might be better.
*/
if (!m->is_dma_mapped)
t->rx_dma = t->tx_dma = 0;
status = bitbang->txrx_bufs(spi, t); // s3c24xx_spi_txrx函数,在控制器probe函数中初始化,见下面分析
}
if (status > 0)
m->actual_length += status; //数据统计
if (status != t->len) {
/* always report some kind of error */
if (status >= 0)
status = -EREMOTEIO;
break;
}
status = 0;
/* protocol tweaks before next transfer */
if (t->delay_usecs)
udelay(t->delay_usecs);
if (!cs_change)
continue;
if (t->transfer_list.next == &m->transfers) //该message中所有transfers传送完毕?
break;
/* sometimes a short mid-message deselect of the chip
* may be needed to terminate a mode or command
*/
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE); //取消片选
ndelay(nsecs);
}
m->status = status;
m->complete(m->context); //消息完成函数,通知spidev_sync函数消息完成
/* restore speed and wordsize if it was overridden */
if (do_setup == 1) //恢复到spi_device 中对传输速率的设置
setup_transfer(spi, NULL); //NULL代表以spi_device而不是spi_transfer的设置来设置sppre寄存器
do_setup = 0;
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
*/
if (!(status == 0 && cs_change)) {
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE); //正常情况取消当前设备片选
ndelay(nsecs);
}
spin_lock_irqsave(&bitbang->lock, flags);
}
bitbang->busy = 0;
spin_unlock_irqrestore(&bitbang->lock, flags);
}
跟踪s3c24xx_spi_setupxfer函数:
-
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
-
struct spi_transfer *t)
-
{
-
struct s3c24xx_spi_devstate *cs = spi->controller_state;
-
struct s3c24xx_spi *hw = to_hw(spi);
-
int ret;
-
-
ret = s3c24xx_spi_update_state(spi, t);
-
if (!ret)
-
writeb(cs->sppre, hw->regs + S3C2410_SPPRE);
-
-
return ret;
-
}
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state;
struct s3c24xx_spi *hw = to_hw(spi);
int ret;
ret = s3c24xx_spi_update_state(spi, t); //在控制器驱动程序中已经讲述
if (!ret)
writeb(cs->sppre, hw->regs + S3C2410_SPPRE); //设置SPPRE硬件寄存器改变数据速率
return ret;
}
跟踪s3c24xx_spi_chipsel函数:
-
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
-
{
-
struct s3c24xx_spi_devstate *cs = spi->controller_state;
-
struct s3c24xx_spi *hw = to_hw(spi);
-
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
-
-
-
-
switch (value) {
-
case BITBANG_CS_INACTIVE:
-
hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
-
writeb(cs->spcon, hw->regs + S3C2410_SPCON);
-
break;
-
-
case BITBANG_CS_ACTIVE:
-
writeb(cs->spcon | S3C2410_SPCON_ENSCK,
-
hw->regs + S3C2410_SPCON);
-
hw->set_cs(hw->pdata, spi->chip_select, cspol);
-
break;
-
}
-
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
struct s3c24xx_spi_devstate *cs = spi->controller_state; //读取当前控制器状态
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
/* change the chipselect state and the state of the spi engine clock */
switch (value) {
case BITBANG_CS_INACTIVE:
hw->set_cs(hw->pdata, spi->chip_select, cspol^1); //在BSP中注册或者s3c24xx_spi_gpiocs,见s3c24xx_spi_probe
writeb(cs->spcon, hw->regs + S3C2410_SPCON);
break;
case BITBANG_CS_ACTIVE:
writeb(cs->spcon | S3C2410_SPCON_ENSCK,
hw->regs + S3C2410_SPCON);
hw->set_cs(hw->pdata, spi->chip_select, cspol);
break;
}
}
此处,消息的发送是基于中断模式进行的,跟踪s3c24xx_spi_txrx函数:
-
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
-
{
-
struct s3c24xx_spi *hw = to_hw(spi);
-
-
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
-
t->tx_buf, t->rx_buf, t->len);
-
-
hw->tx = t->tx_buf;
-
hw->rx = t->rx_buf;
-
hw->len = t->len;
-
hw->count = 0;
-
-
init_completion(&hw->done);
-
-
-
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
-
-
wait_for_completion(&hw->done);
-
-
return hw->count;
-
}
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
t->tx_buf, t->rx_buf, t->len);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
init_completion(&hw->done);
/* send the first byte */
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); //将发送数据写入寄存器,如果是读数据,则发送0,具体解释可以参考spi_transfer结构体的英语解释
wait_for_completion(&hw->done); //等待结束
return hw->count;
}
当数据发送完成后,触发中断,我们跟踪控制器驱动中probe函数中注册的中断处理函数:
-
static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
-
{
-
struct s3c24xx_spi *hw = dev;
-
unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
-
unsigned int count = hw->count;
-
-
if (spsta & S3C2410_SPSTA_DCOL) {
-
dev_dbg(hw->dev, "data-collision\n");
-
complete(&hw->done);
-
goto irq_done;
-
}
-
-
if (!(spsta & S3C2410_SPSTA_READY)) {
-
dev_dbg(hw->dev, "spi not ready for tx?\n");
-
complete(&hw->done);
-
goto irq_done;
-
}
-
-
hw->count++;
-
-
if (hw->rx)
-
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
-
-
count++;
-
-
if (count < hw->len)
-
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
-
else
-
complete(&hw->done);
-
-
irq_done:
-
return IRQ_HANDLED;
-
}
static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{
struct s3c24xx_spi *hw = dev;
unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
unsigned int count = hw->count;
if (spsta & S3C2410_SPSTA_DCOL) { //判断有无错误发生
dev_dbg(hw->dev, "data-collision\n");
complete(&hw->done);
goto irq_done;
}
if (!(spsta & S3C2410_SPSTA_READY)) {
dev_dbg(hw->dev, "spi not ready for tx?\n");
complete(&hw->done);
goto irq_done;
}
hw->count++;
if (hw->rx)
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); //读取数据
count++;
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done); //通知完成数据读写
irq_done:
return IRQ_HANDLED;
}
至此,对一个SPI设备的操作读操作完成,写操作与此类似,读者可以自行分析!
接下来,我们分析ioctl函数,有了前面的基础,分析ioctl就非常简单啦,我们在此只把一些要点说明一下,不做详细讲解,其实前面都已经讲过啦:
-
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;
-
-
-
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
-
return -ENOTTY;
-
-
-
-
-
-
if (_IOC_DIR(cmd) & _IOC_READ)
-
err = !access_ok(VERIFY_WRITE,
-
(void __user *)arg, _IOC_SIZE(cmd));
-
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
-
err = !access_ok(VERIFY_READ,
-
(void __user *)arg, _IOC_SIZE(cmd));
-
if (err)
-
return -EFAULT;
-
-
-
-
-
spidev = filp->private_data;
-
spin_lock_irq(&spidev->spi_lock);
-
spi = spi_dev_get(spidev->spi);
-
spin_unlock_irq(&spidev->spi_lock);
-
-
if (spi == NULL)
-
return -ESHUTDOWN;
-
mutex_lock(&spidev->buf_lock);
-
-
switch (cmd) {
-
-
case SPI_IOC_RD_MODE:
-
retval = __put_user(spi->mode & SPI_MODE_MASK,
-
(__u8 __user *)arg);
-
break;
-
case SPI_IOC_RD_LSB_FIRST:
-
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
-
(__u8 __user *)arg);
-
break;
-
case SPI_IOC_RD_BITS_PER_WORD:
-
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
-
break;
-
case SPI_IOC_RD_MAX_SPEED_HZ:
-
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
-
break;
-
-
-
case SPI_IOC_WR_MODE:
-
retval = __get_user(tmp, (u8 __user *)arg);
-
if (retval == 0) {
-
u8 save = spi->mode;
-
-
if (tmp & ~SPI_MODE_MASK) {
-
retval = -EINVAL;
-
break;
-
}
-
-
tmp |= spi->mode & ~SPI_MODE_MASK;
-
spi->mode = (u8)tmp;
-
retval = spi_setup(spi);
-
if (retval < 0)
-
spi->mode = save;
-
else
-
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
-
}
-
break;
-
case SPI_IOC_WR_LSB_FIRST:
-
retval = __get_user(tmp, (__u8 __user *)arg);
-
if (retval == 0) {
-
u8 save = spi->mode;
-
-
if (tmp)
-
spi->mode |= SPI_LSB_FIRST;
-
else
-
spi->mode &= ~SPI_LSB_FIRST;
-
retval = spi_setup(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:
-
retval = __get_user(tmp, (__u8 __user *)arg);
-
if (retval == 0) {
-
u8 save = spi->bits_per_word;
-
-
spi->bits_per_word = tmp;
-
retval = spi_setup(spi);
-
if (retval < 0)
-
spi->bits_per_word = save;
-
else
-
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
-
}
-
break;
-
case SPI_IOC_WR_MAX_SPEED_HZ:
-
retval = __get_user(tmp, (__u32 __user *)arg);
-
if (retval == 0) {
-
u32 save = spi->max_speed_hz;
-
-
spi->max_speed_hz = tmp;
-
retval = spi_setup(spi);
-
if (retval < 0)
-
spi->max_speed_hz = save;
-
else
-
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
-
}
-
break;
-
-
default:
-
-
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) {
-
retval = -EINVAL;
-
break;
-
}
-
n_ioc = tmp / sizeof(struct spi_ioc_transfer);
-
if (n_ioc == 0)
-
break;
-
-
-
ioc = kmalloc(tmp, GFP_KERNEL);
-
if (!ioc) {
-
retval = -ENOMEM;
-
break;
-
}
-
if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
-
kfree(ioc);
-
retval = -EFAULT;
-
break;
-
}
-
-
-
retval = spidev_message(spidev, ioc, n_ioc);
-
kfree(ioc);
-
break;
-
}
-
-
mutex_unlock(&spidev->buf_lock);
-
spi_dev_put(spi);
-
return retval;
-
}
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)
err = !access_ok(VERIFY_WRITE, //对访问用户空间进行合法性校验
(void __user *)arg, _IOC_SIZE(cmd));
if (err == 0 && _IOC_DIR(cmd) & _IOC_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;
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
return -ESHUTDOWN;
mutex_lock(&spidev->buf_lock);
switch (cmd) {
/* read requests */
case SPI_IOC_RD_MODE: //读取信息
retval = __put_user(spi->mode & SPI_MODE_MASK,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST:
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD:
retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ:
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
/* write requests */
case SPI_IOC_WR_MODE:
retval = __get_user(tmp, (u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode;
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u8)tmp;
retval = spi_setup(spi); //调用s3c24xx_spi_setup,前面已经分析过
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST:
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->mode;
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(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:
retval = __get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u8 save = spi->bits_per_word;
spi->bits_per_word = tmp;
retval = spi_setup(spi);
if (retval < 0)
spi->bits_per_word = save;
else
dev_dbg(&spi->dev, "%d bits per word\n", tmp);
}
break;
case SPI_IOC_WR_MAX_SPEED_HZ:
retval = __get_user(tmp, (__u32 __user *)arg);
if (retval == 0) {
u32 save = spi->max_speed_hz;
spi->max_speed_hz = tmp;
retval = spi_setup(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 */
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); //无数据传送?
if (n_ioc == 0)
break;
/* copy into scratch area */
ioc = kmalloc(tmp, GFP_KERNEL);
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);
break;
}
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;
}
追踪spidev_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);
-
if (k_xfers == NULL)
-
return -ENOMEM;
-
-
-
-
-
-
-
buf = spidev->buffer;
-
total = 0;
-
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;
-
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;
-
if (copy_from_user(buf, (const u8 __user *)
-
(uintptr_t) u_tmp->tx_buf,
-
u_tmp->len))
-
goto done;
-
}
-
buf += k_tmp->len;
-
-
-
k_tmp->cs_change = !!u_tmp->cs_change;
-
k_tmp->bits_per_word = u_tmp->bits_per_word;
-
k_tmp->delay_usecs = u_tmp->delay_usecs;
-
k_tmp->speed_hz = u_tmp->speed_hz;
-
#ifdef VERBOSE
-
dev_dbg(&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 ? : spi->bits_per_word,
-
u_tmp->delay_usecs,
-
u_tmp->speed_hz ? : spi->max_speed_hz);
-
#endif
-
spi_message_add_tail(k_tmp, &msg);
-
}
-
-
status = spidev_sync(spidev, &msg);
-
if (status < 0)
-
goto done;
-
-
-
buf = spidev->buffer;
-
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
-
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;
-
}
-
status = total;
-
-
done:
-
kfree(k_xfers);
-
return status;
-
}
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); //对msg进行初始化
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
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.
*/
//主要目的是构建一个spi_message,其实spi_ioc_transfer是spi_transfer的用户空间版本,或者说是映射,具体见英语注释
buf = spidev->buffer;
total = 0;
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) { //在open中给spidev_data申请了bufsiz大小的buffer,不能超过此值
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) { //接收数据?
k_tmp->rx_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;
if (copy_from_user(buf, (const u8 __user *)
(uintptr_t) u_tmp->tx_buf,
u_tmp->len))
goto done;
}
buf += k_tmp->len;
//初始化
k_tmp->cs_change = !!u_tmp->cs_change; //当前transfer结束影响CS引脚?
k_tmp->bits_per_word = u_tmp->bits_per_word;
k_tmp->delay_usecs = u_tmp->delay_usecs; //当前spi_transfer结束后延迟时间
k_tmp->speed_hz = u_tmp->speed_hz;//传输速度,可以overwrite对应spi_device的值
#ifdef VERBOSE
dev_dbg(&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 ? : spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg); //将spi_transfer通过它的transfer_list字段挂到spi_message的transfer队列上
}
status = spidev_sync(spidev, &msg); //前面已经分析过,此处飘过
if (status < 0)
goto done;
/* copy any rx data out of bounce buffer */
buf = spidev->buffer;
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
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;
}
status = total;
done:
kfree(k_xfers);
return status;
}
总结:
设备驱动完成的主要工作就是将spi_driver注册到spi_bus_type,经过前面控制器驱动程序分析我们知道,当控制器驱动注册时会扫描BSP中注册的设备链表将spi_device注册到sip_bus_type,这样,当spi_driver注册时会扫描spi_bus_type上的spi_device,如果其上的spi_device和spi_driver能通过spi_bus_type的match函数进行匹配,则会调用spi_driver的probe函数进行资源分配。
Spi子系统的主要目的是通过spi_bus_type实现spi_device和spi_driver的匹配,具体的用户接口还是通过file_operations来实现,在file_operations的接口函数中,通过次设备号找到对应的spi设备,调用其接口实现数据发送。