本文唯一地址:http://blog.csdn.net/dearsq/article/details/51839083
欢迎转载,转载请注明,谢谢~~

之前有归纳过传统 board-info 形式下的 spi 驱动模型:Linux 内核中 SPI 设备驱动模型(Platform设备驱动方式)。

但是这里代码的环境是 Android5.1。
所以我们先来分析一下 DTS。

DTS

根据硬件工程师给出的信息,这颗 GSensor 接到 Spi0 上,我们可以看一下 DTS 中的信息:

#spi0
spi0: spi@70a00000{compatible = "sprd, sprd-spi";interrupts = <0 7 0x0>reg = <0x70a00000 0x1000>clock-names = "clk_spi0";#address-cells = <1>;#size-cells = <0>;
};
# spi1...

SPI 部分驱动

这种 SPI 类的 Sensor 一般会将驱动分为两个部分,一部分是 CHIP_spi.c 一部分是 CHIP.c。

CHIP_spi.c

CHIP_spi_driver

在 CHIP_SPI.c 中完成的任务是 CHIP_spi_read 及 CHIP_spi_write 等回调函数的实现。
SPI 协议驱动有些类似平台设备驱动:

static struct spi_driver ad714x_spi_driver = {.driver = {.name   = "ad714x_captouch",.owner  = THIS_MODULE,.pm = &ad714x_spi_pm,},.probe      = ad714x_spi_probe,.remove     = ad714x_spi_remove,
};

module_spi_driver() 宏

内核将调用 module_spi_driver() 这个宏来注册和卸载 spi 设备,这个 module_spi_driver 是专门针对于 spi 架构定义的。
在 include/linux/spi/spi.h 中,我们可以看到:

281 /**
282  * module_spi_driver() - Helper macro for registering a SPI driver
283  * @__spi_driver: spi_driver struct
284  *
285  * Helper macro for SPI drivers which do not do anything special in module
286  * init/exit. This eliminates a lot of boilerplate. Each module may only
287  * use this macro once, and calling it replaces module_init() and module_exit()
288  */
289 #define module_spi_driver(__spi_driver) \
290         module_driver(__spi_driver, spi_register_driver, \
291                         spi_unregister_driver)

可以还可以往下去追看 module_driver() 这个宏 ,它将 spi_register/unregister_driver() 与 module_init 和 module_exit 封装了起来。所以说实际上 module_spi_driver() 和 module_init/exit 几乎是没有区别的。
之所以直接将其封装的原因是因为这个 SPI 设备本身是不可插拔的,也就不需要 init 和 exit 的过程,系统上电就直接注册了。

CHIP_spi_probe()

static int ad714x_spi_probe(struct spi_device *spi)
{// struct CHIP_chip 被定义在 .h 文件中,其中封装了 CHIP_platform_data、CHIP_driver_data、device 等结构体,以及一些回调函数// 我们可以将其理解为 spi_device 的封装struct ad714x_chip *chip; int err;spi->bits_per_word = 8; //定义每个字传输的 bit 数err = spi_setup(spi); // 配置 SPI 的通信相关的信息,实现在 driver/spi/spi.c 中if (err < 0)return err;// 将我们实现的 CHIP_spi_read 和 CHIP_spi_write 传到 CHIP.c 文件中的主 probe 中// 在 主 probe 中调用 read 和 write 来进行和从设备的通信,主 probe() 我们在下一段分析chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,ad714x_spi_read, ad714x_spi_write);if (IS_ERR(chip))return PTR_ERR(chip);spi_set_drvdata(spi, chip); //=> dev_set_drvdata(&spi->dev,chip)//=> spi->dev->driver_data = chipreturn 0;
}

抽象出来就是

