当前位置:文档之家› Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)

Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)

Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)
Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、驱动程序和类)

Linux设备驱动程序学习(13)-Linux设备模型(总线、设备、

驱动程序和类)

Linux设备驱动程序学习(13)

-Linux设备模型(总线、设备、驱动程序和类)

文章的例子和实验使用《LDD3》所配的lddbus模块(稍作修改)。

提示:在学习这部分内容是一定要分析所有介绍的源代码,知道他们与上一部分内容(kobject、kset、attribute等等)的关系,最好要分析一个实际的“flatform device”设备,不然会只学到表象,到后面会不知所云的。

总线

总线是处理器和一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟"platform"总线。总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。

在Linux 设备模型中, 总线由bus_type 结构表示, 定义在

struct blocking_notifier_head bus_notifier;

struct bus_attribute * bus_attrs;/*总线属性*/

struct device_attribute * dev_attrs;/*设备属性,指向为每个加入总线的设备建立的默认属性链表*/

struct driver_attribute * drv_attrs;/*驱动程序属性*/

struct bus_attribute drivers_autoprobe_attr;/*驱动自动探测属性*/

struct bus_attribute drivers_probe_attr;/*驱动探测属性*/

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

int(*uevent)(struct device *dev,char**envp,

int num_envp,char*buffer,int buffer_size);

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(*suspend_late)(struct device * dev, pm_message_t state);

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

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

/*处理热插拔、电源管理、探测和移除等事件的方法*/

unsigned int drivers_autoprobe:1;

};

在更新的内核里,这个结构体变得更简洁了,隐藏了无需驱动编程人员知道的一些成员:

/*in Linux 2.6.26.5*/

struct bus_type {

const char*name;

struct bus_attribute *bus_attrs;

struct device_attribute *dev_attrs;

struct driver_attribute *drv_attrs;

int(*match)(struct device *dev,struct device_driver *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(*suspend_late)(struct device *dev, pm_message_t state);

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

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

struct bus_type_private *p;

};

struct bus_type_private {

struct kset subsys;

struct kset *drivers_kset;

struct kset *devices_kset;

struct klist klist_devices;

struct klist klist_drivers;

struct blocking_notifier_head bus_notifier;

unsigned int drivers_autoprobe:1;

struct bus_type *bus;

};

总线的注册和删除

总线的主要注册步骤:

(1)申明和初始化bus_type 结构体。只有很少的bus_type 成员需要初始化,大部分都由设备模型核心控制。但必须为总线指定名字及一些必要的方法。例如:

(2)调用bus_register函数注册总线。

调用可能失败, 所以必须始终检查返回值。若成功,新的总线子系统将被添加进系统,并可在 sysfs 的 /sys/bus 下看到。之后可以向总线添加设备。例如:

当必须从系统中删除一个总线时, 调用:

总线方法

在bus_type 结构中定义了许多方法,它们允许总线核心作为设备核心和单独的驱动程序之间提供服务的中介,主要介绍以下两个方法:

lddbus的match和uevent方法:

对设备和驱动的迭代

若要编写总线层代码, 可能不得不对所有已经注册到总线的设备或驱动进行一些操作,这可能需要仔细研究嵌入到bus_type 结构中的其他数据结构, 但最好使用内核提供的辅助函数:

总线属性

几乎Linux 设备模型中的每一层都提供添加属性的函数, 总线层也不例外。bus_attribute 类型定义在如下:

可以看出struct bus_attribute 和struct attribute 很相似,其实大部分在kobject 级上的设备模型层都是以这种方式工作。

内核提供了一个宏在编译时创建和初始化bus_attribute 结构:

例如创建一个包含源码版本号简单属性文件方法如下:

设备

在最底层, Linux 系统中的每个设备由一个struct device 代表:

功能号.*/

struct device_type *type;

unsigned is_registered:1;

unsigned uevent_suppress:1;

struct device_attribute uevent_attr;

struct device_attribute *devt_attr;

struct semaphore sem;/* semaphore to synchronize calls to its driver. */

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

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

void*driver_data;/*该设备驱动使用的私有数据成员

*/

void*platform_data;/* Platform specific data, device core doesn't touch it */

struct dev_pm_info power;

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

spinlock_t devres_lock;

struct list_head devres_head;

/* class_device migration path */

struct list_head node;

struct class*class;

dev_t devt;/* dev_t, creates the sysfs "dev" */ struct attribute_group **groups;/* optional groups */

void(*release)(struct device * dev);/*当这个设备的最后引用被删除时,内核调用该方法; 它从被嵌入的 kobject 的 release 方法中调用。所有注册到核心的设备结构必须有一个 release 方法, 否则内核将打印错误信息*/

};

