linux驱动基础

初始化函数和变量

函数或变量前加关键字:__init

初始化代码。在驱动程序初始化后被抛弃。

函数或变量前加关键字:__exit

退出代码。对于非模块化的驱动程序来说是忽略的。

入口函数和出口函数

使用module_init(xxx)/module_exit(xxx)标记后的函数会被当做驱动程序的入口/出口函数。反汇编后显示为__init/__exit函数。

不要标记pci_driver结构体。

驱动初始化函数

int __pci_register_driver (	struct pci_driver * drv,
 	struct module * owner,
 	const char * mod_name);

其中的mod_name指向一个字符串,作为驱动的内核名称。

struct module结构体较为复杂,可以留待以后查看:

 struct module
    {
        enum module_state state;
        struct list_head list;
        char name[MODULE_NAME_LEN];
 
        struct module_kobject mkobj;
        struct module_param_attrs *param_attrs;
        const char *version;
        const char *srcversion;
 
        const struct kernel_symbol *syms;
        unsigned int num_syms;
        const unsigned long *crcs;
 
        const struct kernel_symbol *gpl_syms;
        unsigned int num_gpl_syms;
        const unsigned long *gpl_crcs;
 
        unsigned int num_exentries;
        const struct exception_table_entry *extable;
 
        int (*init)(void);
        void *module_init;
        void *module_core;
        unsigned long init_size, core_size;
        unsigned long init_text_size, core_text_size;
        struct mod_arch_specific arch;
        int unsafe;
        int license_gplok;
 
#ifdef CONFIG_MODULE_UNLOAD
        struct module_ref ref[NR_CPUS];
        struct list_head modules_which_use_me;
        struct task_struct *waiter;
        void (*exit)(void);
#endif
 
#ifdef CONFIG_KALLSYMS
        Elf_Sym *symtab;
        unsigned long num_symtab;
        char *strtab;
        struct module_sect_attrs *sect_attrs;
#endif
        void *percpu;
        char *args;
    };

关键的pci_driver结构体:

struct hlist_head { 
    struct hlist_node *first;  //指向每一个hash桶的第一个结点的指针
}; 
struct hlist_node { 
    struct hlist_node *next;   //指向下一个结点的指针
    struct hlist_node **pprev; //指向上一个结点的next指针的指针
};

/* pci设备配置寄存器 */
struct pci_device_id {
    /* 厂商id,设备id */
    __u32 vendor, device;        /* Vendor and device ID or PCI_ANY_ID*/
    /* 子系统厂商id,子系统设备id */
    __u32 subvendor, subdevice;    /* Subsystem ID's or PCI_ANY_ID */
    /* 类,类掩码,使驱动程序可以指定支持某一种pci设备 */
    __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */
    /* 保存于设备相关的私有信息 */
    kernel_ulong_t driver_data;    /* Data private to the driver */
};

struct pci_driver {
        struct list_head node;
        const char *name;
        const struct pci_device_id *id_table;   /*不能为 NULL,以便 probe 函数调用*/
        int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);   /* New device inserted */
        void (*remove) (struct pci_dev *dev);   /* Device removed (NULL if not a hot-plug capable driver) */
        int  (*suspend) (struct pci_dev *dev, pm_message_t state);      /* Device suspended */
        int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
        int  (*resume_early) (struct pci_dev *dev);
        int  (*resume) (struct pci_dev *dev);                   /* Device woken up */
        void (*shutdown) (struct pci_dev *dev);
        int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
        const struct pci_error_handlers *err_handler;
        struct device_driver    driver;
        struct pci_dynids dynids;

};

probe(driver_probe)回调函数

上述struct pci_driver结构体中有几个关键的函数,其中包括probe函数。该函数由PCI子系统调用,并对pci设备进行初始化。这一点,跟windows下的WDM驱动程序很相似,wdm驱动往往通过注册AddDevice函数来实现初始化,并将调用交由pnp程序来完成。

函数的原型通常如下:

int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);

pci_dev结构体:

