linux驱动开发之platform总线

1.什么是platform总线
我们先来思考这样一个问题,当我们把usb设备插到电脑上时,电脑是如何识别到这个usb设备的?其实,每一个usb中都有一个vid(厂商id)和pid(设备id),vid和pid在usb的生产过程中就已经设置好了。同时,对于一个usb驱动,在驱动里也保存着这个pid和vid,当usb设备中的vid和pid是与usb驱动中的vid和pid相同时,驱动便可以识别到这个设备。这是因为在linux内核中维护者两条链表,一个是设备链表,另一个是驱动链表,linux内核会把设备加到设备链表,把驱动加到驱动链表,然后一个设备到来时,就在去驱动链里找这个设备所对应的驱动。但是,对于led设备,并没有像usb设备一样,有这种设备链和驱动链,所以,我们需要虚拟出一条总线,这条总线负责让设备找到驱动,驱动找到设备,我们把这样的总线称为platform总线。
现在,我们就可以把驱动和设备分离,这样做的好处在与驱动是通用的,一个驱动可以支持多个设备,而如果将驱动和设备写在一起,则这个驱动就只能支持这个设备,当设备改变了,驱动也要改变。
对于platform总线下的设备和驱动,设备和驱动就通过name域来找到对方,设备的name域如果和驱动的name域相同,这个设备和这个驱动就是相互对应的。

设备中的name域:

static struct platform_device s3c_led_device = {
 71     .name    = "s3c_led",
 72     .id      = 1,
 73     .dev     =
 74     {
 75         .platform_data = &s3c_led_data,
 76         .release = platform_led_release,
 77     },
 78 };

驱动中的name域:

static struct platform_driver s3c_led_driver = {
    .probe      = s3c_led_probe,
    .remove     = s3c_led_remove,
    .driver     = {
        .name       = "s3c_led",
        .owner      = THIS_MODULE,
    },
};

2.设备端完成哪些工作
现在,设备和驱动是分离的,所以,需要由设备端向platform虚拟总线告知一些关于设备端的信息。例如,对于led设备,其需要告知的信息是:led设备用到的那几个引脚,如果要点亮led,需要给高电平还是低电平,需要设置成输入模式还是输出模式,以及该设备的name域是多少,这些信息都是需要在设备端完成的,
platform_device_register函数用来将设备注册到platform总线。其函数原型如下:

int platform_device_register(struct platform_device *pdev)

其参数是一个指向platform_device 类型的结构体指针,我们可以将该结构体指针指向包含的设备信息的结构体,从而完成了设备的注册。
下面是led设备的注册代码:

static int __init platdev_led_init(void)
{
   int       rv = 0;

   rv = platform_device_register(&s3c_led_device);
   if(rv)
   {   
        printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__,
rv); 
        return rv; 
   }   
   printk("Regist S3C LED Platform Device successfully.\n");

   return 0;
}

s3c_led_device结构体包含设备信息:

static struct platform_device s3c_led_device = {
    .name    = "s3c_led",
    .id      = 1,
    .dev     =
    {
        .platform_data = &s3c_led_data,
        .release = platform_led_release,
    },
};

于是通过s3c_led_device结构体就将设备信息注册到了platform虚拟总线上。platform_device 结构体的定义如下:

struct platform_device {
const char * name; //设备名称
int id;            //设备id
struct device dev;
u32 num_resources; //设备使用各类资源的数量
struct resource * resource; //设备使用的资源


struct platform_device_id *id_entry;
};

其中struct device 结构体的定义如下:

struct device {
    struct device       *parent;


    struct device_private   *p;


    struct kobject kobj;
    const char      *init_name; /* initial name of the device */
    const struct device_type *type;


    struct mutex        mutex;  /* mutex to synchronize calls to
                     * its driver.
                     */


    struct bus_type *bus;       /* type of bus device is on */
    struct device_driver *driver;   /* which driver has allocated this
                       device */
    void        *platform_data; /* Platform specific data, device
                       core doesn't touch it */
    struct dev_pm_info  power;
struct dev_power_domain *pwr_domain;


#ifdef CONFIG_NUMA
    int     numa_node;  /* NUMA node this device is close to */
#endif
    u64     *dma_mask;  /* dma mask (if dma'able device) */
    u64     coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */


