1、引言

待补充,总线是linux设备驱动的一种软件架构思想。

2、总线概述

总线设备驱动一共分为3步,第一是学习总线,第二是学习设备,第三步是学习驱动。

 

这张图不全,不管是总线还是设备还是驱动,都分为结构描述、注册、注销这三部分。

2.1第一步:总线创建-结构体-注册-注销

 

 

 

 

 如何创建一条总线?

bus.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");

int my_match(struct device *dev, struct device_driver *drv)
{ //两个结构体,一个代表设备,一个代表驱动
  return 0;
}

struct bus_type my_bus_type={
  .name = "my_bus",
  .match = my_match,
};
EXPORT_SYMBOL(my_bus_type); 

int my_bus_init(void)
{
  int ret=0;
  ret = bus_register(&my_bus_type);
}
void my_bus_exit(void)
{
  bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit);

:加载之后,会在sys/bus总线目录下多出来我们定义的总线

 

 

 

2.2 如何创建驱动?并且挂载在上面的总线上

 

 

 

 

 

 driver.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

int my_probe(struct device *dev)//谁来触发probe函数呢???
{
  printk("driver found the device it can handler\n");
  return 0;
}
struct device_driver my_driver = {
  .name = "my_dev",
  .bus = &my_bus_type,//这里引用了上面创建的总线结构体
  .probe = my_probe,
};
int my_driver_init(void)
{
  int ret;
  ret = driver_register(&my_driver);
}

void my_driver_exit(void)
{
  driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);


Makefile
obj-m := bus.o driver.o
KDIR := /home/aston/040-linux-tq2440/linux-tq2440
all:
    make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order

  在讲讲总线设备驱动的注册过程:

 

 

 

 

 

 

 

 

 

 

2.3 第三步:如何创建并且在总线上挂载一个设备??

前面两步我们已经加载好了总线和驱动

 

 

 

device.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;
struct device my_device={
  .init_name = "my_device",
  .bus = &my_bus_type,
};

int my_device_init(void)
{
  int ret;
  ret = device_register(&my_device);
}
void my_device_exit(void)
{
  device_unregister(&my_device);
}
module_init(my_device_init);
module_exit(my_device_exit);

 

问题1、设备上线后,谁来触发driver的probe函数??谁来粗发bus的match函数??

这个应该在内核里面有答案,match匹配到之后,才会调用到对应的驱动。也就说上层有地方会持续的扫描调用match函数,或者由中断触发。当发现有device上线后,match会拿着device的name去逐一和当前的driver的name进行匹配。找到之后就会调用probe函数。这也是到现在为止,把这几个ko加载后没反应的原因。

 

需要修改bus.c
bus.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");

int my_match(struct device *dev, struct device_driver *drv)
{
    //两个结构体,一个代表设备,一个代表驱动
  //return !strncmp(dev->init_name,drv->name,strlen(drv->name));
  //上电后发现init_name是空指针,导致出现堆栈回溯异常
  return !strncmp(dev->kobj.name, drv->name, strlen(drv->name));
}
/*在添加设备时
int device_add(struct device *dev)  问题:这个函数在什么时候被调用呢????
{
    if (dev->init_name) {
        dev_set_name(dev, "%s", dev->init_name);
        dev->init_name = NULL;所以我们需要比较的是dev->kobj.name
    }
}
*/

struct bus_type my_bus_type={
  .name = "my_bus",
  .match = my_match;
};
EXPORT_SYMBOL(my_bus_type); 

int my_bus_init()
{
   int ret=0;
  ret = bus_register(&my_bus_type);
}
void my_bus_exit()
{
  bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_init(my_bus_exit);


Makefile
obj-m := bus.o driver.o device.o
KDIR := /home/aston/040-linux-tq2440/linux-tq2440
all:
    make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order

问题1:device_add函数由谁来调用??