LINUX SPI设备驱动模型分析之四 SPI 通信接口(spi_sync、spi_async)分析

      上两篇文章完成了spi总线、设备、驱动、master的分析,下面我们分析下spi模块提供的通信方法,通过该通信方法,即可完成cpu与具体spi设备之间的通信(借助spi controller)。

      其实,spi_sync、spi_async的实现也不是太复杂,但是由于在新版内核中,针对spi_sync,spi核心提供了基于worker线程的处理方式,基于该方式则所有spi master均不需要提供spi_transfer接口,而使用spi核心定义的spi_queued_transfer即可。而这两个接口也是与具体的spi master相关联的。

 

spi_sync、spi_async

       Spi 核心提供两种通信方法spi_sync、spi_async(同步、异步),这两种方法最终均是通过调用__spi_async接口实现数据的通信。

这两个接口的处理流程图如下所示:

  1. 对于同步通信,设置本次传输的spi_message类型变量的complete函数指针指向spi_complete,借助完成量机制,完成spi的同步通信操作(主要借助完成量的complete、wait_for_completion这两个接口,其实这两个接口也就是借助内核的等待队列,根据等待队列的sleep、wakeup实现完成量);
  2. 对于异步通信,则不必执行上述1中的内容;
  3. 对本次通信的spi_message类型变量进行合法性检查,包括spi_message->transfer链表上所有需要输出的spi_transfer类型的数据进行合法性检测、传输速率等信息的合法性检测。
  4. 调用spi控制器的transfer接口,进行数据传输操作。

 

spi核心提供的spi queue处理方式

针对spi核心提供的spi_sync、spi_async接口,基本上完成上面的框架接口,然后每一个spi_master则通过实现spi_master->transfter接口,即实现了数据通信。但在新版的内核中,spi核心模块又提供了kthread_worker机制,通过为每一个spi控制器创建worker线程,由该worker线程处理该spi总线上所有需要处理的通信数据。

Spi 控制器的worker kthread的创建

  1. 当调用spi_register_master接口注册一个spi控制器,若该控制器支持队列模式,则调用spi_master_initialize_queue接口创建一个worker kthread,同时将master->kworker绑定到该线程生;
  2. 初始化master->pump_messages,用于注册到master->kworker上。

Spi 控制器queue机制的transfer接口

针对提供queue机制的spi控制器,其transfer接口均指向spi_queued_transfer,该接口的实现流程如下,主要实现功能如下:

  1. 将该spi_message类型的变量加入到spi->master的queue队列上;
  2. 将master->pump_messages加入到master->kworker上。

当spi_queued_transfer完成将master->pump_messages加入到master->kworker上后,worker kthread线程的处理函数kthread_worker_fn即会从master->kworker上取出一个kthread_work,并调用该变量的func接口,对于spi控制器而言,即为spi_pump_messages接口。该接口的实现功能如下:

  1. 然后调用该master->prepare_transfer_hardware、transfer_one_message接口,完成针对该spi_message类型变量的数据传输,这就由具体控制器的接口来实现相应功能。

 

 

       以上便是spi核心提供的基于队列模式(借助LINUX的worker kthread机制)相关的spi核心层提供的接口分析。