static int __devinit CHIP_spi_probe(struct spi_device *spi){struct CHIP                   *chip;struct CHIP_platform_data     *pdata; //也可以将这个封装到上面的 CHIP 里面/* assuming the driver req一个"spi_masteruires board-specific data: */pdata = &spi->dev.platform_data;if (!pdata)return -ENODEV;/* get memory for driver\'s per-chip state */chip = kzalloc(sizeof *chip, GFP_KERNEL);if (!chip)return -ENOMEM;spi_set_drvdata(spi, chip);... etcreturn 0;中}

其中的主 probe 放到后面去分析。
其中的 spi_setup() 中可以修改 spi_device 特征,如传输模式、字长或时钟速率。

CHIP_spi_read / CHIP_spi_write

先介绍一下 struct spi_message

789 struct spi_message {
790         struct list_head        transfers;
792         struct spi_device       *spi;...
814         /* for optional use by whatever driver currently owns the
815          * spi_message ...  between calls to中 spi_async and then later
816          * complete(), that's the spi_master controller driver.
817          */
818         struct list_head        queue;
821         /* list of spi_res reources when the spi message is processed */
822         struct list_head        resources;##### spi_message
823 };

一个 message 是一次数据交换的原子请求, spi_message 是多个 spi_transfer 结构组成,这些 spi_transfer 通过一个链表 transfers 组织在一起。具体可以看这篇博文:SPI 数据传输的队列化。
spi_message 结构有一个链表头字段 transfers。
spi_transfer 结构包含一个链表头字段 transfer_list。
通过这两个链表头字段,所有属于这次 message 传输的 transfer 都会挂在 spi_messages.transfers 下。
我们通过 spi_message_add_tail 来向 spi_message 结构中添加一个 spi_transfer 结构。
然后调用 spi_async 同步 或者 spi_sync 异步来发起一个 message 传输请求,通常 spi_async 或 spi_sync 由将被封装在 read 和 write 中。

//read 有四个参数
//1. 之前封装了 device 的结构体
//2. 需要读取的寄存器
//3. 用来接受数据的 data
//4. 传输的字节数
static int ad714x_spi_read(struct ad714x_chip *chip,源码unsigned short reg, unsigned short *data, size_t len)
{struct spi_device *spi = to_spi_device(chip->dev);//利用 container_of 将封装的 dev 提取出来   struct spi_message message;struct spi_transfer xfer[2]; // SPI 控制器 和 协议驱动 之间的 I/O 接口int i;int error;spi_message_init(&message); // 先初始化 messagememset(xfer, 0, sizeof(xfer));chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX |AD714x_SPI_READ | reg);xfer[0].tx_buf = &chip->xfer_buf[0];xfer[0].len = sizeof(chip->xfer_buf[0]);spi_message_add_tail(&xfer[0], &message);xfer[1].rx_buf = &chip->xfer_buf[1];xfer[1].len = sizeof(chip->xfer_buf[1]) * len;spi_message_add_tail(&xfer[1], &message);error = spi_sync(spi, &message);//调用spi_sync 来发起 message 传输请求if (unlikely(error)) {dev_err(chip->dev, "SPI read error: %d\n", error);return error;}for (i = 0; i < len; i++)data[i] = be16_to_cpu(chip->xfer_buf[i + 1]);return 0;
}
// 三个参数
// 1. 封装了 device 的结构体
// 2. 要写入的寄存器
// 3. 要写入的数据
static int ad714x_spi_write(struct ad714x_chip *chip,unsigned short reg, unsigned short data)
{struct spi_device *spi = to_spi_device(chip->dev);int error;chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | reg);chip->xfer_buf[1] = cpu_to_be16(data);error = spi_write(spi, (u8 *)chip->xfer_buf,  2 * sizeof(*chip->xfer_buf));/* 这里的 spi_write 等价于struct spi_transfer t = {.tx_buf = chip->xfer_buf,.len      = 2*sizeof(*chip->xfer_buf),};struct spi_message  m;spi_message_init(&m);spi_message_add_tail(&t,&m);return spi_sync(spi, &m);*/if (unlikely(error)) {dev_err(chip->dev, "SPI write error: %d\n", error);return error;}return 0;
}

CHIP.c

之前我们在 CHIP_spi_probe() 中可以注意到,有如下代码

chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,ad714x_spi_read, ad714x_spi_write);

这里 CHIP_probe() 的定义在 CHIP.c
我们来读一下 CHIP_probe() 的代码

// 有 5 个参数
// 1. device
// 2. 总线类型
// 3. 中断号
// 4. 5. read 和 write 的实现函数(封装了 spi_async)
// 代码太长,我们直接看哪些地方调用了  read 和 write,其他的地方省略
struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,ad714x_read_t read, ad714x_write_t write)
{int i, alloc_idx;int error;struct input_dev *input[MAX_DEVICE_NUM];struct ad714x_platform_data *plat_data = dev->platform_data;struct ad714x_chip *ad714x;void *drv_mem;unsigned long irqflags;struct ad714x_button_drv *bt_drv;struct ad714x_slider_drv *sd_drv;struct ad714x_wheel_drv *wl_drv;struct ad714x_touchpad_drv *tp_drv;if (irq <= 0) {dev_err(dev, "IRQ not configured!\n");error = -EINVAL;goto err_out;}if (dev->platform_data == NULL) {dev_err(dev, "platform data for ad714x doesn't exist\n");error = -EINVAL;goto err_out;}ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) +sizeof(*sd_drv) * plat_data->slider_num +sizeof(*wl_drv) * plat_data->wheel_num +sizeof(*tp_drv) * plat_data->touchpad_num +sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL);if (!ad714x) {error = -ENOMEM;goto err_out;}ad714x->hw = plat_data;drv_mem = ad714x + 1;ad714x->sw = drv_mem;drv_mem += sizeof(*ad714x->sw);ad714x->sw->slider = sd_drv = drv_mem;drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num;ad714x->sw->wheel = wl_drv = drv_mem;drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num;ad714x->sw->touchpad = tp_drv = drv_mem;drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num;ad714x->sw->button = bt_drv = drv_mem;drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num;ad714x->read = read;ad714x->write = write;ad714x->irq = irq;ad714x->dev = dev;//读硬件寄存器中的 chip ID,判断具体是哪种 CapToucherror = ad714x_hw_detect(ad714x);   if (error)goto err_free_mem;/* initialize and request sw/hw resources */ad714x_hw_init(ad714x);mutex_init(&ad714x->mutex);/** Allocate and register AD714X input device*/alloc_idx = 0;/* a slider uses one input_dev instance */if (ad714x->hw->slider_num > 0) {struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;for (i = 0; i < ad714x->hw->slider_num; i++) {sd_drv[i].input = input[alloc_idx] = input_allocate_device();if (!input[alloc_idx]) {error = -ENOMEM;goto err_free_dev;}__set_bit(EV_ABS, input[alloc_idx]->evbit);__set_bit(EV_KEY, input[alloc_idx]->evbit);__set_bit(ABS_X, input[alloc_idx]->absbit);__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);input_set_abs_params(input[alloc_idx],ABS_X, 0, sd_plat->max_coord, 0, 0);input[alloc_idx]->id.bustype = bus_type;input[alloc_idx]->id.product = ad714x->product;input[alloc_idx]->id.version = ad714x->version;input[alloc_idx]->name = "ad714x_captouch_slider";input[alloc_idx]->dev.parent = dev;error = input_register_device(input[alloc_idx]);if (error)goto err_free_dev;alloc_idx++;}}/* a wheel uses one input_dev instance */if (ad714x->hw->wheel_num > 0) {struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;for (i = 0; i < ad714x->hw->wheel_num; i++) {wl_drv[i].input = input[alloc_idx] = input_allocate_device();if (!input[alloc_idx]) {error = -ENOMEM;goto err_free_dev;}__set_bit(EV_KEY, input[alloc_idx]->evbit);__set_bit(EV_ABS, input[alloc_idx]->evbit);__set_bit(ABS_WHEEL, input[alloc_idx]->absbit);__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);input_set_abs_params(input[alloc_idx],ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);input[alloc_idx]->id.bustype = bus_type;input[alloc_idx]->id.product = ad714x->product;input[alloc_idx]->id.version = ad714x->version;input[alloc_idx]->name = "ad714x_captouch_wheel";input[alloc_idx]->dev.parent = dev;error = input_register_device(input[alloc_idx]);if (error)goto err_free_dev;alloc_idx++;}}/* a touchpad uses one input_dev instance */if (ad714x->hw->touchpad_num > 0) {struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;for (i = 0; i < ad714x->hw->touchpad_num; i++) {tp_drv[i].input = input[alloc_idx] = input_allocate_device();if (!input[alloc_idx]) {error = -ENOMEM;goto err_free_dev;}__set_bit(EV_ABS, input[alloc_idx]->evbit);__set_bit(EV_KEY, input[alloc_idx]->evbit);__set_bit(ABS_X, input[alloc_idx]->absbit);__set_bit(ABS_Y, input[alloc_idx]->absbit);__set_bit(BTN_TOUCH, input[alloc_idx]->keybit);input_set_abs_params(input[alloc_idx],ABS_X, 0, tp_plat->x_max_coord, 0, 0);input_set_abs_params(input[alloc_idx],ABS_Y, 0, tp_plat->y_max_coord, 0, 0);input[alloc_idx]->id.bustype = bus_type;input[alloc_idx]->id.product = ad714x->product;input[alloc_idx]->id.version = ad714x->version;input[alloc_idx]->name = "ad714x_captouch_pad";input[alloc_idx]->dev.parent = dev;error = input_register_device(input[alloc_idx]);if (error)goto err_free_dev;alloc_idx++;}}/* all buttons use one input node */if (ad714x->hw->button_num > 0) {struct ad714x_button_plat *bt_plat = ad714x->hw->button;input[alloc_idx] = input_allocate_device();if (!input[alloc_idx]) {error = -ENOMEM;goto err_free_dev;}__set_bit(EV_KEY, input[alloc_idx]->evbit);for (i = 0; i < ad714x->hw->button_num; i++) {bt_drv[i].input = input[alloc_idx];__set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit);}input[alloc_idx]->id.bustype = bus_type;input[alloc_idx]->id.product = ad714x->product;input[alloc_idx]->id.version = ad714x->version;input[alloc_idx]->name = "ad714x_captouch_button";input[alloc_idx]->dev.parent = dev;error = input_register_device(input[alloc_idx]);if (error)goto err_free_dev;alloc_idx++;}irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING;irqflags |= IRQF_ONESHOT;error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,irqflags, "ad714x_captouch", ad714x);if (error) {dev_err(dev, "can't allocate irq %d\n", ad714x->irq);goto err_unreg_dev;}return ad714x;err_free_dev:dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx);input_free_device(input[alloc_idx]);err_unreg_dev:while (--alloc_idx >= 0)input_unregister_device(input[alloc_idx]);err_free_mem:kfree(ad714x);err_out:return ERR_PTR(error);
}
EXPORT_SYMBOL(ad714x_probe);

