一、总结
内核开放的通用版本的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,
};<

最低0.47元/天 解锁文章
367

被折叠的 条评论
为什么被折叠?