此篇文章简要说明了spi_sync、spi_async的实现方式,以及与spi master之间的调用关联。

  • 7
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个使用 epoll 和 SPI 通信的字符设备驱动代码示例: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/spi/spi.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/slab.h> #define DRIVER_NAME "my_spi_driver" #define DEVICE_NAME "my_spi_device" #define MAX_BUF_LEN 256 static struct spi_device *spi_dev; static struct cdev my_cdev; static dev_t my_dev; static struct class *my_class; static struct device *my_device; static char spi_buf[MAX_BUF_LEN]; static int buf_len; static int irq_gpio = -1; static volatile int irq_flag = 0; static struct fasync_struct *async_queue; static DECLARE_WAIT_QUEUE_HEAD(irq_wait_queue); static irqreturn_t my_spi_irq_handler(int irq, void *dev_id) { irq_flag = 1; wake_up_interruptible(&irq_wait_queue); kill_fasync(&async_queue, SIGIO, POLL_IN); return IRQ_HANDLED; } static int my_spi_open(struct inode *inode, struct file *filp) { int ret = 0; printk(KERN_INFO "my_spi: opening device\n"); ret = spi_sync(spi_dev, NULL, 0); if (ret < 0) { printk(KERN_ERR "my_spi: SPI sync failed\n"); return ret; } return 0; } static int my_spi_release(struct inode *inode, struct file *filp) { printk(KERN_INFO "my_spi: closing device\n"); return 0; } static ssize_t my_spi_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; if (count > buf_len) count = buf_len; ret = copy_to_user(buf, spi_buf, count); if (ret < 0) { printk(KERN_ERR "my_spi: copy_to_user failed\n"); return ret; } return count; } static ssize_t my_spi_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; if (count > MAX_BUF_LEN) count = MAX_BUF_LEN; ret = copy_from_user(spi_buf, buf, count); if (ret < 0) { printk(KERN_ERR "my_spi: copy_from_user failed\n"); return ret; } buf_len = count; return count; } static unsigned int my_spi_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; poll_wait(filp, &irq_wait_queue, wait); if (irq_flag != 0) mask |= POLLIN | POLLRDNORM; return mask; } static int my_spi_fasync(int fd, struct file *filp, int mode) { return fasync_helper(fd, filp, mode, &async_queue); } static struct file_operations my_spi_fops = { .owner = THIS_MODULE, .open = my_spi_open, .release = my_spi_release, .read = my_spi_read, .write = my_spi_write, .poll = my_spi_poll, .fasync = my_spi_fasync, }; static int my_spi_probe(struct spi_device *spi) { int ret = 0; printk(KERN_INFO "my_spi: probing device\n"); spi_dev = spi; ret = gpio_request(spi->irq, "my_spi_irq"); if (ret < 0) { printk(KERN_ERR "my_spi: request irq gpio failed\n"); return ret; } irq_gpio = spi->irq; ret = gpio_direction_input(irq_gpio); if (ret < 0) { printk(KERN_ERR "my_spi: set irq gpio direction failed\n"); goto fail; } ret = request_irq(gpio_to_irq(irq_gpio), my_spi_irq_handler, IRQF_TRIGGER_RISING, "my_spi_irq_handler", NULL); if (ret < 0) { printk(KERN_ERR "my_spi: request irq failed\n"); goto fail; } ret = alloc_chrdev_region(&my_dev, 0, 1, DRIVER_NAME); if (ret < 0) { printk(KERN_ERR "my_spi: alloc_chrdev_region failed\n"); goto fail; } cdev_init(&my_cdev, &my_spi_fops); my_cdev.owner = THIS_MODULE; ret = cdev_add(&my_cdev, my_dev, 1); if (ret < 0) { printk(KERN_ERR "my_spi: cdev_add failed\n"); goto fail; } my_class = class_create(THIS_MODULE, DRIVER_NAME); if (IS_ERR(my_class)) { printk(KERN_ERR "my_spi: class_create failed\n"); ret = PTR_ERR(my_class); goto fail; } my_device = device_create(my_class, NULL, my_dev, NULL, DEVICE_NAME); if (IS_ERR(my_device)) { printk(KERN_ERR "my_spi: device_create failed\n"); ret = PTR_ERR(my_device); goto fail; } return 0; fail: if (irq_gpio != -1) gpio_free(spi->irq); return ret; } static int my_spi_remove(struct spi_device *spi) { printk(KERN_INFO "my_spi: removing device\n"); device_destroy(my_class, my_dev); class_destroy(my_class); cdev_del(&my_cdev); unregister_chrdev_region(my_dev, 1); free_irq(gpio_to_irq(irq_gpio), NULL); gpio_free(irq_gpio); return 0; } static struct spi_driver my_spi_driver = { .driver = { .name = "my_spi_driver", .owner = THIS_MODULE, }, .probe = my_spi_probe, .remove = my_spi_remove, }; static int __init my_spi_init(void) { int ret = 0; printk(KERN_INFO "my_spi: initializing driver\n"); ret = spi_register_driver(&my_spi_driver); if (ret < 0) { printk(KERN_ERR "my_spi: spi_register_driver failed\n"); return ret; } return 0; } static void __exit my_spi_exit(void) { printk(KERN_INFO "my_spi: exiting driver\n"); spi_unregister_driver(&my_spi_driver); } module_init(my_spi_init); module_exit(my_spi_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("My SPI Driver"); ``` 该驱动程序使用 SPI 通信与外部设备通信,并使用字符设备接口提供用户空间访问。它使用 epoll 和 fasync 机制来实现非阻塞 I/O 和异步通知。它还使用了一个 GPIO 中断来实现异步通知。该驱动程序在初始化时注册了一个 SPI 设备驱动程序,当 SPI 总线上有新设备被探测到时,驱动程序的 probe 函数将被调用。在 probe 函数中,驱动程序会为该设备分配一个字符设备号,并创建一个字符设备文件,以便用户空间程序可以打开和读写该设备。当 SPI 设备从系统中移除时,驱动程序的 remove 函数将被调用,该函数将执行清理操作,以确保系统在设备被移除时不会出现问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值