⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 driver.txt

📁 USB驱动使用比较详细的一个例子
💻 TXT
📖 第 1 页 / 共 3 页
字号:
	open("/etc/ld.so.cache", O_RDONLY)      = 3
	fstat64(3, {st_mode=S_IFREG|0644, st_size=63441, ...}) = 0
	old_mmap(NULL, 63441, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40014000
	close(3)                                = 0
	open("/lib/i686/libc.so.6", O_RDONLY)   = 3
	read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\260X\1"..., 1024) = 1024
	fstat64(3, {st_mode=S_IFREG|0755, st_size=1246468, ...}) = 0
	old_mmap(NULL, 1256516, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40024000
	mprotect(0x40151000, 23620, PROT_NONE)  = 0
	old_mmap(0x40151000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12c000) = 0x40151000
	old_mmap(0x40155000, 7236, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40155000
	close(3)                                = 0
	munmap(0x40014000, 63441)               = 0
	open("/dev/MiniNurse", O_WRONLY)        = 3
	ioctl(3, 0x80022500, 0xc)               = 0
	close(3)                                = 0
	exit_group(0)                           = ?
    strace能够显示一个进程使用到的系统调用及参数和返回值,这是一个有力
    的调试工具。通过它可以更清楚的了解究竟应用程序做了什么。</PRE>
<PRE>    最后说明一点,MN的驱动对内核非强占式的Linux2.4而言基本上是可重入的,
    也就是说可以用两个进程(只要你喜欢,三个也行)同时对MN进行操作。要取
    得好的效果,IPC当然是必不可少的,不过它不在本文讨论范围之内。
</PRE>
<PRE><font size="3" color="#0000FF"><b><a href="#MN的驱动程序">— MN的驱动程序 —</a></b></font></PRE>
<PRE>    本段想按如下顺序进行:
	* 更多Linux模块、驱动细节介绍
	* 作为USB设备的MN的驱动程序
	* BUGs 和 TODO</PRE>
<PRE>    前面说到过一个模块的入口和出口,现在看看关于这两个函数的一些操作,
    usb-mininurse.h的L83-86。</PRE>
<PRE>	static int  usb_mn_init(void);
	static void usb_mn_exit(void);
	module_init (usb_mn_init);
	module_exit (usb_mn_exit);</PRE>
<PRE>    moudle_init和module_exit都是位于&lt;kernels&gt;/include/linux/init.h中的
    宏,这两个宏的作用是重命名出、入口函数。我猜这大概是为了响应命名规
    则的号召。执行了这两个宏后,模块被加入时执行usb_mn_init(void);被移
    除时执行usb_mn_exit。</PRE>
<PRE>    仅有模块出、入口还不能完成驱动能力。前面说过驱动程序完成的事情:解
    释、处理资源(如中断);实现系统调用;实现机制。对资源的解释处理融于
    驱动的实现细节之内,而关于机制的讨论可能会放在后面。现在看看驱动程
    序是如何实现系统调用从而让应用程序发挥功用。首先介绍两个重要的数据
    结构,均定义于&lt;kernels&gt;/include/linux/fs.h</PRE>
<PRE>	struct file_operations {
	        struct module *owner;
		loff_t (*llseek) (struct file *, loff_t, int);
	        ssize_t (*read) (struct file *, char *, size_t, loff_t *);
		ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
		int (*readdir) (struct file *, void *, filldir_t);
	        unsigned int (*poll) (struct file *, struct poll_table_struct *);
		int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
		int (*mmap) (struct file *, struct vm_area_struct *);
		int (*open) (struct inode *, struct file *);
		int (*flush) (struct file *);
		int (*release) (struct inode *, struct file *);
		int (*fsync) (struct file *, struct dentry *, int datasync);
		int (*fasync) (int, struct file *, int);
		int (*lock) (struct file *, int, struct file_lock *);
		ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
		ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
		ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
		unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	};</PRE>
<PRE>    在这个数据结构中,多数成员名是似曾相识的,并且它们的类型都是函数指
    针。你应该很容易联想到开头说到的两个面向对象术语:“重载”和“方
    法”。没错,操作系统正是用这一技术对外提供统一的文件“对象”名,对
    内(驱动程序)允许重载每个系统调用的实现。并不是每个域都是为设备驱动
    程序准备的,并且即便如此,也不是每个驱动程序都必须实现所有的域。随
    着时间的推移,结构成员也会有所变化。一会儿会结合MN驱动做分析。</PRE>
<PRE>	struct file {
	      struct list_head        f_list;
	      struct dentry           *f_dentry;
	      struct vfsmount         *f_vfsmnt;
	      struct file_operations  *f_op;
	      atomic_t                f_count;
	      unsigned int            f_flags;
	      mode_t                  f_mode;
	      loff_t                  f_pos;
	      unsigned long           f_reada, f_ramax, f_raend, f_ralen,
	      f_rawin;
	      struct fown_struct      f_owner;
	      unsigned int            f_uid, f_gid;
	      int                     f_error;</PRE>
<PRE>              unsigned long           f_version;</PRE>
<PRE>	      /* needed for tty driver, and maybe others */
	      void                    *private_data;</PRE>
<PRE>	      /* preallocated helper kiobuf to speedup O_DIRECT */
	      struct kiobuf           *f_iobuf;
	      long                    f_iobuf_lock;
	};</PRE>
<PRE>    file结构代表一个广义文件,在*nix里一切都是文件(包括MN),而不仅仅是
    通常认为的磁盘或其它介质上文件。当执行系统调用open时,内核创建该结
    构的一个实例并自动维护直到对open时返回的文件描述符做close。成员
    *private_date,可以由驱动开发人员自由使用,通常用来保存设备的相关
    信息,因为并不是所有的系统调用内核都向其传递设备信息。这个结构里也
    有一个file_operations实例:*f_op,不同文件有不同的f_op,也就有了不
    同的read/write等实现。主设备号通常可以对应一个f_op,但也是可以重载
    的。
    </PRE>
<PRE>    文件/dev/MiniNurse的file_operations定义在usb-mininurse.h的L94-100。</PRE>
<PRE>	static struct file_operations mn_fops = {
	       owner:          THIS_MODULE,</PRE>
<PRE>	       ioctl:          mn_ioctl,
	       open:           mn_open,
	       release:        mn_release,
	 };</PRE>
<PRE>    这不是ISO-C89语法,这是一个GNU-C扩展。比起直接用指针传递,这种方法
    看上去更加清爽,并且使用这种扩展可以更容易移植。通常编译器是不会调
    整结构成员顺序的,但是当编译这个语法成分时,编译器则能将频繁访问的
    成员放在相同的硬件缓冲行上取得性能的提高。在ISO-C99规范中也开始引
    进类似的语法,Linux2.4可以同时使用这两个,Linux2.6强制使用ISO-C99
    的语法。</PRE>
<PRE>    owner字段被初始化为THIS_MODULE,这个宏帮助Linux2.4初始化模块,并自
    动完成模块引用记数的工作。
    MN实现了三个系统调用:ioctl/open/release,并分别命名为:
    mn_ioctl/mn_open/mn_release。其它的都被做为NULL处理。</PRE>
<PRE>    MN应该算作字符设备(至少不会是块或网络设备),但是同时它也是USB设备,
    对MN的操作强烈依赖USB-CORE,因此钩上USB子系统比象征意义地钩上字符
    设备列表重要得多。
    作为USB设备,其驱动软件的最高抽象是结构usb_driver,定义于
    &lt;kernels&gt;/include/linux/usb.h:
	</PRE>
<PRE>	struct usb_driver {
	       struct module *owner;</PRE>
<PRE>	       const char *name;</PRE>
<PRE>	       void *(*probe)(
		    struct usb_device *dev,             /* the device */
		    unsigned intf,                      /* what interface */
		    const struct usb_device_id *id      /* from id_table */
		);
		void (*disconnect)(struct usb_device *, void *);</PRE>
<PRE>		struct list_head driver_list;</PRE>
<PRE>		struct file_operations *fops;
		int minor;</PRE>
<PRE>		struct semaphore serialize;</PRE>
<PRE>		int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);</PRE>
<PRE>		const struct usb_device_id *id_table;
	};</PRE>
<PRE>    就起要者:
    const char *name,这个名字在USB系统中应该是唯一的,按照建议它和模块
    的名字应该是一样的。</PRE>
<PRE>    函数指针void *(*probe) {struct usb_device *dev, unsigned intf,
    const struct usb_device_id *id},当USB CORE发现有新设备查在总线上
    时,这个函数会被调用,如果返回一个指针,则说明这个驱动认领了一个新
    的设备接口,如果返回NULL,则这个驱动并不适用新的设备。</PRE>
<PRE>    调用参数结构usb_device代表一个USB硬件,同样定义于usb.h,在此不列出。
    这个结构体保存了一些USB规范上的信息,和一些链表用于把usb_device链
    成树型,其中非叶子节点是一种特殊的USB设备:HUB。因为USB是可以热拔
    插的,所以软件也必须做足够的健壮性处理。
    </PRE>
<PRE>    结构体usb_device_id为热拔插策略的部署提供信息,定义于usb.h。第一个
    成员为匹配规则,后面的成员为idVendor/idProduct等。通常一个驱动可以
    带一堆同类的设备,所以通常实现出这个结构体的数组,并且数组的最后一
    个元素要为零,作为停止匹配的标记。usb.h中同时定义了用于构筑id条目
    的宏,使用很方便。</PRE>
<PRE>    函数指针void (*disconnect)(struct usb_device *, void *)实现和probe
    正好相反的功能,在设备被拔走时调用。</PRE>
<PRE>    MN的usb_driver定义在usb-mininurse.h的L102-109。USB CORE提供了注册
    和注销函数:
	extern int usb_register(struct usb_driver *);
	extern void usb_deregister(struct usb_driver *);
    usb-mininurse.c在usb_mn_init和usb_mn_exit中分别调用这两个函数,也
    就是说每当模块被加载时就向USB CORE注册MN驱动,模块被移除时向USB
    CORE注销MN驱动。</PRE>
<PRE>    至此已经为全面分析MN驱动做了足够准备。下面来看看MN是如何实现那三个
    系统调用以及两个为USB新增的函数。</PRE>
<PRE>    通常probe总是第一个被调用,所以也先讨论,它位于usb-mininurse.c的
    L134-201。它的参数刚才介绍过了。</PRE>
<PRE>	L142-145
	if ((udev-&gt;descriptor.idVendor != USB_MN_VENDOR_ID) ||
            (udev-&gt;descriptor.idProduct != USB_MN_PRODUCT_ID)) {
          return NULL;
	}</PRE>
<PRE>    这段代码用于检测新设备的各种ID值是否适用于这个驱动。
    USB_MNVENDOR_ID和USB_MN_PRODUCT_ID是在usb_mininurse.h定义的宏,其
    值分别为0x0471(这是Philips在USB-IF申请的厂商代码)和0x0666,不过似
    乎到2003年1月为止,Philips并没有启用0x0666这个产品代码。</PRE>
<PRE>    L147-156,这段代码为新插入的MN选择一个合适的次设备号。数组
    minor_table则保存了所有在线的MN。不过,自从驱动版本到0.1.1后,这段
    代码和相关的变量不仅显得多余而且迫害了整个代码的清晰度。把它保留在
    这里的唯一理由是只要做少量修改,代码就可以就更好的兼容性。以后会说
    到版本0.1.1到底增加了神奇的东西。</PRE>
<PRE>    L158-163,这段代码做常规的内存分配。其中的kmalloc函数和malloc函数
    在使用上基本相同,但是它的第二个参数使得kamlloc有更多行为方式。在
    这里使用的是GFP_KERNEL,通常在系统调用级的函数中会用这个标记分配内
    存,它要求调用函数必须可重入。此外kmalloc获得内存后不会清空原有内容,
    通常为了安全用memeset将其置空。</PRE>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -