R Field - arm
type: channel
link: http://www.ramlife.org/tag/arm/
items: stm32 iap md5
items: 100ask 韦东山 驱动大全 子系统 v4l2 学习笔记
items: 100ask 韦东山 驱动大全 子系统 usb 学习笔记
items: 100ask 韦东山 驱动大全 子系统 spi 学习笔记
items: 100ask 韦东山 驱动大全 子系统 pci 学习笔记
items: 100ask 韦东山 驱动大全 子系统 uart 学习笔记
items: 100ask 韦东山 驱动大全 子系统 interrupt 学习笔记
items: 100ask 韦东山 驱动大全 子系统 gpio 学习笔记
items: 100ask 韦东山 驱动大全 子系统 pinctrl 学习笔记
items: 100ask 韦东山 驱动大全 子系统 input 学习笔记

stm32 iap md5
type: item
date: 2022-04-30T17:31:00+08:00
description: 参考: STM32F103 通过SD卡IAP升级程序，带MD5校验，（带源码）可在实际项目中使用 https://blog.csdn.net/qq_15181569/article/details/106852095 文件md5在线计算 http://www.metools.info/other/o21.html STM32F103通过Ymodem协议更新程序带MD5校验 https://blog.csdn.net/qq_15181569/article/details/115762289

100ask 韦东山 驱动大全 子系统 v4l2 学习笔记
type: item
date: 2020-12-13T13:45:00+08:00
description: 总结 数据采集流程 设置好长宽之后，摄像头可能不支持，需要检查长宽参数是否被修改。 app 根据自身处理能力的快慢，指定申请的 buffer 数量。 一般使用链表的形式来组织 buffer，app 从 out 链表中拿数据，拿完之后，放入 in 链表；驱动从 in 链表中拿到 buffer，然后填充数据，再放到 out 链表中去。 app 开发 关键结构体: v4l2_fmtdesc format enumeration v4l2_format stream data format ioctl 中常用的 id 是: VIDIOC_QUERYCTRL 查找参数 VIDIOC_G_CTRL 获取当前参数 VIDIOC_S_CTRL 设置当前参数 对于 VideoControl interface 内部，有多个 unit。 unit 自身只能多个输入，单个输出。 unit之间，只能单个 unit 输出给多个 unit. VideoControl interface 内部有 CT(camera terminal), IT(input terminal), OT(out terminal), SU(selector unit), PU(processing unit), EU(encoding unit), XU(extension unit) VideoStreaming interface 是 USB 相关 如果自己做摄像头，想要增加额外功能，那就需要用到 XU，并且还需要添加 mapping，指定具体哪些位是哪些功能。 app 帧细节 格式枚举(yuv 还是其他)，用 VIDIOC_ENUM_FMT; 帧枚举(分辨率之类)用 VIDIOC_ENUM_FRAMESIZES, 都需要让相应的 index 增加来通过 ioctl 枚举。 app 获取数据 检查设备能力 struct v4l2_capability cap; memset(&cap, 0, sizeof(struct v4l2_capability)); if (0 == ioctl(fd, VIDIOC_QUERYCAP, &cap)) { if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { 枚举格式和帧VIDIOC_ENUM_FMT VIDIOC_ENUM_FRAMESIZES 设置格式 struct v4l2_format fmt; memset(&fmt, 0, sizeof(struct v4l2_format)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 1024; fmt.fmt.pix.height = 768; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; fmt.fmt.pix.field = V4L2_FIELD_ANY; if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt)) 申请buffer 并 mmap struct v4l2_requestbuffers rb; memset(&rb, 0, sizeof(struct v4l2_requestbuffers)); rb.count = 32; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; if (0 == ioctl(fd, VIDIOC_REQBUFS, &rb)) { /* 申请成功后, mmap这些buffer */ buf_cnt = rb.count; for(i = 0; i < rb.count; i++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(struct v4l2_buffer)); buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (0 == ioctl(fd, VIDIOC_QUERYBUF, &buf)) { /* mmap */ bufs[i] = mmap(0 /* start anywhere */ , buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if(bufs[i] == MAP_FAILED) { perror("Unable to map buffer"); return -1; } } …

100ask 韦东山 驱动大全 子系统 usb 学习笔记
type: item
date: 2020-11-29T09:45:00+08:00
description: 总结 usb 框架 usb 每一级都可以是 hub 或者 设备，但是 hub 最多6级，第7级只能是设备。 app 可以通过两种方法来访问硬件 app -> usb device driver -> usb host driver -> host 硬件 -> device 硬件 app -> libusb -> usb host driver -> host 硬件 -> device 硬件 电气信号 低速，d-变化，全速和高速 d+ 变化， 先识别到全速，然后 host 会发送一个复位，device 复位之后，会和 host 之间发送 K J 信号，如果没问题，那就是高速 host + 高速设备，然后高速设备就会取消 d+ 的上拉电阻。 高速时， host 和 设备都会下拉电阻用于防止信号反射，一旦 设备拔出，信号反射变化变大，就可以被检测出来。 从 idle 的 j 状态变化为 k 状态，就是 sop 信号了，后面就是要开始传输信号了。 NRZI：反向不归零编码，波形变化表示0，波形不变表示1. 当连续传输6个1之后，必须强制插入一个0，防止双方在长期不变信号的情况下，传输错位。 协议，数据包格式 外层包：sop + sync + 包内容 + eop 包内容: pid + 地址 + 帧号 + 数据 + crc pid: 方向 + 类型。 一个完整的事务，含有 令牌包 + 数据包 + 握手回应包，对应了 令牌域，数据域，握手域。 host 写，含有 host 发送的 令牌包（地址） + 数据包，以及 device 发送的 握手回应包。 host 读，含有 host 发送的 令牌包（地址） + device 发送的数据包，以及 host 发送的 握手回应包。 数据传输形式 批量传输：u盘，可靠，非实时 中断传输：鼠标，可靠，实时 实时传输：摄像头，非可靠，实时 控制传输：识别、枚举等。 控制传输由多个事务实现，其他传输由一个事务实现。 批量传输：读和写都是完整的事务，另外还有一个 ping 查询包 中断传输: 读和写都是完整事务，但是要周期性的传输。 实时传输: 没有握手包。 控制传输：由设置阶段 + 数据阶段 + 状态阶段组成 设置阶段: 令牌包中指明 setup 数据阶段: 由一个或多个批量传输来组成 状态阶段：由一个批量传输组成，如果数据阶段是读或写，那么本阶段会反向；如果无数据阶段，那么本阶段就是读 体验数据格式 usbprotocolsuit 中的示例程序可以看数据格式。 高速模式，sync 4个字节，eop 1个字节，字节长度是包含他们的。 描述符 一个硬件设备可能有多种配置，一般情况下，只能生效一种配置。 比如：上网卡有 u盘 和 上网两种配置。 一个配置下，可能有多个接口也就是功能逻辑设备，一般可以同时生效。比如，耳机有音频和按键 一个接口，可能有多个端点，一种端点只能由一种数据方向。 比如说音频接口，有播放和录音这两个端点。 所以描述符会有：设备描述符，配置描述符，接口描述符，端点描述符。 usb设备，都有端口0，用于最开始的通讯。 linux 上面可以使用 lsusb -v 来查看当前的 usb 描述符。 枚举过程 全速设备在复位后，会检查是否支持高速。 设备初始地址为0， host 会和这个地址 0, ep0 进行通讯。设置地址之类的事情 启用新地址后， host 与新地址通信。 配置可用配置 在设置事务中，数据阶段，都是标准的 8字节数据，并且是固定格式。 枚举流程: 获取设备描述符 设置地址 获取设备描述符 获取配置描述符 设置配置 libusb app 可以通过 libusb 来和 host 驱动打交道。 但是这就要求 app 需要阅读 usb 设备手册，了解设备相关细节，然后才能用 libusb 提供的接口进行操作。 本来这些 usb 设备相关的工作，应该由驱动工程师来实现。 app 有两种方式来打开 usb 设备 知道确切的 usb 编号，就可以通过编号直接打开 不知道编号，就需要遍历所有 sub 设备，然后查看设备描述符和配置描述符，确定好之后，再打开。 app 通过 libusb 使用 usb 设备的时候，还需要通过 detach 相关函数，先移除驱动，然后再 claim 函数，认领这个 usb 设备。 传输可以通过 同步传输也可以异步传输。 另外 libusb 使用的时候，也需要 init, open, detach claim 等操作以及相应的反操作。 libusb api libusb 异步的好处是，可以针对多 ep 进行操作，并且可以取消传输。 同步只能对一个 ep 进行操作，除非用多线程。 异步传输主要是几个步骤: 分配 填充 提交 处理事件，检查结果，回调函数。 释放 usb 鼠标…

100ask 韦东山 驱动大全 子系统 spi 学习笔记
type: item
date: 2020-11-15T15:10:00+08:00
description: 总结 整体 spi 分为 soc 上面的 spi master 和 外接的 spi device. 在设备树中，spi 节点本身是 spi master, 子节点就是对应的 spi device. 加载驱动的时候，根据 compitble 识别到 spi master, 并作为 platform 总线设备，匹配到对应的总线驱动。 在其 probe 中，注册 spi 总线master 并识别设备树中的 spi 子节点，并生成 spi 总线设备，并匹配到 spi 总线驱动。 设备树 可以参考内核文档 Documentation\devicetree\bindings\spi\spi-bus.txt, 有详细的说明。 deivce 必须提供的三个内容是，compatible, frequecy, reg. reg 用于指明是 cs-gpios 中的第几个。 master 必须提供的内容是: address-cells：这个SPI Master下的SPI设备，需要多少个cell来表述它的片选引脚 size-cells：必须设置为0 compatible：根据它找到SPI Master驱动 cs-gpios：SPI Master可以使用多个GPIO当做片选，可以在这个属性列出那些GPIO num-cs：片选引脚总数 驱动 spidev.c 中的 init 可以看出，并没有直接创建 device, 而是会直接注册 spi device 驱动。 在这个驱动的 probe 中，会根据设备树的情况，来创建 device，并把私有数据结构，挂到spi 链表上，用于后续的 open 来找到私有数据结构。 spidev.c 提供的 read, write 只能用于写或读，ioctrl 只能用与写一个字节后读，如果想要实现复杂功能，需要自己编写驱动。 内核中提供了相关的测试程序，在 tools\spi\spidev_fdx.c 里面。 spi设备使用 设备树 设备树中，compatible = "spidev" 这个会有点警告，不过可以用，因为并不是匹配 of_match_table. 具体的匹配在 spi.c 中的 spi_match_device 函数中 strcmp(spi->modalias, drv->name) 。 内核 内核中的 spidev 需要启用，名称是spi 名目下面的 user xxx. gpio 控制 gpio 控制一般可以三种方式， sysfs 接口：简单易用，适合快速测试 GPIO 功能。 类似于命令行方式。 libgpiod 库：高效灵活，推荐在生产环境中使用。 需要安装 libgpiod 库 mmap 直接访问硬件：适合高级用户，需要了解硬件寄存器布局。需要知道寄存器地址，比较麻烦。 sysfs 接口使用方式: // echo 509 > /sys/class/gpio/export char cmd[100]; dc_pin_num = number; sprintf(cmd, "echo %d > /sys/class/gpio/export", number); system(cmd); spi 使用 sysfs 控制 dc 引脚，用于数据传输，效率很慢。 自己的 spi 设备驱动 发送数据，首先需要构造 spi_message 作为链表头，然后需要收发多少次数据就构造多少个 spi_transfer，并把 spi_transfer 放入到 spi_message 的链表中。然后使用 spidev_sync 来发送。 如果只发或者只收，可以针对应的在 spi_transfer 中把 rx_buf 或者 tx_buf 设置为 null 自己的设备驱动，可以在 probe 中，直接 register_chrdev class_create device_create 一起完成。 自己的 oled spi 设备驱动 dc 使用 gpiod 放在驱动中控制，app 通过 ioctl 进行初始化或设置位置等操作，写入通过 write. framebuffer 改造 oled 正常的 qt 等程序，使用 framebuffer，像素是横向对应到 fb 里面的字节，app 并不关心具体的控制。 所以需要改造我们的驱动。把以前的 framebuffer 中的 fb_ops 相关的操作移植过来。 都处理好之后，register_framebuffer 注册 fb. 因为 app 只需要把显示的内容放到 fb 中即可。这就需要驱动定期的把 fb 内容更新到屏幕上面，可以使用 timer 或者内核线程来做这个事情。如果用内核线程，那就用 kthread_run 创建，用 schedule_timeout_interruptible 休…

100ask 韦东山 驱动大全 子系统 pci 学习笔记
type: item
date: 2020-11-01T09:30:00+08:00
description: 总结 pci 和 pcie 都是对地址进行转换，转换情况需要上电后，写到配置寄存器中。一般这种中间设备成为 pci 桥，相关驱动是 pci_host. pci 通过地址线的方式，来选通桥后的设备。 pcie桥本身内部端口有地址，端口后只接一个设备，选通的其实是端口。 针对通过本级 pci/pcie 能够直达的，配置是，发送 type0包，不能直达的，发送 type1包。 type1包在桥之间转发，直到下级就是目标终端，才会转换为 type0 包。 数据包中，一般包含 bus, device, function, register 四部分。 桥设备会根据直接连接的下级设备，向上级索要地址范围。

100ask 韦东山 驱动大全 子系统 uart 学习笔记
type: item
date: 2020-10-18T17:30:00+08:00
description: 总结 分层 主要分为三层， 最下面的厂商硬件相关驱动 drivers\tty\serial\imx.c 中间的行规层 drivers\tty\n_tty.c, 用于决定换行等特殊字符是否生效 上层 tty drivers\tty\tty_io.c 结构体 uart 驱动中关键结构体 uart_driver 定义在include\linux\serial_core.h，里面有两个成员变量 struct uart_state *state; struct tty_driver *tty_driver; serial_core.h 中的 struct uart_state *state; -> struct uart_port *uart_port; -> const struct uart_ops *ops; . 而在厂商驱动成员中，定义了 static const struct uart_ops imx_pops 里面包括了各种硬件相关的操作函数，probe 的时候，会把厂商的 uart_ops 给到 uart_driver一路向下的 uart_ops，让后续系统调用中可以正确的操作硬件。 include\linux\tty_driver.h 中的 struct tty_driver 包含了面向上层用的 tty 相关的内容，包括 struct cdev **cdevs; struct tty_struct **ttys; struct tty_port **ports; struct ktermios **termios; 其中 struct tty_port **ports; 指向前面 struct uart_state *state; 中的 struct tty_port port; 注册 使用 int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) 去注册。 console console 是 tty 中的一种，可以通过设备树或者 cmdline 进行配置，具体的实现也是在厂商驱动中，需要实现 static struct console imx_console 这样的结构体。

100ask 韦东山 驱动大全 子系统 interrupt 学习笔记
type: item
date: 2020-10-04T11:20:00+08:00
description: 异常与中断的概念及处理流程 异常向量表 _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq //发生中断时，CPU跳到这个地址执行该指令 **假设地址为0x18** ldr pc, _fiq 进程线程中断的核心_栈 CPU内部的寄存器很重要，如果要暂停一个程序，中断一个程序，就需要把这些寄存器的值保存下来：这就称为保存现场。 保存在内存中，这块内存就称之为栈。程序要继续执行，就先从栈中恢复那些CPU内部寄存器的值。 并不局限于中断，包括: 函数调用 中断处理 进程切换 Linux系统对中断处理的演进 Linux对中断的扩展：硬件中断、软件中断 Linux系统把中断的意义扩展了，对于按键中断等硬件产生的中断，称之为“硬件中断”(hard irq)。每个硬件中断都有对应的处理函数，比如按键中断、网卡中断的处理函数肯定不一样。 相对的，还可以人为地制造中断：软件中断(soft irq) 有哪些软件中断？查内核源码include/linux/interrupt.h enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, IRQ_POLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */ RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS }; + 怎么触发软件中断？最核心的函数是`raise_softirq`，简单地理解就是设置`softirq_veq[nr]`的标记位 `extern void raise_softirq(unsigned int nr);` + 怎么设置软件中断的处理函数：`extern void open_softirq(int nr, void (*action)(struct softirq_action *));` + 后面讲到的中断下半部tasklet就是使用软件中断实现的 中断处理原则1：不能嵌套 如果中断嵌套突然暴发，那么栈将越来越大，栈终将耗尽。 中断处理原则2：越快越好 在单芯片系统中，假设中断处理很慢，那应用程序在这段时间内就无法执行：系统显得很迟顿。 在SMP系统中，假设中断处理很慢，那么正在处理这个中断的CPU上的其他线程也无法执行。 在中断的处理过程中，该CPU是不能进行进程调度的，所以中断的处理要越快越好，尽早让其他中断能被处理──进程调度靠定时器中断来实现。 在Linux系统中使用中断是挺简单的，为某个中断irq注册中断处理函数handler，可以使用request_irq函数, 在handler函数中，代码尽可能高效。 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) 要处理的事情实在太多，拆分为：上半部、下半部 在handler函数里只做紧急的事，然后就重新开中断，让系统得以正常运行；那些不紧急的事，以后再处理，处理时是开中断的。 中断下半部的实现有很多种方法，讲2种主要的：tasklet(小任务)、work queue(工作队列)。 下半部要做的事情耗时不是太长：tasklet 当下半部比较耗时但是能忍受，并且它的处理比较简单时，可以用tasklet来处理下半部。tasklet是使用软件中断来实现。 写字太多，不如贴代码，代码一目了然： 使用流程图简化一下： 简单说： 下半部，开中断的时候，可以被中断，但是被中断前，count >= 1 再次中断上半部结束的时候，会检查 count 的值，正常情况 count 是0，但是如果是再下半部中被中断的，那么 count 就不是0，就会直接退出结束本次中断 cpu 会回到上次下半部被中断的地方继续执行下去。 注意：从上面的流程可以看出，上半部和下半部的关系是 N:1. 所以，下半部处理的时候要完整一点。 比如按键处理，那就要处理所有按键。 下半部要做的事情太多并且很复杂：工作队列 如…

100ask 韦东山 驱动大全 子系统 gpio 学习笔记
type: item
date: 2020-09-19T10:30:00+08:00
description: GPIO子系统视频介绍 参考资料： Linux 5.x内核文档 Linux-5.4\Documentation\driver-api Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt Linux 4.x内核文档 Linux-4.9.88\Documentation\gpio Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt 通用属性 Active-High and Active-Low gpiod_set_value(gpio, 1); // 输出逻辑1 // 在Active-High的情况下它会输出高电平 // 在Active-Low的情况下它会输出低电平 Open Drain and Open Source 多个GPIO驱动同时驱动一个电路时，就需要设置Open Drain或Open Source。 Open Drain：引脚被设置为低电平时才会驱动电路，典型场景是I2C接口。 Open Source：引脚被设置为高电平时才会驱动电路 使用GPIO子系统要掌握的重要概念 设备树 在设备树 dtsi 中，“GPIO组”就是一个GPIO Controller，这通常都由芯片厂家设置好。我们要做的是找到它名字，比如“gpio1”，然后在 dts 中指定要用它里面的哪个引脚，比如<&gpio1 0>。 下图是一些芯片的GPIO控制器节点，它们一般都是厂家定义好，在xxx.dtsi文件中： 我们暂时只需要关心里面的这2个属性： “gpio-controller”表示这个节点是一个GPIO Controller，它下面有很多引脚。 “#gpio-cells = ”表示这个控制器下每一个引脚要用2个32位的数(cell)来描述。这个 2 指的是除了gpioN 以外还要 2个值来描述。 普遍的用法是，用第1个cell来表示哪一个引脚，用第2个cell来表示有效电平。例如在使用引脚的时候，如 gpios = <&gpio5 3 GPIO_ACTIVE_LOW> 在自己的设备节点中使用属性[<name>-]gpios，name 用来做标识作用，示例如下： 代码 GPIO子系统有两套接口：基于描述符的(descriptor-based)、老的(legacy)。前者的函数都有前缀“gpiod_”，它使用gpiodesc结构体来表示一个引脚；后者的函数都有前缀“gpio”，它使用一个整数来表示一个引脚。 驱动程序中要包含头文件， #include <linux/gpio/consumer.h> // descriptor-based 或 #include <linux/gpio.h> // legacy 常用的函数： 获得GPIO descriptor-based legacy 说明 gpiod_get gpio_request gpiod_get_index gpiod_get_array gpio_request_array devm_gpiod_get devm_gpiod_get_index devm_gpiod_get_array 设置方向 descriptor-based legacy 说明 gpiod_direction_input gpio_direction_input gpiod_direction_output gpio_direction_output 读值、写值 descriptor-based legacy 说明 gpiod_get_value gpio_get_value gpiod_set_value gpio_set_value 释放GPIO descriptor-based legacy 说明 gpio_free gpio_free gpiod_put gpio_free_array gpiod_put_array devm_gpiod_put devm_gpiod_put_array 有前缀devm_的含义是“设备资源管理”(Managed Device Resource)，这是一种自动释放资源的机制。比如在Linux开发过程中，先申请了GPIO，再申请内存；如果内存申请失败，那么在返回之前就需要先释放GPIO资源。如果使用devm的相关函数，在内存申请失败时可以直接返回：设备的销毁函数会自动地释放已经申请了的GPIO资源。建议使用devm_版本的相关函数。 代码从设备树中获取数值 foo_device { compatible = "acme,foo"; ... led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */ <&gpi…

100ask 韦东山 驱动大全 子系统 pinctrl 学习笔记
type: item
date: 2020-09-05T09:40:00+08:00
description: 介绍 参考资料： Linux 5.x内核文档 Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt Linux 4.x内核文档 Documentation\pinctrl.txt Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt 真正去写驱动的是 bsp 工程师； 驱动工程师，只要了解，然后会用即可。 Pinctrl子系统重要概念 主要参考文档是：内核 Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt 这会涉及2个对象：pin controller、client device。 前者提供服务：可以用它来复用引脚、配置引脚。 在芯片手册里你找不到pin controller，它是一个软件上的概念，你可以认为它对应IOMUX──用来复用引脚，还可以配置引脚(比如上下拉电阻等)。 后者使用服务：声明自己要使用哪些引脚的哪些功能，怎么配置它们。 Pinctrl系统的客户，那就是使用Pinctrl系统的设备，使用引脚的设备。它在设备树里会被定义为一个节点，在节点里声明要用哪些引脚。 示例 上图中，左边是pin controller节点，右边是client device节点： pin state： 上图中，pinctrl-names里定义了2种状态：default、sleep。 第0种状态用到的引脚在pinctrl-0中定义，它是state_0_node_a，位于pincontroller节点中。 第1种状态用到的引脚在pinctrl-1中定义，它是state_1_node_a，位于pincontroller节点中。 当这个设备处于default状态时，pinctrl子系统会自动根据上述信息把所用引脚复用为uart0功能。 当这这个设备处于sleep状态时，pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。 groups和function 一个设备会用到一个或多个引脚，这些引脚就可以归为一组(group)；这些引脚可以复用为某个功能：function。 当然：一个设备可以用到多组引脚，比如A1、A2两组引脚，A1组复用为F1功能，A2组复用为F2功能。 Generic pin multiplexing node和Generic pin configuration node 在上图左边的pin controller节点中，有子节点或孙节点，它们是给client device使用的。 可以用来描述复用信息：哪组(group)引脚复用为哪个功能(function)； 可以用来描述配置信息：哪组(group)引脚配置为哪个设置功能(setting)，比如上拉、下拉等。 注意：pin controller节点的格式，没有统一的标准！！！！每家芯片都不一样。 甚至上面的group、function关键字也不一定有，但是概念是有的。 代码中怎么引用pinctrl 这是透明的，我们的驱动基本不用管。当设备切换状态时，对应的pinctrl就会被调用。 比如在platform_device和platform_driver的枚举过程中，流程如下： 当系统休眠时，也会去设置该设备sleep状态对应的引脚，不需要我们自己去调用代码。 非要自己调用，也有函数： devm_pinctrl_get_select_default(struct device *dev); // 使用"default"状态的引脚 pinctrl_get_select(struct device *dev, const char *name); // 根据name选择某种状态的引脚 pinctrl_put(struct pinctrl *p); // 不再使用, 退出时调用 pinctrl 使用示例 正常需要做三步： 哪个引脚？ iomux 引脚功能？ i2c 还是什么？ 引脚配置？ 开漏还是什么 设备树 引脚功能和引脚配置，会在设备树中体现, 可以参考 Linux-4.9.88\Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt pinctrl 节点描述 pincontroller { ... /* Standard DT properties for the device itself elided */ state_0_node_a { ... }; state_1_node_a { ... }; state_1_node_b { ... }; } 设备节点描述 device { p…

100ask 韦东山 驱动大全 子系统 input 学习笔记
type: item
date: 2020-08-23T14:45:00+08:00
description: input 子系统介绍 参考资料 Linux 5.x内核文档 Documentation\input\input-programming.rst Documentation\input\event-codes.rst Linux 4.x内核文档 Documentation\input\input-programming.txt Documentation\input\event-codes.txt input 种类 按键：EV_KEY，比如键盘 相对位移：EV_REL，比如鼠标 绝对位移：EV_ABS，比如触摸屏 杂项：EV_MSC 软件：EV_SW LED：EV_LED 声音：EV_SND 会自动发出重复按键：EV_REP 电源开关、按键：EV_PWR 驱动框架 input device, 主要了解的是 gpio 和 uinput. uinput 主要用于用户态的键盘输入模拟用。 app 输入系统框架及调试 输入系统框架 数据的流程如下： APP发起读操作，若无数据则休眠； 用户操作设备，硬件上产生中断； 输入系统驱动层对应的驱动程序处理中断：读取到数据，转换为标准的输入事件，向核心层汇报。所谓输入事件就是一个“struct input_event”结构体。 核心层可以决定把输入事件转发给上面哪个handler来处理： 从handler的名字来看，它就是用来处输入操作的。有多种handler，比如：evdev_handler、kbd_handler、joydev_handler等等。 最常用的是evdev_handler：它只是把input_event结构体保存在内核buffer等，APP来读取时就原原本本地返回。它支持多个APP同时访问输入设备，每个APP都可以获得同一份输入事件。 当APP正在等待数据时，evdev_handler会把它唤醒，这样APP就可以返回数据。 APP对输入事件的处理：APP获得数据的方法有2种：直接访问设备节点(比如/dev/input/event0,1,2,...)，或者通过tslib、libinput这类库来间接访问设备节点。这些库简化了对数据的处理。 调试 hexdump /dev/input/event0 命令行获取输入设备当前的数据。点击触摸屏就可以看到数据刷新。 ls /dev/input/* -l 查看输入设备节点 cat /proc/bus/input/devices 查看详细的设备信息 详细设备信息解析 主要是 include\linux\input.h 中 struct input_dev 里面的值的表示。 struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; ... I:id of the device(设备ID) 该参数由结构体struct input_id来进行描述，驱动程序中会定义这样的结构体： // include\uapi\linux\input.h struct input_id { __u16 bustype; __u16 vendor; __u16 product; __u16 version; }; N:name of the device 设备名称 P:physical path to the device in the system hierarchy 系统层次结构中设备的物理路径。 S:sysfs path 位于sys文件系统的路径 U:unique identification code for the devi…