参考:
1. 内核源码 /kernel/driver/spi/spi.c spi.h
2.这个没有纠结于代码实现,归纳了Linux内核对 SPI 的支持方法。还有抽象出来说如何实现 SPI 驱动: http://linux.it.net.cn/m/view.php?aid=18852
3.这个以 一个 eeprom 的代码来进行分析 SPI 总线 http://www.cnblogs.com/jason-lu/p/3165327.html

本文唯一地址:http://blog.csdn.net/dearsq/article/details/51839083
欢迎转载,转载请注明,谢谢~~

[Linux] SPI 设备驱动模型(以 Ad714x CapTouch 驱动分析)相关推荐

  1. LINUX SPI设备驱动模型分析之二 SPI总线模块分析

    上一篇文章我们简要介绍了SPI驱动模块,本章我们详细说明一下spi总线.设备.驱动模块的注册.注销以及这几个模块之间的关联. SPI总线的注册 spi模块也是基于LINUX设备-总线-驱动模型进行开发 ...

  2. linux spi屏驱动程序,65 linux spi设备驱动之spi LCD屏驱动

    SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来描述.在设备驱动中就可以通过函数spi_write, spi_read, spi_w8r16, spi_w8r8等函数 ...

  3. Linux SPI设备驱动

    实现了SPI OLED外设驱动,OLED型号为SH1106. 1.主机驱动与外设驱动分离 Linux中的I2C.SPI.USB等总线驱动,都采用了主机(控制器)驱动与外设(设备)驱动分离的思想.主机端 ...

  4. 有点意思!Linux 块设备处理模型,基础【簇、柱面、存储的计算】

    http://blog.csdn.net/zplove003/article/details/7020557 簇:簇是指可分配的用来保存文件的最小磁盘空间,扇区是磁盘最小的物理存储单元,但由于操作系统 ...

  5. linux probe函数调用,linux spi设备驱动中probe函数何时被调用

    这两天被设备文件快搞疯了,也怪自己学东西一知半解吧,弄了几天总算能把设备注册理清楚一点点了.就以spi子设备的注册为例总结一下,免得自己忘记. 首先以注册一个spidev的设备为例: static s ...

  6. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

  7. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

  8. LINUX I2C设备驱动模型分析之二 总线部分分析

    上一章我们对I2C模块做了总体框架的分析,本章我们主要分析下I2C模块的总线部分,主要涉 及总线初始化.总线相关属性.总线相关接口函数处理等几部分 I2c bus的定义 I2c bus的定义如下,主要 ...

  9. Linux设备驱动模型2——总线式设备驱动组织方式

    以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 更深入理解资料:http://blog.csdn.net/oqqhutu12345678/article/details/78933386 ...

  10. Linux·SPI驱动分析和实例

    目录 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.master 驱动框架 3.1 驱动侧 3.2 设备侧 4.SPI 设备驱动框架 4.1 设备层 4.2 驱动侧 5.设备驱动程序实例 1 ...

最新文章

  1. 【组队学习】【30期】6. 树模型与集成学习
  2. 扩展欧几里得 POJ 1061
  3. 机器学习 模型性能评估_如何评估机器学习模型的性能
  4. exec族函数、system函数、popen函数、PATH
  5. 62. 不同路径(JavaScript)
  6. 前端工程师需要学习ps 吗_转行学习web前端开发,需要哪些工具和需要学习什么?...
  7. Android input监控耳机插入demo
  8. iOS之内存管理(ARC)
  9. BZOJ35453551[ONTAK2010]Peaks——kruskal重构树+主席树+dfs序+树上倍增
  10. 【Git可视化工具】Sourcetree的初始化与使用
  11. 素数筛 python
  12. win7计算机属性资源管理器停止工作,win7系统windows资源管理器已停止工作的解决方法...
  13. 我的世界服务器自定义附魔外挂,我的世界怎么用命令方块刷自定义附魔神器
  14. CentOS7.9安装教程,以及出现dracut- initqueue timeout-starting…starting timeout scripts 解决办法,linux配置静态IP地址
  15. Centos7 源码编译安装linux longterm 内核4.19.47
  16. Springboot2(43)轻松搞定自定义@Enable模块装配
  17. JavaScript中this工作原理
  18. 将Cocos Creator项目运行到微信小程序
  19. web上传文件到ftp服务器,web 上传文件到ftp服务器上
  20. 「ACM-ICPC基础算法」

热门文章

  1. Volatility3内存取证工具使用详解
  2. MySQL下载压缩包安装详细过程
  3. k8s1.23 Ingress-nginx实操
  4. [EventKit] Error getting default calendar for new reminders: Error Domain=EKCADErrorDomain Code=1013
  5. 单片微型计算机频率测量实验,毕业论文:频率计系统设计
  6. ORACLE索引分裂(enq: TX - index contention)
  7. 软磁材料种类、特点和应用范围
  8. 洛谷 4238 【模板】多项式求逆
  9. 宋江是怎么当上老大的
  10. ERP与条码系统集成实现精益化可追溯