第6章 设备驱动程序(5)

目录

6.6 资源分配

6.6.1 资源管理

6.6.2 I/O 内存

6.6.3 I/O端口

6.7 总线系统

6.7.1 通用驱动程序模型


本专栏文章将有70篇左右,欢迎+关注,查看后续文章。

6.6 资源分配

6.6.1 资源管理

1. 树数据结构

内核所有资源以树形式组织。

        树根节点表示整个系统资源。

每个设备有自己的资源子树,表示该设备资源和子设备的资源。

资源类型有:

        IORESOURCE_IO

        IORESOURCE_MEM

        IORESOURCE_REG

        IORESOURCE_IRQ

        IORESOURCE_DMA

        IORESOURCE_BUS

资源的结构体:

struct         resource {

        resource_size_t       start;

        resource_size_t       end;

        char                         *name;

        unsigned long          flags;

        struct resource         *parent, *sibling, *child;         // 用于组织树。

};

2. 请求和释放资源

请求、释放资源:

        即向资源树中添加、删除项。

请求资源

struct resource    * __request_resource(struct resource    *root,     struct resource    *new)

        扫描资源树,将资源添加到以root为根节点的树中合适位置。

如请求一段内存、一个中断、一个 I/O 端口等资源。

释放资源

int     release_resource(struct resource     *old)

6.6.2 I/O 内存

物理地址的大部分给系统内存,其余部分给其他设备,如显存。

IO端口:用于X86。

IO内存:用于非X86。

查看系统IO内存信息:

        cat /proc/iomem

通过resource的parent sibling child成员,形成资源树。

全局变量iomem_resource:

        系统所有IO内存的资源树。

struct resource     iomem_resource     =     {

        .name         =        "PCI mem",

        .start          =         0,

        .end           =         -1,

        .flags         =         IORESOURCE_MEM,

};

void __iomem      *ioremap(phys_addr_t      offset,      size_t      size)

        将外设的物理地址映射为内核的虚拟地址。

        实现方法:修改页表。

访问IO内存区域的函数:

        readb(addr)

        readw(addr)

        readl(addr)

        writeb(addr)

        writew(addr)

        writel(addr)

        memcpy_fromio(dest, src, sum)

        memcpy_toio(dest, src, num);

6.6.3 I/O端口

I/O端口:适用于x86。

访问区域之前先注册。

ioport_resource:系统IO端口资源树的根结点。

struct resource      ioport_resource = {

        .name    =    "PCI IO",

        .start    =    0,

        .end    =    IO_SPACE_LIMIT,

        .flags    =   IORESOURCE_IO,

};

查看系统IO端口:

        cat /proc/ioports

一个设备可同时映射到IO端口,IO内存。

访问IO端口函数

insb(port, addr, num)

insl(port, addr, num)

insw(port, addr, num)

        从端口port读(字节、字、长整型)数据到addr。

outsb(port, addr, num)

outsl(port, addr, num)

outsw(port, addr, num)

        从addr向端口port写数据(字节、字、长整型)。

6.7 总线系统

6.7.1 通用驱动程序模型

  1. 设备的表示

struct    device {

        struct kobject         kobj;

        struct device         *parent; //父设备

        struct device_private         *p;

        struct device_type              *type;

        struct klist_node                 knode_class;

        struct bus_type                   *bus;         //所在总线

        struct device_driver            *driver;

        struct class                         *class;

        void                                     *platform_data;         //通常为BSP信息

        void                                     *driver_data;            //驱动程序的私有数据。

        dev_t                                  devt;

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

};

注册设备:

        int         device_register(struct device     *dev)

通用的驱动程序模型:

struct     device_driver {

        const char                 *name;         // 驱动程序名称

        struct bus_type         *bus;

        struct module            *owner;

        const struct of_device_id         *of_match_table;

        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);

                // 这三个函数用于电源管理。

        struct driver_private         *p;

};

驱动程序注册:

        int     driver_register(struct device_driver    *drv)

2. 总线的表示

总线作用:

        将device和device_driver两者关联起来。

struct         bus_type {

        const char         *name;

        const char         *dev_name;

        struct device      *dev_root;

        int         (*match)(struct device    *dev,    struct device_driver     *drv);

                // 用于寻找设备与设备驱动,找到后调用device_driver的probe

        int         (*uevent)(struct device     *dev,    struct kobj_uevent_env    *env);

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

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

                //热插拔 或 删除device 和driver联系

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

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

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

                //电源管理

};

3. 注册过程

下面讲总线,设备,驱动三者注册过程。

注册总线

以PCI为例

struct bus_type         pci_bus_type = {

        .name         =         "pci",

        .match        =         pci_bus_match,

        .uevent       =         pci_uevent,

        .probe         =         pci_device_probe,

        .remove      =         pci_device_remove,

        .shutdown   =         pci_device_shutdown,

};

bus_register(&pci_bus_type);

int    bus_register(struct bus_type     *bus)

{

        kobject_set_name(&priv->subsys.kobj,     "%s",    bus->name);

               

        kset_register(&bus->devices);

        kset_register(&bus->drivers);

                 //注册device的kset和drivers的kset。

}

注册设备

int     device_register(struct device     *dev)

{

        device_initialize(dev);

        device_add(dev);

}

void     device_initialize(struct device     *dev)

{

        dev->kobj.kset     =     devices_kset;

        kobject_init(&dev->kobj,     &device_ktype);

}

int     device_add(struct device     *dev)

{

        parent    =     get_device(dev->parent);

        kobj_parent    =    get_device_parent(dev, parent);

        dev->kobj.parent      =     kobj_parent;

        kobject_add(&dev->kobj,      dev->kobj.parent,     NULL);

        bus_add_device(dev);

                //在sysyfs总线目录下添加设备

                //在sysfs设备中添加链接指向总线。

}

注册设备驱动程序

int     driver_register(struct device_driver     *drv)

{

        struct device_driver     *other;

        other     =     driver_find(drv->name,     drv->bus);

        if (other) {

                printk(KERN_ERR "Error: Driver '%s' is already registered, aborting...\n",    drv->name);

                return     -EBUSY;

        }

        bus_add_driver(drv);

        kobject_uevent(&drv->p->kobj,     KOBJ_ADD);

                //触发uevent事件,用于热插拔

}

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值