最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

USB设备驱动架构

来源:动视网 责编:小OO 时间:2025-10-01 01:58:13
文档

USB设备驱动架构

1.Linux下驱动整体架构应用程序Open/Read/Write命令操作设备文件Mknod通过主设备号关联返回主设备号insmod向系统注册kernel驱动模块通过模块中的Read/Write操作主机硬件这个图我们从下面往上面看,linux驱动最底层是主机硬件,运行在主机硬件其上是驱动模块了,驱动模块直接对进行读写操作。驱动模块在内核是可以通过静态或者动态加载,来在内核中注册的。例如用insmodXXX.o模块,那么用limod就可以看到已经在内核中加载进来,设备文件是用mknod+新设备名
推荐度:
导读1.Linux下驱动整体架构应用程序Open/Read/Write命令操作设备文件Mknod通过主设备号关联返回主设备号insmod向系统注册kernel驱动模块通过模块中的Read/Write操作主机硬件这个图我们从下面往上面看,linux驱动最底层是主机硬件,运行在主机硬件其上是驱动模块了,驱动模块直接对进行读写操作。驱动模块在内核是可以通过静态或者动态加载,来在内核中注册的。例如用insmodXXX.o模块,那么用limod就可以看到已经在内核中加载进来,设备文件是用mknod+新设备名
1.Linux下驱动整体架构

应用程序

Open/Read/Write命令操作

设备文件

Mknod通过主设备号关联

返回主设备号

insmod向系统注册

kernel

驱动模块

通过模块中的Read/Write操作

主机硬件

这个图我们从下面往上面看,linux驱动最底层是主机硬件,运行在主机硬件其上是驱动模块了, 驱动模块直接对进行读写操作。驱动模块在内核是可以通过静态或者动态加载,来在内核中注册的。例如用 insmod XXX.o 模块,那么用limod就可以看到已经在内核中加载进来,设备文件是用 mknod+新设备名+c/b+主设备号+次设备号来关联起来的。上层的应用程序仅仅对设备文件进行读写等操作。

2.linux USB驱动层次

设备侧看

从运行Linux的主机侧看

USB设备驱动usb-skeleton/

Mass storage/ AL576

USB核心

USB主机控制器驱动

OHCI/EHCI/UHCI

USB控制器

OHCI/EHCI/UHCI

USB控制器

OHCI/EHCI/UHCI

上图可以说是前面第一部分的驱动模块的一个特写。

从主机侧来看,在linux驱动中,USB驱动最底层的是USB主控制器硬件。在其上运行的是USB主机控制驱动。主机控制驱动之上为USB核心层,在往上层为USB设备驱动层了(与插入主机上的U盘、鼠标、AL576等对应的设备驱动)。

    Linux内核的USB核心负责USB驱动管理和协议处理的重要工作,其功能包括:通过定义一些数据结构、宏、和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口:通过全部变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。

3.USB骨架驱动usb-skeleton分析

    module_init (usb_skel_init);/*模块入口*/

module_exit (usb_skel_exit); /*模块出口*/

static struct usb_driver skel_driver = {

    name:        "skeleton",/*本驱动名*/

    probe:        skel_probe,/*USB设备探测函数指针*/

    disconnect:    skel_disconnect,/*USB设备断开函数指针*/

    fops:        &skel_fops,/*对应的操作*/

    minor:        USB_SKEL_MINOR_BASE,

    id_table:    skel_table,/*本驱动支持的设备列里面对应设备VENDOR_ID,PRODUCT_ID*/

};

static struct file_operations skel_fops = {

    owner:        THIS_MODULE,

    read:        skel_read,

    write:        skel_write,

    ioctl:        skel_ioctl,

    open:        skel_open,

    release:    skel_release,

};

USB骨架程序的关键几点如下:

USB驱动的注册和注销 

Usb驱动程序在注册时会发送一个命令给usb_register,通常在驱动程序的初始化函数里。

当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister 函数处理:

static void __exit usb_skel_exit(void)

{

/* deregister this driver with the USB subsystem */

usb_deregister(&skel_driver);

}

module_exit(usb_skel_exit);

当usb设备插入时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_TABLE。代码如下(这个模块仅支持某一特定设备):

/* table of devices that work with this driver */

static struct usb_device_id skel_table [] = {

{ USB_DEVICE(USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID) },

{ } /* Terminating entry */

};

MODULE_DEVICE_TABLE (usb, skel_table);

USB_DEVICE宏利用厂商ID和产品ID为我们提供了一个设备的唯一标识。当系统插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。usb_device 结构指针、接口号和接口ID都会被传递到函数中。