    struct device_dma_parameters *dma_parms;


    struct list_head    dma_pools;  /* dma pools (if dma'ble) */


    struct dma_coherent_mem *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */

    struct dev_archdata archdata;


    struct device_node  *of_node; /* associated device tree node */


    dev_t           devt;   /* dev_t, creates the sysfs "dev" */


    spinlock_t      devres_lock;
    struct list_head    devres_head;


    struct klist_node   knode_class;
    struct class        *class;
    const struct attribute_group **groups;  /* optional groups */


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

在device结构体的platform_data成员中保存着设备的特定信息,对于以上讲到的led设备,我们关心以下的这些信息:

static struct s3c_led_platform_data s3c_led_data = {
    .leds = s3c_leds,
    .nleds = ARRAY_SIZE(s3c_leds),
};

其中s3c_led_platform_data是我们自己定义的结构体,结构体保存着这个led的特定信息。
通过以上这些步骤,设备端的信息基本描述清楚了。

3.驱动端需要完成的工作:
在驱动端,主要完成下面的工作:
1)调用module_init();当我们insmod时,就将从module_init()开始。
2)调用platform_driver_register(),将驱动注册到platform虚拟总线。

static int __init platdrv_led_init(void)
{
   int       rv = 0;

   rv = platform_driver_register(&s3c_led_driver);
   if(rv)
   {
        printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__,
rv);
        return rv;
   }
   printk("Regist S3C LED Platform Driver successfully.\n");

   return 0;
}

3)platform_driver_register的参数是一个platform_driver类型的结构体指针,如果是insmod 一个模块,将会调用probe函数;如果是rmmod一个模块,将调用remove函数。
platform_driver结构体:

static struct platform_driver s3c_led_driver = {
    .probe      = s3c_led_probe,
    .remove     = s3c_led_remove,
    .driver     = {
        .name       = "s3c_led",
        .owner      = THIS_MODULE,
    },
};

4)probe函数的参数以一个platform_device的结构体指针,该结构体指针指向设备端的设备的信息。在probe函数中完成:设备硬件的初始化;分配主次设备号;申请cdev结构体;cdev结构体的初始化;将cdev结构体与设备号绑定并将cdev结构体注册到内核。cdev结构体是linux驱动中的一种关键数据结构,在cdev结构体的初始化过程中,会把file_operations 结构体指向cdev结构体的ops成员。
在remove函数中,需要将在probe函数中申请到的资源释放,但是需要说明的是,remove函数是在rmmod一个模块时才调用的。
5)如果想在驱动中完成在/dev路径下创建设备节点,可以调用class_create函数和device_create函数。当我们调用device_create函数时,就会在内核里产生一个hotplug事件,内核本身不会处理这个hotplug事件,他会通知应用程序空间来处理这个hotplug事件。linux内核启动后,挂载根文件系统,根文件系统启动后,就执行init进程,然后管理权就交给了init进程,init进程会读/etc/inittab这个脚本,在inittab脚本文件中指定了系统启动时应该完成哪些事情。

#Use mdev to auto generate device nod and auto mount SD card and USB storage
::sysinit:/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug  
::sysinit:/sbin/mdev -s

proc文件系统是linux内核和应用程序空间进行信息传递的桥梁,就通过proc文件告诉应用程序。把/sbin/mdev写到/proc/sys/kernel/hotplug中,一旦产生一个hotplug事件,linux内核通知应用程序空间,调用/proc/sys/kernel/hotplug文件里所指定内容的程序,/sbin/medv 程序默认扫描所有的hotplug事件,创建设备节点。

4.设备和驱动如何找到对方:
我们知道,设备有自己的一条链,驱动有自己的一条链,而设备和驱动能够找到对方是借助操作系统来实现的,设备和驱动是通过name域来识别的,当有新的设备连接时,操作系统就会到驱动一端找是否有该设备对应的驱动;当有新的驱动时,操作系统会到设备端找是否有该驱动对应的设备。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值