一.spidev.c文件
看一个设备驱动的方法:
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
@@open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:
- spidev->users++; //spidev_data使用者计数++
- filp->private_data = spidev; //spidev_data放在文件的私有数据里
- nonseekable_open(inode, filp); //设置文件的打开模式(文件读写指针不会跟随读写操作移动)
@@read方法spidev_read
spidev = filp->private_data;=========>>status = spidev_sync_read(spidev, count);===========>>
spidev_sync(spidev, &m);==========>>status = spi_async(spidev->spi, message);===========>>
wait_for_completion(&done);========>>到了这一步是重点,在spi_async()方法中,使用以下语句将要做的事情加到workqueue中
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
此后所有的处理程序便转移到在之前初始化的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);
- /* 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;
- list_for_each_entry (t, &m->transfers, transfer_list) {
- /* override speed or wordsize? */
- if (t->speed_hz || t->bits_per_word)
- 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);
- 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);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- 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);
- }
- 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)
- 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);
- /* restore speed and wordsize if it was overridden */
- if (do_setup == 1)
- setup_transfer(spi, NULL);
- 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);
- }
结束处理所有任务后,见上面红色底纹部分解除wait_for_completion(&done);
最后missing = copy_to_user(buf, spidev->buffer, status);将数据发送到用户空间
@@write方法spidev_write
与上面open方式基本相同
@@ioctl方法spidev_ioctl
具体的详解见下面章节(三,四)
下面是spidev.c添加注释部分
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/ioctl.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/list.h>
- #include <linux/errno.h>
- #include <linux/mutex.h>
- #include <linux/slab.h>
- #include <linux/spi/spi.h>
- #include <linux/spi/spidev.h>
- #include <asm/uaccess.h>
- #define SPIDEV_MAJOR 153 //spidev主设备号
- #define N_SPI_MINORS 32 /* ... up to 256 */
- static DECLARE_BITMAP(minors, N_SPI_MINORS); //声明次设备位图
- #define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)
- struct spidev_data {
- dev_t devt; //设备号
- spinlock_t spi_lock; //自旋锁
- struct spi_device *spi; //spi设备结构体
- struct list_head device_entry;
- struct mutex buf_lock; //互斥锁
- unsigned users; //使用者计数
- u8 *buffer; //缓冲区
- };
- static LIST_HEAD(device_list); //声明spi设备链表
- static DEFINE_MUTEX(device_list_lock); //定义互斥锁
- static unsigned bufsiz = 4096; //最大传输缓冲区大小
- module_param(bufsiz, uint, S_IRUGO);
- MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
- static void spidev_complete(void *arg)
- {
- complete(arg); //调用complete
- }
- static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
- message->complete = spidev_complete; //设置spi消息的complete方法 回调函数
- message->context = &done;
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL) //判断是否有指定对应的spi设备
- status = -ESHUTDOWN;
- else
- status = spi_async(spidev->spi, message); //spi异步同步
- spin_unlock_irq(&spidev->spi_lock);
- if (status == 0) {
- wait_for_completion(&done); //等待传输完成
- status = message->status; //获取spi消息传输事务状态
- if (status == 0)
- status = message->actual_length; //status等于传输的实际长度
- }
- return status; //返回实际传输长度
- }
- static inli