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 = { //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) //其入口参数是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, /* 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); //根据设备号找到对应的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; /* 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 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) //没有对应的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; /* 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的队列中:
/** * 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; // 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); //设置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; /* 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); /* 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; }
至此,对一个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; /* 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); //对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设备,调用其接口实现数据发送。