struct pci_dev {
/* 总线设备链表元素bus_list:每一个pci_dev结构除了链接到全局设备链表中外,还会通过这个成员连接到
其所属PCI总线的设备链表中。每一条PCI总线都维护一条它自己的设备链表视图,以便描述所有连接在该
PCI总线上的设备,其表头由PCI总线的pci_bus结构中的 devices成员所描述t*/
struct list_head bus_list;
/* 总线指针bus:指向这个PCI设备所在的PCI总线的pci_bus结构。因此,对于桥设备而言,bus指针将指向
桥设备的主总线(primary bus),也即指向桥设备所在的PCI总线*/
struct pci_bus *bus;
/* 指针subordinate:指向这个PCI设备所桥接的下级总线。这个指针成员仅对桥设备才有意义,而对于一般
的非桥PCI设备而言,该指针成员总是为NULL*/
struct pci_bus *subordinate;
/* 无类型指针sysdata:指向一片特定于系统的扩展数据*/
void *sysdata;
/* 指针procent:指向该PCI设备在/proc文件系统中对应的目录项*/
struct proc_dir_entry *procent;
/* devfn:这个PCI设备的设备功能号,也成为PCI逻辑设备号(0-255)。其中bit[7:3]是物理设备号(取值
范围0-31),bit[2:0]是功能号(取值范围0-7)。 */
unsigned int devfn;
/* vendor:这是一个16无符号整数,表示PCI设备的厂商ID*/
unsigned short vendor;
/*device:这是一个16无符号整数,表示PCI设备的设备ID */
unsigned short device;
/* subsystem_vendor:这是一个16无符号整数,表示PCI设备的子系统厂商ID*/
unsigned short subsystem_vendor;
/* subsystem_device:这是一个16无符号整数,表示PCI设备的子系统设备ID。*/
unsigned short subsystem_device;
/* class:32位的无符号整数,表示该PCI设备的类别,其中,bit[7:0]为编程接口,bit[15:8]为子类
别代码,bit [23:16]为基类别代码,bit[31:24]无意义。显然,class成员的低3字节刚好对应与PCI配
置空间中的类代码*/
unsigned int class;
/* hdr_type:8位符号整数,表示PCI配置空间头部的类型。其中,bit[7]=1表示这是一个多功能设备,
bit[7]=0表示这是一个单功能设备。Bit[6:0]则表示PCI配置空间头部的布局类型,值00h表示这是一
个一般PCI设备的配置空间头部,值01h表示这是一个PCI-to-PCI桥的配置空间头部,值02h表示CardBus桥
的配置空间头部*/
u8 hdr_type;
/* rom_base_reg:8位无符号整数,表示PCI配置空间中的ROM基地址寄存器在PCI配置空间中的位置。
ROM基地址寄存器在不同类型的PCI配置空间头部的位置是不一样的,对于type 0的配置空间布局,ROM基
地址寄存器的起始位置是30h,而对于PCI-to-PCI桥所用的type 1配置空间布局,ROM基地址寄存器的起始
位置是38h*/
u8 rom_base_reg;
/* 指针driver:指向这个PCI设备所对应的驱动程序定义的pci_driver结构。每一个pci设备驱动程序都必须定
义它自己的pci_driver结构来描述它自己。*/
struct pci_driver *driver;
/*dma_mask:用于DMA的总线地址掩码,一般来说,这个成员的值是0xffffffff。数据类型dma_addr_t定义在
include/asm/types.h中,在x86平台上,dma_addr_t类型就是u32类型*/
u64 dma_mask;
/* 当前操作状态 */
pci_power_t  current_state;
/* 通用的设备接口*/
 struct device dev;
/* 无符号的整数irq:表示这个PCI设备通过哪根IRQ输入线产生中断,一般为0-15之间的某个值 */
unsigned int irq;
/*表示该设备可能用到的资源,包括:I/O断口区域、设备内存地址区域以及扩展ROM地址区域。*/
struct resource resource[DEVICE_COUNT_RESOURCE];
/* 配置空间的大小 */
int cfg_size;
/* 透明 PCI 桥 */
unsigned int transparent:1;
/* 多功能设备*/
unsigned int multifunction:1;
/* 设备是主设备*/
unsigned int is_busmaster:1;
/* 设备不使用msi*/
unsigned int no_msi:1;
/* 配置空间访问形式用块的形式 */
unsigned int block_ucfg_access:1;
/* 在挂起时保存配置空间*/
u32 saved_config_space[16];
/* sysfs ROM入口的属性描述*/
struct bin_attribute *rom_attr;
/* 能显示rom 属性*/
int rom_attr_enabled;
/* 资源的sysfs文件*/
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE];

};

struct pci_dev *dev 指向一个struct pci_dev的实例,该实例描述了具体的PCI设备。
const struct pci_device_id *id 指向一个struct pci_device_id的实例,该实例描述了与驱动程序兼容的PCI设备的ID信息。

探测函数的任务通常包括:

设备资源分配。

设备初始化

设备注册

错误处理

如果探测函数没有返回错误,那么PCI子系统会认为该设备与驱动程序成功绑定,设备将被标记为已启用,并且可以在用户空间中被使用。

参考链接:

  1. https://www.kernel.org/doc/html/latest/search.html?
  2. https://os.inf.tu-dresden.de/l4env/doc/html/dde_linux/modules.html
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动是连接硬件和操作系统之间的重要桥梁,负责将硬件设备的功能映射为操作系统所能识别和使用的接口。学习和掌握Linux驱动基础知识对于深入理解和应用Linux操作系统具有重要意义。 首先,了解Linux设备模型是学习Linux驱动基础Linux将硬件设备抽象为设备结构体,并将其组织成设备树形结构。理解设备树的组织方式可以帮助我们理解设备的层级关系,加深对设备驱动的认识。 其次,掌握设备驱动的注册和初始化过程是理解Linux驱动基础的关键。驱动的注册需要通过构造设备结构体并调用相应的函数实现,这样操作系统才能识别和加载驱动。在驱动注册之后,需要进行设备的初始化,包括设置设备所需的资源、注册中断处理函数等。 理解设备操作方法和中断处理是考察Linux驱动基础的重点。设备操作方法包括打开设备、关闭设备、读取设备和写入设备等功能。通过掌握设备操作方法的实现,我们可以对设备进行控制和读写。中断处理是设备与操作系统交互的重要方式,学习中断处理可以帮助我们理解设备与操作系统之间的异步通信机制。 最后,理解设备的文件系统接口也是考察Linux驱动基础的重要方面。Linux将设备抽象成文件,并通过文件系统接口来进行设备的读写操作。掌握设备文件的创建和管理,了解如何通过文件系统接口进行设备的读写,对于理解驱动的应用场景以及与用户空间的交互具有关键作用。 总之,考察Linux驱动基础需要理解Linux设备模型、设备驱动的注册和初始化过程、设备操作方法和中断处理、设备的文件系统接口等多个方面的知识。只有全面掌握这些基础知识,才能深入理解和应用Linux驱动的原理和技术。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值