在linux中,spi框架是通过主机、设备分离的思想设计的,主机、设备分离的思想就是主机驱动跟设备驱动分开实现,但是它们之间又相互联系,毕竟上层应用是通过主机控制器间接来控制具体设备,这就涉及到了两个关系,上层应用访问跟主机控制器、主机控制器跟具体设备之间的关系,清楚linux如何处理它们之间的关系,是了解这个框架的关键。在内核中对上层访问spi设备抽象出了通用的部分,这部分在spidev.c文件中实现,这个文件里面有个初始化函数:
static int __init spidev_init(void)
{
int status;
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);//注册一个字符设备,并把SPIDEV_MAJOR主设备号跟spidev_fops进行绑定
if (status < 0)
return status;
spidev_class = class_create(THIS_MODULE, "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_driver
if (status < 0) {
class_destroy(spidev_class);
unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
return status;
}
spi驱动是基于字符设备驱动实现的,在学习字符设备驱动的时候知道,编写一个完整的字符设备的四部曲:
1、注册一个设备号(可用接口alloc_chrdev_region,register_chrdev_region)
2、主设备号跟文件操作函数集的绑定(cdev_add) 注:以上两步可用register_chrdev代替(次设备号固定)
3、创建一个struct class结构(class_create)
4、创建设备驱动文件(device_create)
由上代码可知这里完成了编写一个字符设备驱动的前三步,还需要创建一个设备文件供上层访问,这里只是进行初始化,显然也是没有必要去生成设备文件,因此这里可以引出一个疑问?(设备文件会在哪里生成)。最后是调用spi_register_driver注册一个spi_driver,这里需要先说明一下,spi是通过总线的方式注册进系统的(在spi.c文件中实现),这里先来看下spi_register_driver的实现:
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}
这里初始化driver结构的总线类型,然后是调用driver_register注册一个driver,到这里可以回到总线驱动模型的知识,driver_register是所有总线注册一个driver进总线中的统一接口,在学习总线模型的时候知道,在调用driver_register注册一个driver会去遍历总线下的device,并调用总线提供的总线提供的match函数进行匹配,对应的spi总线,它的match函数为:
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))//driver的of_match_table跟device的of_node成员进行比较,
return 1;