您的位置:首页 > 其它

平台总线、设备、驱动的学习

2016-08-17 21:39 176 查看
设备和驱动的绑定是通过总线实现的。总线通过查找总线下的设备列表和驱动列表,名字相同的进行匹配。

总线代码的实现:

struct bus_type {

       constchar             *name;     //总线名字

       structbus_attribute      *bus_attrs;  //总线属性

       structdevice_attribute  *dev_attrs;     //设备属性

       structdriver_attribute   *drv_attrs;      //驱动属性

 

       int(*match)(struct device *dev, struct device_driver *drv); //匹配总线下的dev(设备)和drv(驱动)

       int(*uevent)(struct device *dev, struct kobj_uevent_env *env);//用于总线环境变量添加

       int(*probe)(struct device *dev); //回调函数,总线和驱动匹配成功就会调用此函数。

      

int(*remove)(struct device *dev);

       void(*shutdown)(struct device *dev);

 

       int(*suspend)(struct device *dev, pm_message_t state);

       int(*resume)(struct device *dev);

 

       conststruct dev_pm_ops *pm;    电源管理

 

       structbus_type_private *p;         这个指针将bus同其他类型联系起来。将bus、device、sysfs三者联系起来。 Bus的一些私有成员、数据

};

structbus_type_private {

       structkset subsys; 

       structkset *drivers_kset;      表示总线目录下的驱动的子目录

       structkset *devices_kset;     表示总线目录下的设备的子目录

       structklist klist_devices;              表示总线下的设备的链表

       structklist klist_drivers;        表示总线下的驱动的链表(总线通过链表查找驱动和设备)

       structblocking_notifier_head bus_notifier;

       unsignedint drivers_autoprobe:1;

       structbus_type *bus;

};

 

设备启动时候初始化过程:(main.c(init))

1、 kernel_init()  2、do_basic_setup ()3、driver_init() 4、devices_init 5、buses_init 。。。

 

在driver_init()中驱动没有注册。驱动的注册时在驱动函数中的moudle_init中注册。在do_initcalls() 中才开始加载module_init。

 

分析总线的注册:

1、平台总线如何注册?

