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

📄 drivers.html

📁 USB驱动使用比较详细的一个例子
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<PRE>    正如前面说到的,打开设备文件并返回文件描述符的操作与对一个普通文本    的操作没有任何区别。O_WRONLY标记指明文件以只读方式打开,因为MN固件    尚未实现从设备读的处理。</PRE><PRE>    紧跟着的一个switch是对第二个参数的处理,至于其中的0x04/0x08/0x0C,    可以参考固件和MN的连线。</PRE><PRE>    对第一个参数的处理包含着重要的语句:ioctl,正是这些语句让MN散发出    迷人的光芒。核外空间ioctl的原型长得比较奇怪:	int ioctl (int fd, int cmd, ...);    省略号表示可变参数,根据cmd的不同这个可变参数的意义/存在与否/参数    类型也不同(不要怀疑你对C语言的掌握,这个参数其实总是存在的,并且有    一个唯一的类型:char *argp)。参数cmd会原装传递给内核,附加的参数会    被作为unsigned long传递。不过要十分注意的是当附加参数是指针时,需    特别处理,原因是内核空间的核外空间使用不同的映射方法;并且也有可能    带来安全问题。MN的这个参数类型是u16,所以暂时不用担心这个问题。</PRE><PRE>    回到终端用户身份,再执行一次turnLED,不过这次同时使用一个特殊的程    序:	[root@Home show]# strace ./turnLED 1 A	execve(&quot;./turnLED&quot;, [&quot;./turnLED&quot;, &quot;1&quot;, &quot;A&quot;], [/* 53 vars */]) = 0	uname({sys=&quot;Linux&quot;, node=&quot;Home&quot;, ...})  = 0	brk(0)                                  = 0x8049854	old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40013000	open(&quot;/etc/ld.so.preload&quot;, O_RDONLY)    = -1 ENOENT (No such file or directory)	open(&quot;/etc/ld.so.cache&quot;, 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(&quot;/lib/i686/libc.so.6&quot;, O_RDONLY)   = 3	read(3, &quot;\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\260X\1&quot;..., 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(&quot;/dev/MiniNurse&quot;, O_WRONLY)        = 3	ioctl(3, 0x80022500, 0xc)               = 0	close(3)                                = 0	exit_group(0)                           = ?    strace能够显示一个进程使用到的系统调用及参数和返回值,这是一个有力    的调试工具。通过它可以更清楚的了解究竟应用程序做了什么。</PRE><PRE>    最后说明一点,MN的驱动对内核非强占式的Linux2.4而言基本上是可重入的,    也就是说可以用两个进程(只要你喜欢,三个也行)同时对MN进行操作。要取    得好的效果,IPC当然是必不可少的,不过它不在本文讨论范围之内。</PRE><PRE><b><a name="— MN的驱动程序 —"><font size="3" color="#000080">— MN的驱动程序 —</font></a></b></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,但也是可以重载    的。注意不是ANSI C中的FILE,核外的东西不可能出现在核内。    </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发现有新设备查在总线上    时,这个函数会被调用,如果返回一个指针,则说明这个驱动认领了一个新

⌨️ 快捷键说明

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