/*在注册 struct device 前,最少要设置parent, bus_id, bus, 和release 成员*/

设备注册

设备的注册和注销函数为:

一个实际的总线也是一个设备,所以必须单独注册,以下为lddbus 在编译时注册它的虚拟总线设备源码:

设备属性

sysfs 中的设备入口可有属性,相关的结构是:

设备结构的嵌入

device 结构包含设备模型核心用来模拟系统的信息。但大部分子系统记录了关于它们又拥有的设备的额外信息,所以很少单纯用device 结构代表设备,而是,通常将其嵌入一个设备的高层表示中。底层驱动几乎不知道struct device。

lddbus 驱动创建了它自己的device 类型,并期望每个设备驱动使用这个类型来注册它们的设备:

lddbus 导出的注册和注销接口如下:

/*

* For now, no references to LDDbus devices go out which are not * tracked via the module reference count, so we use a no-op

* release function.

*/

static void ldd_dev_release(struct device *dev)

{}

int register_ldd_device(struct ldd_device *ldddev)

{

ldddev->dev.bus =&ldd_bus_type;

ldddev->dev.parent =&ldd_bus;

ldddev->dev.release = ldd_dev_release;

strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);

return device_register(&ldddev->dev);

}

EXPORT_SYMBOL(register_ldd_device);

void unregister_ldd_device(struct ldd_device *ldddev)

{

device_unregister(&ldddev->dev);

}

EXPORT_SYMBOL(unregister_ldd_device);

sculld 驱动添加一个自己的属性到它的设备入口,称为dev, 仅包含关联的设备号,源码如下:

{

struct sculld_dev *dev = ddev->driver_data;

return print_dev_t(buf, dev->cdev.dev);

}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev,NULL);

/*接着, 在初始化时间, 设备被注册, 并且 dev 属性通过下面的函数被

创建:*/

static void sculld_register_dev(struct sculld_dev *dev,int index)

{

sprintf(dev->devname,"sculld%d", index);

dev->https://www.doczj.com/doc/1a11849045.html, = dev->devname;

dev->ldev.driver =&sculld_driver;

dev->ldev.dev.driver_data = dev;

register_ldd_device(&dev->ldev);

if (device_create_file(&dev->ldev.dev, &dev_attr_dev))

printk( "Unable to create dev attribute ! \n");

}/*注意:程序使用 driver_data 成员来存储指向我们自己的内部的设备

结构的指针。请检查device_create_file的返回值,否则编译时会有警告。

*/

设备驱动程序

设备模型跟踪所有系统已知的驱动,主要目的是使驱动程序核心能协调驱动和新设备之间的关系。一旦驱动在系统中是已知的对象就可能完成大量的工作。驱动程序的结构体device_driver 定义如下:

现 )*/

struct bus_type * bus;/*驱动程序所操作的总线类型*/

struct kobject kobj;/*内嵌的kobject对象*/

struct klist klist_devices;/*当前驱动程序能操作的设备链表*/

struct klist_node knode_bus;

struct module * owner;

const char* mod_name;/* used for built-in modules */

struct module_kobject * mkobj;

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

};

/*注册device_driver 结构的函数是:*/

int driver_register(struct device_driver *drv);

void driver_unregister(struct device_driver *drv);

/*driver的属性结构在:*/

struct driver_attribute {

struct attribute attr;

在更新的内核里,这个结构体变得更简洁了,隐藏了无需驱动编程人员知道的一些成员:

驱动程序结构的嵌入

对大多数驱动程序核心结构, device_driver 结构通常被嵌入到一个更高层的、总线相关的结构中。以lddbus 子系统为例,它定义了ldd_driver 结构:

lddbus总线中相关的驱动注册和注销函数是:

sprintf(buf,"%s\n", ldriver->version);

return strlen(buf);

}

int register_ldd_driver(struct ldd_driver *driver)

