154 spidev:SPI“万能”驱动

一、总结

内核开放的通用版本的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_t
spidev_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中分配,4096
			.rx_buf		= spidev->rx_buffer,
			// 从fops->read可以知道是用户空间希望读取的字节数量
			.len		= len,
			// 在spi_probe中赋值,就是spi的最大通信频率
			.speed_hz	= spidev->speed_hz,
		};<
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值