static void * skel_probe(struct usb_device *dev,unsigned int ifnum, const struct usb_device_id *id)

驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。

在骨架驱动程序里,最后一点是我们要注册devfs。我们创建一个缓冲用来保存那些被发送给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs子系统中注册设备,允许devfs用户访问我们的设备。注册过程如下:

/* initialize the devfs node for this deviceand register it */

sprintf(name, "skel%d", skel->minor);

skel->devfs = devfs_register(usb_devfs_handle, name,

DEVFS_FL_DEFAULT, USB_MAJOR,

USB_SKEL_MINOR_BASE + skel->minor,

S_IFCHR | S_IRUSR | S_IWUSR |

S_IRGRP | S_IWGRP | S_IROTH,

&skel_fops, NULL);

如果devfs_register函数失败,不用担心,devfs子系统会将此情况报告给用户。

当然最后,如果设备从usb总线拔掉,设备指针会调用disconnect 函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且从devfs上注销调自己。

/* remove our devfs node */

devfs_unregister(skel->devfs);

现在,skeleton驱动就已经和设备绑定上了,任何用户态程序要操作此设备都可以通过file_operations结构所定义的函数进行了。首先,我们要open此设备。在open函数中MODULE_INC_USE_COUNT 宏是一个关键,它的作用是起到一个计数的作用,有一个用户态程序打开一个设备,计数器就加一,例如,我们以模块方式加入一个驱动,若计数器不为零,就说明仍然有用户程序在使用此驱动,这时候,你就不能通过rmmod命令卸载驱动模块了。

/* increment our usage count for the module */

MOD_INC_USE_COUNT;

++skel->open_count;

/* save our object in the file's private structure */

file->private_data = skel;

当open完设备后,read、write函数就可以收、发数据了。

skel的write、和read函数 

他们是完成驱动对读写等操作的响应。

在skel_write中,一个FILL_BULK_URB函数,就完成了urb 系统callbak和我们自己的skel_write_bulk_callback之间的联系。注意skel_write_bulk_callback是中断方式,所以要注意时间不能太久,本程序中它就只是报告一些urb的状态等。

read 函数与write 函数稍有不同在于:程序并没有用urb 将数据从设备传送到驱动程序,而是我们用usb_bulk_msg 函数代替,这个函数能够不需要创建urbs 和操作urb函数的情况下,来发送数据给设备,或者从设备来接收数据??。我们调用usb_bulk_msg函数并传提一个存储空间,用来缓冲和放置驱动收到的数据,若没有收到数据,就失败并返回一个错误信息。

usb_bulk_msg函数 

当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提交给usb子系统。

skel_disconnect函数 

当我们释放设备文件句柄时,这个函数会被调用。MOD_DEC_USE_COUNT宏会被用到(和MOD_INC_USE_COUNT刚好对应,它减少一个计数器),首先确认当前是否有其它的程序正在访问这个设备,如果是最后一个用户在使用,我们可以关闭任何正在发生的写,操作如下: 

/* decrement our usage count for the device */

--skel->open_count;

if (skel->open_count <= 0) {

/* shutdown any bulk writes that might be

going on */

usb_unlink_urb (skel->write_urb);

skel->open_count = 0;

}

/* decrement our usage count for the module */

MOD_DEC_USE_COUNT;

最困难的是,usb 设备可以在任何时间点从系统中取走,即使程序目前正在访问它。usb驱动程序必须要能够很好地处理解决此问题,它需要能够切断任何当前的读写,同时通知用户空间程序:usb设备已经被取走。

如果程序有一个打开的设备句柄,在当前结构里,我们只要把它赋值为空,就像它已经消失了。对于每一次设备读写等其它函数操作,我们都要检查usb_device结构是否存在。如果不存在,就表明设备已经消失,并返回一个-ENODEV错误给用户程序。当最终我们调用release 函数时,在没有文件打开这个设备时,无论usb_device结构是否存在、它都会清空skel_disconnect函数所作工作。

 

文档

USB设备驱动架构

1.Linux下驱动整体架构应用程序Open/Read/Write命令操作设备文件Mknod通过主设备号关联返回主设备号insmod向系统注册kernel驱动模块通过模块中的Read/Write操作主机硬件这个图我们从下面往上面看,linux驱动最底层是主机硬件,运行在主机硬件其上是驱动模块了,驱动模块直接对进行读写操作。驱动模块在内核是可以通过静态或者动态加载,来在内核中注册的。例如用insmodXXX.o模块,那么用limod就可以看到已经在内核中加载进来,设备文件是用mknod+新设备名
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top