{

int ret;

driver->driver.bus =&ldd_bus_type;

ret = driver_register(&driver->driver);/*注册底层的

device_driver 结构到核心*/

if(ret)

return ret;

driver->version_https://www.doczj.com/doc/1a11849045.html, ="version";/* driver_attribute 结构必须手工填充*/

driver->version_attr.attr.owner = driver->module;/*注意:设定version 属性的拥有者为驱动模块, 不是 lddbus 模块!因为

show_version 函数是使用驱动模块所创建的 ldd_driver 结构,若

ldd_driver 结构在一个用户空间进程试图读取版本号时已经注销,就会出错*/

driver->version_attr.attr.mode = S_IRUGO;

driver->version_attr.show = show_version;

driver->version_attr.store =NULL;

return driver_create_file(&driver->driver,

&driver->version_attr);/*建立版本属性,因为这个属性在运行时被创建,所以不能使用 DRIVER_ATTR 宏*/

}

void unregister_ldd_driver(struct ldd_driver *driver)

{

在sculld 中创建的ldd_driver 结构如下:

类子系统

类是一个设备的高层视图, 它抽象出了底层的实现细节,从而允许用户空间使用设备所提供的功能, 而不用关心设备是如何连接和工作的。类成员通常由上层代码所控制, 而无需驱动的明确支持。但有些情况下驱动也需要直接处理类。

几乎所有的类都显示在/sys/class 目录中。出于历史的原因,有一个例外:块设备显示在/sys/block目录中。在许多情况, 类子系统是向用户空间导出信息的最好方法。当类子系统创建一个类时, 它将完全拥有这个类,根本不用担心哪个模块拥有那些属性,而且信息的表示也比较友好。

为了管理类,驱动程序核心导出了一些接口,其目的之一是提供包含设备号的属性以便自动创建设备节点,所以udev的使用离不开类。类函数和结构与设备模型的其他部分遵循相同的模式,所以真正崭新的概念是很少的。

注意:class_simple 是老接口,在2.6.13中已被删除,这里不再研究。

管理类的接口

类由struct class 的结构体来定义:

* device classes

*/

struct class{

const char* name;/*每个类需要一个唯一的名字, 它将显示在 /sys/class 中*/

struct module * owner;

struct kset subsys;

struct list_head children;

struct list_head devices;

struct list_head interfaces;

struct kset class_dirs;

struct semaphore sem;/* locks both the children and interfaces lists */

struct class_attribute * class_attrs;/* 指向类属性的指针(以NULL结尾) */

struct class_device_attribute * class_dev_attrs;/* 指向类中每个设备的一组默认属性的指针 */

struct device_attribute * dev_attrs;

int(*uevent)(struct class_device *dev,char**envp,

int num_envp,char*buffer,int buffer_size);/* 类热插拔产生时添加环境变量的函数 */

int(*dev_uevent)(struct device *dev,char**envp,int num_envp,

char*buffer,int buffer_size);/* 类中的设备热插拔时添加环境变量的函数 */

void(*release)(struct class_device *dev);/* 把设备从类中删除的函数 */

void(*class_release)(struct class*class);/* 删除类本身的函数 */

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

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

int(*resume)(struct device *);

};

/*类注册函数:*/

int class_register(struct class*cls);

void class_unregister(struct class*cls);

/*类属性的接口:*/

struct class_attribute {

struct attribute attr;

ssize_t (*show)(struct class*cls,char*buf);

ssize_t (*store)(struct class*cls,const char*buf,size_t count); };

CLASS_ATTR(_name,_mode,_show,_store);

int class_create_file(struct class*cls,const struct

class_attribute *attr);

void class_remove_file(struct class*cls,const struct

class_attribute *attr);

在更新的内核里,这个结构体变得简洁了,删除了一些成员:

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

linux驱动工程师面试题整理

1、字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件。 评:这只是其中一种方式,也叫手动创建设备文件。还有UDEV/MDEV自动创建设备文件的方式,UDEV/MDEV是运行在用户态的程序,可以动态管理设备文件,包括创建和删除设备文件,运行在用户态意味着系统要运行之后。那么在系统启动期间还有devfs创建了设备文件。一共有三种方式可以创建设备文件。 2、写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?答:中断处理例程应该尽量短,把能放在后半段(tasklet,等待队列等)的任务尽量放在后半段。 评:写一个中断服务程序要注意快进快出,在中断服务程序里面尽量快速采集信息,包括硬件信息,然后推出中断,要做其它事情可以使用工作队列或者tasklet方式。也就是中断上半部和下半部。 第二:中断服务程序中不能有阻塞操作。为什么?大家可以讨论。 第三:中断服务程序注意返回值,要用操作系统定义的宏做为返回值,而不是自己定义的OK,FAIL之类的。 3、自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么? 答:使用自旋锁的进程不能睡眠,使用信号量的进程可以睡眠。中断服务例程中的互斥使用的是自旋锁,原因是在中断处理例程中,硬中断是关闭的,这样会丢失可能到来的中断。 4、原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行? 答:原子操作指的是无法被打断的操作。我没懂第二句是什么意思,自己定义一个变量怎么可能标记资源的使用情况?其他进程又看不见这个变量 评:第二句话的意思是: 定义一个变量,比如 int flag =0; if(flag == 0) { flag = 1; 操作临界区; flag = 0; }这样可否?

Linux设备驱动模型之platform总线深入浅出

Linux设备驱动模型之platform总线深入浅出 在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中 platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver //include\linux\platform_device.h 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 (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢复函数,在开机时被调用struct device_driver driver;//设备驱动结构}; 1 2 3 4 5 6 7 8

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

如何实现Linux设备驱动模型

文库资料?2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd. 如何实现Linux 设备驱动模型 设备驱动模型,对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了device 设备、driver 驱动、bus 总线和class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。总线、设备、驱动以及类之间的关系错综复杂,在Linux 内核中通过kobject 、kset 和subsys 来进行管理,驱动编写可以忽略这些管理机制的具体实现。 设备驱动模型的内部结构还在不停的发生改变,如device 、driver 、bus 等数据结构在不同版本都有差异,但是基于设备驱动模型编程的结构基本还是统一的。 Linux 设备驱动模型是Linux 驱动编程的高级内容,这一节只对device 、driver 等这些基本概念作介绍,便于阅读和理解内核中的代码。实际上,具体驱动也不会孤立的使用这些概念,这些概念都融合在更高层的驱动子系统中。对于大多数读者可以忽略这一节内容。 1.1.1 设备 在Linux 设备驱动模型中,底层用device 结构来描述所管理的设备。device 结构在文件中定义,如程序清单错误!文档中没有指定样式的文字。.1所示。 程序清单错误!文档中没有指定样式的文字。.1 device 数据结构定义 struct device { struct device *parent; /* 父设备 */ struct device_private *p; /* 设备的私有数据 */ struct kobject kobj; /* 设备的kobject 对象 */ const char *init_name; /*设备的初始名字 */ struct device_type *type; /* 设备类型 */ struct mutex mutex; /*同步驱动的互斥信号量 */ struct bus_type *bus; /*设备所在的总线类型 */ struct device_driver *driver; /*管理该设备的驱动程序 */ void *platform_data; /*平台相关的数据 */ struct dev_pm_info power; /* 电源管理 */ #ifdef CONFIG_NUMA int numa_node; /*设备接近的非一致性存储结构 */ #endif u64 *dma_mask; /* DMA 掩码 */ u64 coherent_dma_mask; /*设备一致性的DMA 掩码 */ struct device_dma_parameters *dma_parms; /* DMA 参数 */ struct list_head dma_pools; /* DMA 缓冲池 */ struct dma_coherent_mem *dma_mem; /* DMA 一致性内存 */ /*体系结构相关的附加项*/ struct dev_archdata archdata; /* 体系结构相关的数据 */ #ifdef CONFIG_OF

Linux设备驱动程序学习(10)-时间、延迟及延缓操作

Linux设备驱动程序学习(10)-时间、延迟及延缓操作 Linux设备驱动程序学习(10) -时间、延迟及延缓操作 度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在中定义或该文件包含的某个子平台相关文件中。作为通用的规则,即便如果知道HZ 的值,在编程时应当不依赖这个特定值,而始终使用HZ。对于当前版本,我们应完全信任内核开发者,他们已经选择了最适合的HZ值,最好保持HZ 的默认值。 对用户空间,内核HZ几乎完全隐藏,用户HZ 始终扩展为100。当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。对用户来说确切的HZ 值只能通过/proc/interrupts 获得:/proc/interrup ts 的计数值除以/proc/uptime 中报告的系统运行时间。 对于ARM体系结构:在文件中的定义如下: 也就是说:HZ 由__KERNEL__和CONFIG_HZ决定。若未定义__KERNEL__,H Z为100;否则为CONFIG_H Z。而CONFIG_HZ是在内核的根目录

的.config文件中定义,并没有在make menuconfig的配置选项中出现。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为: 所以正常情况下s3c24x0的HZ为200。这一数值在后面的实验中可以证实。 每次发生一个时钟中断,内核内部计数器的值就加一。这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。 使用jiffies 计数器 这个计数器和用来读取它的工具函数包含在,通常只需包含,它会自动放入jiffi es.h 。 jiffies 和jiffies_64 必须被当作只读变量。当需要记录当前jiffies 值(被声明为volatile 避免编译器优化内存读)时,可以简单地访问这个unsigned long 变量,如: 以下是一些简单的工具宏及其定义:

基于Linux系统的HHARM9电机驱动程序设计

收稿日期:2005-09-22 作者简介:朱华生(1965-),男,江西临川人,副教授. 文章编号:1006-4869(2005)04-0051-03 基于Linux 系统的HHARM9电机驱动程序设计 朱华生,胡凯利 (南昌工程学院计算机科学与技术系,江西南昌330099) 摘 要:对嵌入式Linux 操作系统驱动程序的组成进行分析,讨论了驱动程序的基本框架,以HHARM9电机控制为实例,详细论述了电机驱动程序的实现过程. 关键词:嵌入式;Linux;驱动程序 中图分类号:TP316 文献标识码:A Linux System -Based Design of HHARM 9Electromotor Driver ZHU Hua -sheng,HU Ka-i li (Department of Computer and Science,Nanchang Institute of Technology,Nanchang 330099,China) Abstract:The paper analyses the composition of driver in embedded linux system,disuses its basic frame of driver,and illustrales the process of driver design of HHARM9electromotor in detail. Key words:Embedded;Linux; driver 嵌入式Linux 操作系统因具有免费、开放源代码、强大的网络功能等 特点,在嵌入式产品中得到越来越广泛的应用.基于Linux 操作系统的嵌入 式产品结构[1]如图1所示.本文主要探讨嵌入式系统驱动程序的设计. 1 嵌入式Linux 操作系统驱动程序简介 1)驱动程序和应用程序的区别 驱动程序的设计和应用程序的设计有很大的区别[2].首先,驱动程序 的设计要对硬件的结构、信号的工作流程十分清楚,而在应用程序的设计 中,一般不需要了解这些.其次,应用程序一般有一个main 函数,从头到尾 执行一个任务;驱动程序却不同,它没有main 函数,通过使用宏module _init(初始化函数名),将初始化函数加入内核全局初始化函数列表中,在内核初始化时执行驱动的初始化函数,从而完成驱动的初始化和注册,之后驱动便停止等待被应用软件调用.应用程序可以和GLIB C 库连接,因此可以包含标准的头文件,比如等;在驱动程序中,不能使用标准C 库,因此不能调用所有的C 库函数,比如输出打印函数只能使用内核的printk 函数,包含的头文件只能是内核的头文件,比如. 2)Linux 系统设备文件 为了方便应用程序的开发,在Linux 操作系统中,使用了设备文件这一概念来管理硬件设备.Linux 操 第24卷 第4期 2005年12月南昌工程学院学报Journal of Nanchang Institute of Technology Vol.24No.4Dec.2005

Linux设备驱动程序说明介绍

Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__

linux内核部件分析(九)——设备驱动模型之device

linux内核部件分析(九)——设备驱动模型之device 前面我们分析了device、driver、bus三种类型,主要是三者的注册与注销,在sysfs中的目录与属性文件创建等内容。本节就来详细分析下,在设备注册到总线上时,总线是如何为其寻找对应的驱动的;在驱动注册到总线上时,总线又是如何为其寻找对应的设备的。 本节的实现代码集中在drivers/base/bus.c和 drivers/base/dd.c中。 先来回忆下,在device_register()->device_add()中,先是调用bus_add_device()添加device与bus间的联系,并添加bus 为device定义的属性,然后会调用bus_probe_device()。bus_probe_device()会试图为已挂在总线上的该设备寻找对应的驱动。我们的故事就从这里开始。 [cpp] view plaincopyprint?/** * bus_probe_device - probe drivers for a new device * @dev: device to probe * * - Automatically probe for a driver if the bus allows it. */ void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret; if (bus && bus->p->drivers_autoprobe) { ret = device_attach(dev); WARN_ON(ret < 0); } } bus_probe_device()为总

linux驱动程序进入内核

ARM-uClinux下编写加载驱动程序详细过程 本文主要介绍在uClinux下,通过加载模块的方式调试IO控制蜂鸣器的驱动程序。实验过程与上篇文章所讲的过程基本相似,更多注重细节及注意事项。 本文适合学习ARM—Linux的初学者。 //================================================================== 硬件平台:MagicARM2200教学试验开发平台(LPC2290) Linux version 2.4.24,gcc version 2.95.3 电路连接:P0.7——蜂鸣器,低电平发声。 实验条件:uClinux内核已经下载到开发板上,能够正常运行;与宿主机相连的网络、串口连接正常。 //================================================================== 编写蜂鸣器的驱动程序相对来说容易实现,不需要处理中断等繁琐的过程,本文以蜂鸣器的驱动程序为例,详细说明模块化驱动程序设计的主要过程和注意事项。 一、编写驱动程序 驱动程序的编写与上文所说的编写过程基本相同,这里再详细说明一下。 //========================================== //蜂鸣器驱动程序:beep.c文件 //------------------------------------------------------------------- #include /*模块相关*/ #include /*内核相关*/ #include /*linux定义类型*/ #include /*文件系统 file_opertions 结构体定义*/ #include /*出错信息*/ /*PINSEL0 注意:低2位是UART0复用口,不要改动*/ #define PINSEL0 (*((volatile unsigned*) 0xE002C000)) /*P0口控制寄存器*/ #define IO0PIN (*((volatile unsigned*) 0xE0028000))

《LINUX设备驱动程序》阅读笔记全十八章

《LINUX设备驱动程序》阅读笔记 目录 第1章:设备驱动程序简介 (1) 第2章:构造和运行模块 (1) 第3章:字符设备驱动程序 (1) 第4章:调试技术 (2) 第5章:并发和竞态 (2) 第6章:高级字符驱动程序操作 (3) 第7章:时间、延迟及延缓操作 (3) 第8章:分配内存 (3) 第9章:与硬件通信 (4) 第10章:中断处理 (4) 第11章:内核的数据类型 (4) 第12章:PCI 驱动程序 (5) 第13章:USB 驱动程序 (5) 第14章:Linux 设备模型 (5) 第15章:内存映射和 DMA (5) 第16章:块设备驱动程序 (6) 第17章:网络驱动程序 (6) 第18章:TTY 驱动程序 (6) 第1章:设备驱动程序简介 1、“通常,设备驱动程序就是这个进入Linux内核世界的大门”,“设备驱动程序在Linux 内核中扮演着特殊的角色,它们是一个个独立的黑盒子,使某个特定硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序”。 2、Linux系统将设备分成三种基本类型:字符设备、块设备和网络设备。 第2章:构造和运行模块 1、“内核黑客通常拥有一个‘牺牲用的’系统,用于测试新的代码”。 2、模块在被使用之前需要注册,而退出时要仔细撤销初始化函数所做的一切。驱动模块只能调用由内核导出的那些函数。 3、公共内核符号表中包含了所有的全局内核项(即函数和变量)的地址。当模块被装入内核后,它所导出的任何符号都会变成内核符号表的一部分。 第3章:字符设备驱动程序 第一节-主设备号和次设备号。对字符设备的访问都是通过文件系统内的设备名称进行的。通常而言,主设备号标识设备对应的驱动程序,而次设备号用于正确确定设备文件所指的设备。对应的数据结构为dev_t 类型。分配设备号使用函数alloc_chrdev_region() ,释放就使用unregister_chrdev_region() 函数。 第二节-一些重要的数据结构。大部分基本的驱动程序的操作都要涉及到三个重要的内

相关主题
文本预览
相关文档 最新文档