文章目录
-
- 一、总结
- 二、两种数据类型
-
- 1、spidev_data结构体
- 2、spi_ioc_transfer结构体
- 三、设备树节点
-
- 1、pinctrl子节点
- 2、spidev子节点
- 四、spidev_init()函数
-
- spidev_fops文件操作接口
-
- (1) spidev_read()函数
-
- spidev_sync_read()函数
-
- spidev_sync()函数
- (2) spidev_ioctl()函数
-
- spidev_message()函数
- 应用层对应ioctl()函数
- SPI_MODE_MASK宏
- spidev_get_ioc_message()函数
- (3) spidev_open()函数
- 五、spidev_probe()函数
一、总结
内核开放的一般版本spi驱动
drivers/spi/spidev.c 是 SPI万能驱动
内核集成spidev驱动模块将在启动后自动加载 支持多种修改spi通信参数
二、两种数据类型
1、spidev_data结构体
fops使用中间的函数结构,将被赋值file->private_data
struct spidev_data { // 设备号 dev_t devt; spinlock_t spi_lock; struct spi_device *spi; struct list_head device_entry; struct mutex buf_lock; unsigned users; // 发送buf,接收buf,通信频率 u8 *tx_buffer; u8 *rx_buffer; u32 speed_hz;}; 2、spi_ioc_transfer结构体
include/uapi/linux/spi/spidev.h 可用于设置spi这种结构也用于用户空间编程
struct spi_ioc_transfer { // spi缓存区发送数据 __u64 tx_buf; // spi缓存区的数据接收 __u64 rx_buf; // 收发数据长度 __u32 len; __u32 speed_hz; __u16 delay_usecs; __u8 bits_per_word; __u8 cs_change; __u8 tx_nbits; __u8 rx_nbits; __u16 pad;}; 三、设备树节点
1、pinctrl子节点
pinctrl_ecspi3:ecspi3grp { // 此属性记录引脚组 fsl,pins = < MX6UL_PAD_UART2_TX_DATA__ECSPI3_SS0 0x1a090 MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x11090 MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x11090 MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x11090 >; };
2、spidev子节点
&ecspi3{
pinctrl-names = "default"; // default表示使用pinctrl-0引脚组 pinctrl-0 = <&pinctrl_ecspi3>; status = "okay"; #address-cells = <1>; #size-cells = <0>; // 追加一个设备节点 // 此节点挂载在spi节点下,会被内核解析成一个spi_device设备,挂在对应的spi总线上 spidev@0 {
// 用来匹配对应的驱动,pdidev.c compatible = "spidev"; spi-max-frequency = <20000000>; reg = <0>; };};
四、spidev_init()函数
spidev.c
static int __init spidev_init(void){ int status; ... // 申请设备号,参数1主设备号为153 // 这一步将主设备号153机器所有的次设备号都占用了 status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); ... // 创建spidev设备类,新增/sys/class/spidev spidev_class = class_create(THIS_MODULE, "spidev"); ... // 向内核注册一个spi设备驱动 status = spi_register_driver(&spidev_spi_driver); ... return status;}
spidev_fops文件操作接口
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE, .write = spidev_write, .read = spidev_read, // 应用层 ioctl()函数底层操作接口(32位系统) .unlocked_ioctl = spidev_ioctl, // 应用层 ioctl()函数底层操作接口(64位系统) .compat_ioctl = spidev_compat_ioctl, .open = spidev_open, .release = spidev_release, .llseek = no_llseek,};
注意read和write接口只能半双工收发消息, spi支持全双工,可使用unlocked_ioctl接口可以支持半双工,全双工(switch中的default分支)收发消息。
(1) spidev_read()函数
static ssize_tspidev_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; // 通过文件指针获取struct spidev_data,在fops->open中完成赋值 spidev = filp->private_data; mutex_lock(&spidev->buf_lock); // 详见下 status = spidev_sync_read(spidev, count); if (status > 0) {
unsigned long missing; missing = copy_to_user(buf, spidev->rx_buffer, status); if (missing == status) status = -EFAULT; else status = status - missing; } mutex_unlock(&spidev->buf_lock); return status;}
spidev_sync_read()函数
spidev_sync_read(struct spidev_data *spidev, size_t len){
// 此类型是传输spi消息的最基本的单元 struct spi_transfer t = {
// 此buffer在fops->rx_buf中分配,