网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
spi_set_drvdata(spi, spidev);
else
kfree(spidev);
return status;
}
主要功能就是调用device\_create创建设备文件,生成设备节点,用户可以通过节点进行读写和iotrol操作,其次还完成了如下操作:
1、分配一个spidev\_data结构体,用来记录对应的spi\_device。
2、将spi\_data记录在一个链表里。
3、分配一个设备好,以后可以根据这个次设备号在上述的链表里面查找spidev\_data。
4、device\_create函数会生成一个设备节点:/dev/spidevB.D。B表示总线号,B表示这是SPI master下第几个设备,后续就可以通过/dev/spidevB.D来访问spidev驱动。
设备驱动的初始化和退出:
static int __init spidev_init(void)
{
int status;
/* Claim our 256 reserved device numbers. Then register a class
* that will key udev/mdev to add/remove /dev nodes. Last, register
* the driver which manages those device numbers.
*/
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); /* 注册字符设备(spidev_fops) */
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "spidev"); /* 注册sysfs spidev节点 */
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
return PTR_ERR(spidev_class);
}
status = spi_register_driver(&spidev_spi_driver); /* 注册spi设备驱动 */
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
module_init(spidev_init); /* 驱动模块初始化 */
static void __exit spidev_exit(void)
{
spi_unregister_driver(&spidev_spi_driver); /* 注销spi 设备驱动 /
class_destroy(spidev_class); / 注销sysfs spidev节点 /
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); / 注销spi设备驱动 */
}
module_exit(spidev_exit); /* 驱动模块注销 */
module\_init源码分析请关注:[module\_init源码分析](https://bbs.csdn.net/topics/618542503)。
module\_exit源码分析请关注:[module\_exit源码分析](https://bbs.csdn.net/topics/618542503)。
class\_create源码分析请关注:[class\_create源码分析](https://bbs.csdn.net/topics/618542503)
class\_destroy源码分析请关注:[class\_destroy源码分析](https://bbs.csdn.net/topics/618542503)
register\_chrdev源码分析请关注:后续更新(TODO)。
unregister\_chrdev源码分析请关注:后续更新(TODO)。
**SPIDEV\_MAJOR**:#define SPIDEV\_MAJOR 153 /\* assigned \*/
### spidev\_init源码分析
register\_chrdev:创建字符设备,spi属于字符设备驱动,定义如下:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
入参传入 file\_operations 结构体,结构体存了很多函数指针,实现读写和ioctrl相关操作,也是驱动最核心的功能,下面是spidev 实现的结构体:
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It’ll simplify things
* too, except for the locking.
/
.write = spidev_write, / 单工写模式 /
.read = spidev_read, / 单工读模式 /
.unlocked_ioctl = spidev_ioctl, / 设置频率、模式、进行双工传输 */
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,
};
#### spidev\_fops分析
**spiev\_write函数分析**
spidev\_write的源码如下:
/* Write-only message with current device setup */
static ssize_t
spidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status;
unsigned long missing;
/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
return -EMSGSIZE;
spidev = filp->private_data; /* spidev_data结构体是很重要的数据传递类型 */
mutex_lock(&spidev->buf_lock);
missing = copy_from_user(spidev->tx_buffer, buf, count); /* 数据从用户态copy到内核态 */
if (missing == 0)
status = spidev_sync_write(spidev, count); /* 同步数据 */
else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);
return status;
}
spidev\_sync\_write函数的具体实现如下:
static inline ssize_t
spidev_sync_write(struct spidev_data spidev, size_t len)
{
struct spi_transfer t = {
.tx_buf = spidev->tx_buffer, / 指定tx_buffer /
.len = len, / 指定长度 /
.speed_hz = spidev->speed_hz, / 指定传输速率 */
};
struct spi_message m;
spi_message_init(&m); /* spi消息初始化(初始化传输事务链表头) */
spi_message_add_tail(&t, &m); /* 添加spi传输到spi消息传输链表,将t放到message的尾部 */
return spidev_sync(spidev, &m); /* spi同步传输 */
}
上述代码中的spi\_message\_init函数,具体实现如下:
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
INIT_LIST_HEAD(&m->resources);
}
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
spi_message_init_no_memset(m);
}
通过源码可知,spi\_message\_init将传入的结构体spi\_message全部内容初始化为0,并被初始化过的结构体spi\_message传递给了函数spi\_message\_init\_no\_memset。
在spi\_message\_init\_no\_memset通过INIT\_LIST\_HEAD为m->transfers和m->resources分别创建双向链表的头节点。
在spidev\_sync\_write函数中,在完成SPI数据的链表的初始化之后又通过调用spi\_message\_add\_tail函数,将struct spi\_transfer t和struct spi\_message m分别添加到前一步创建的双向链表的尾部。
在spidev\_sync\_write函数的最后通过调用spidev\_sync函数进行SPI的同步传输,并将结果返回,此处spidev\_sync函数的具体实现如下:
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
int status;
struct spi_device *spi;
spin_lock_irq(&spidev->spi_lock);
spi = spidev->spi;
spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
status = -ESHUTDOWN;
else
status = spi_sync(spi, message);
if (status == 0)
status = message->actual_length;
return status;
}
梳理spidev\_sync的数据传输流程:spidev\_sync --> spi\_sync --> \_\_spi\_sync --> \_\_spi\_queued\_transfer --> kthread\_queue\_work最终将数据放到工作队列中,通过SPI总线驱动实现数据的发送功能。
#### **spiev\_read函数分析**
spidev\_read函数源码如下:
/* Read-only message with current device setup */
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;
/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
return -EMSGSIZE;
spidev = filp->private_data; /* 从私有数据中获取spidev_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); /* 将读取的数据从内核态copy到用户态 */
if (missing == status)
status = -EFAULT;
else
status = status - missing;
}
mutex_unlock(&spidev->buf_lock); /* 解锁操作 */
return status;
}
spidev\_sync\_read函数的具体实现如下:
static inline ssize_t
spidev_sync_read(struct spidev_data spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->rx_buffer, / 指定rx_buffer /
.len = len,
.speed_hz = spidev->speed_hz,
};
struct spi_message m; / 构造一个message */
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(&t, &m); /* 将transfer放到message的尾部 */
return spidev_sync(spidev, &m); /* 发起数据传输 */
}
将要发送的数据填充到struct spi\_transfer t结构体中,跟spidev\_sync\_write同样的将通过spi\_message\_init函数初始化spi\_message全部为0,通过spi\_message\_init\_no\_memset函数调用INIT\_LIST\_HEAD为m->transfers和m->resources分别创建双向链表的头节点。
与spidev\_sync\_write函数一样,在完成SPI数据的链表的初始化之后又通过调用spi\_message\_add\_tail函数,将struct spi\_transfer t和struct spi\_message m分别添加到前一步创建的双向链表的尾部。
spidev\_sync函数完成数据同步的流程此处不在重复。
**spidev\_ioctl函数分析**
spidev\_ioctl的源码如下:
static long
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;
/* Check type and command number */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
return -ENOTTY;
/* guard against device removal before, or while,
* we issue this ioctl.
*/
spidev = filp->private_data;
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
return -ESHUTDOWN;
/* use the buffer lock here for triple duty:
* - prevent I/O (from us) so calling spi_setup() is safe;
* - prevent concurrent SPI_IOC_WR_* from morphing
* data fields while SPI_IOC_RD_* reads them;
* - SPI_IOC_MESSAGE needs the buffer locked "normally".
*/
mutex_lock(&spidev->buf_lock);
switch (cmd) {
/* read requests */
case SPI_IOC_RD_MODE:
retval = put_user(spi->mode & SPI_MODE_MASK,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_MODE32:
retval = put_user(spi->mode & SPI_MODE_MASK,
(__u32 __user *)arg);
break;
case SPI_IOC_RD_LSB_FIRST:
retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
(__u8 __user *)arg);
break;
case SPI_IOC_RD_BITS_PER_WORD:
retval = put_user(spi->bits_per_word, (__u8 __user *)arg);
break;
case SPI_IOC_RD_MAX_SPEED_HZ:
retval = put_user(spidev->speed_hz, (__u32 __user *)arg);
break;
/* write requests */
case SPI_IOC_WR_MODE:
case SPI_IOC_WR_MODE32:
if (cmd == SPI_IOC_WR_MODE)
retval = get_user(tmp, (u8 __user *)arg);
else
retval = get_user(tmp, (u32 __user *)arg);
if (retval == 0) {
struct spi_controller *ctlr = spi->controller;
u32 save = spi->mode;
if (tmp & ~SPI_MODE_MASK) {
retval = -EINVAL;
break;
}
if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
ctlr->cs_gpiods[spi->chip_select])
tmp |= SPI_CS_HIGH;
tmp |= spi->mode & ~SPI_MODE_MASK;
spi->mode = (u16)tmp;
retval = spi_setup(spi);
if (retval < 0)
spi->mode = save;
else
dev_dbg(&spi->dev, "spi mode %x\n", tmp);
}
break;
case SPI_IOC_WR_LSB_FIRST:
retval = get_user(tmp, (__u8 __user *)arg);
if (retval == 0) {
u32 save = spi->mode;
if (tmp)
spi->mode |= SPI_LSB_FIRST;
else
spi->mode &= ~SPI_LSB_FIRST;
retval = spi_setup(spi);
if (retval < 0)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!