Uboot-----加载linux内核----从main.c(init)中执行start_kernel----rest_init()一些复位初始化; 开始创建kernel_init线程(kernel_thread(kernel_init,NULL,…)-------然后do_basic_setup基本安装-----driver_init()----先后执行buses_init和platform_bus_init

2、buses_init()进一步分析:



3、platform_bus_init()

error = bus_register(&platform_bus_type); 进行platform总线的注册。

在bus_register(&platform_bus_type);中:

kobject_set_name(&priv->subsys.kobj,"%s", bus->name); 设置bus的名字。

priv->subsys.kobj.kset = bus_kset; 此处理解为bus_kset指向platform上一层的目录,

因为在buses_init中,bus_kset = kset_create_and_add("bus", &bus_uevent_ops,NULL);
所以bus_kset指向了bus目录,所以platform目录是在bus目录下创建的。

 

priv->drivers_autoprobe = 1;设置标志位,后面会用到,当驱动注册时,会自动匹配设备,初始化probe函数。

 

retval =kset_register(&priv->subsys); 注册kset,创建目录。在此处platform_bus_init()

是创建一个platform目录。

 

bus_create_file(bus, &bus_attr_uevent);在当前总线(此处对应platform总线)目录下生成bus_attr_uevent属性,包括读写操作。

 

kset_create_and_add("devices",NULL,&priv->subsys.kobj);创建devices链表。

 

kset_create_and_add("drivers",NULL,&priv->subsys.kobj);创建驱动链表。

 

 

平台设备的注册(platform_devices)

1、 平台设备是如何注册?如何实现?

如果在devices/platform/创建了设备,那么它在平台总线上肯定有对应的、一样的设备。用为我们的平台设备总是挂载到平台总线上的。如图:

系统初始化的时候会调用mini2440_machine_init会调用platform_add_devices()将mini2440_devices这些设备注册到linux系统里面。

staticstruct platform_device *mini2440_devices[] __initdata = {

&s3c_device_usb,

&s3c_device_rtc,

&s3c_device_lcd,

&s3c_device_wdt,

&s3c_device_i2c0,

&s3c_device_iis,

&mini2440_device_eth,

&s3c24xx_uda134x,

&s3c_device_nand,

&s3c_device_sdi,

&s3c_device_usbgadget,

};

 

在platfrom_add_devices中:

1、platform_device_register()

第一步device_initialize(&pdev->dev);  //初始化。



其中dev->kobj.kset = devices_kset;中的devices_kset来自于devices_init 函数中

第二步:return platform_device_add(pdev);

Platform_device 结构体

struct platform_device {

  const char      * name;

  int          id;

  struct device  dev;

  u32         num_resources;

  struct resource      *resource;

  struct platform_device_id     *id_entry;

  struct pdev_archdata    archdata;

};

部分device结构的成员:

struct device{

  struct device         *parent; //设备的父设备,该设备所属的设备,通常是某种总线或者其他宿主控制器

  struct device_private     *p;

  struct kobject kobj;

  const char             *init_name;   设备名字

  struct bus_type     *bus;      标识该设备连接在何种总线上

  struct device_driver *driver; 管理该设备的驱动

  void        *platform_data;    

  void (*release)(struct device*dev);

};

在platform_device_add(structplatform_device *pdev)中首先进行一些初始化

(1)     pdev->dev.parent =&platform_bus; 设置设备的父节点

(2)     pdev->dev.bus =&platform_bus_type;设置设备挂载的总线类型

(3)     dev_set_name(&pdev->dev,"%s.%d", pdev->name, pdev->id);设置设备的名字

(4)     设置设备的资源。

(5)    ret =device_add(&pdev->dev);在这里才是真的开始添加设备,之前都是一些结构体的初始化

平台驱动的注册(platform_driver):

platform_device_resigter设备注册和platform_driver_resigter驱动注册的区别?

 

USB设备和USB驱动的注册过程:

1、 USB设备插进电脑中,开始运行我的USB驱动程序,安装USB驱动时候就会开始“遍历”USB总线上的所有设备与驱动匹配。(设备先注册,驱动后注册)。

2、 U盘的驱动程序先安装好,每次U盘设备插到电脑,设备被识别。在这里(先注册驱动,设备去找驱动)

3、 驱动注册和设备注册没有绝对的先后顺序。

平台驱动的注册过程(以声卡s3c24xx-uda134x)

Uboot—do_initcall—module_init()—platform_driver_resigter()

进入platform_driver_resigter()中对device_driver 结构体的初始化:



struct platform_driver {

int (*probe)(struct platform_device *);

int (*remove)(struct platform_device *);

void (*shutdown)(struct platform_device *);

int (*suspend)(struct platform_device *,pm_message_t state);

int (*resume)(struct platform_device *);

struct device_driver driver;

struct platform_device_id *id_table;

};

 

struct device_driver {

const char             *name;  //名字

struct bus_type            *bus;  //所挂的总线

struct module       *owner;

const char             *mod_name; 

bool suppress_bind_attrs;   

int (*probe) (struct device *dev);

int (*remove) (s
acdc
truct device *dev);

void (*shutdown) (struct device *dev);

int (*suspend) (struct device *dev,pm_message_t state);

int (*resume) (struct device *dev);

const struct attribute_group **groups;

const struct dev_pm_ops *pm;

struct driver_private *p;

}

 

进入driver_register(&drv->driver):

 

other = driver_find(drv->name,drv->bus); 驱动在/sys/bus/platform/drivers列表查找或者遍历是否有相同驱动名字,如果有,就不会注册这个驱动。

 

ret =bus_add_driver(drv); 添加驱动

在bus_add_driver中:

其中drv->bus->p->drivers_autoprobe的赋值在总线注册.的时候就赋值为1 。

然后进入driver_attach中进行驱动绑定。

 

在bus_for_each_dev中,执行error = fn(dev, data);将dev,data的值传递给函数driver_attach;

 

static int__driver_attach(struct device *dev, void *data)

{

struct device_driver *drv = data;

if (!driver_match_device(drv, dev))  //在平台总线下是调用platform_match通过名字进行匹配

        return 0;

if (dev->parent)    /* Needed for USB */

        down(&dev->parent->sem);

down(&dev->sem);

if (!dev->driver)

        driver_probe_device(drv, dev);  //因为设备中没有probe函数所以调用驱动里的probe函数

up(&dev->sem);

if (dev->parent)

        up(&dev->parent->sem);

 

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: