allein.cao原创作品,转载请注明出处:
内核版本:2.6.32.2
硬件:s3c2440
设备驱动是在core之上的模块,向上给应用程序提供file_operations接口,应用程序可以通过设备节点访问驱动程序,向下通过core向控制器模块发送数据,控制器模块将数据发送到物理总线上。
spidev.c是一个典型的设备驱动程序,前面提到在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,目的是最大程度保证代码的可移植性,我们以应用程序调用为主线,详细分析spi驱动的数据流流向。
首先来看
-
staticstruct spi_driver spidev_spi = {
-
.driver = {
-
.name = "spidev",
-
.owner = this_module,
-
},
-
.probe = spidev_probe,
-
.remove = __devexit_p(spidev_remove),
-
};
-
staticint __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匹配完毕:
-
staticint 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中的成员函数:
-
staticconststruct 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设备:
-
staticint 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,我们来看其实现:
-
staticvoid 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函数:
-
staticint 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函数:
-
staticvoid 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函数:
-
staticint 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就非常简单啦,我们在此只把一些要点说明一下,不做详细讲解,其实前面都已经讲过啦:
-
staticlong
-
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 x\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 x\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函数:
-
staticint 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�its %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�its %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设备,调用其接口实现数据发送。