为什么应用程序和字符设备字符驱动程序实例的数据交换要使用

第二章_字符设备驱动程序_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
第二章_字符设备驱动程序
上传于||暂无简介
大小:1.22MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢&&&&linux 添加字符设备驱动程序及测试程序
linux 添加字符设备驱动程序及测试程序
linux下采用模块方法,添加一个新的设备驱动程序。
要求添加字符设备的驱动。
另附一个应用程序,测试添加的驱动程序
若举报审核通过,可奖励20下载分
被举报人:
creazyapple
举报的资源分:
请选择类型
资源无法下载
资源无法使用
标题与实际内容不符
含有危害国家安全内容
含有反动色情等内容
含广告内容
版权问题,侵犯个人或公司的版权
*详细原因:
VIP下载&&免积分60元/年(1200次)
您可能还需要
操作系统下载排行二次元同好交流新大陆
扫码下载App
汇聚2000万达人的兴趣社区下载即送20张免费照片冲印
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
初始化例程init(),负责初始化设备并且将驱动程序和内核的其它部分通过注册函数实现无缝连接。入口函数(或方法)集,如open(),read(),ioctl(),llseek()和write(),这些函数直接对应相应的I/O系统调用,由用户应用程序通过对应的/dev节点调用。中断例程,底半部例程,定时器处理例程,内核辅助线程以及其它的组成部分。它们大部分对用户程序是透明的。从数据流的角度看,字符设备驱动程序包括如下关键的数据结构。(1)与特定设备相关的数据结构。此结构保存着驱动程序使用的信息。(2)struct cdev,针对字符设备驱动程序的内核抽象。这个结构通常嵌入在前面讨论的设备结构中。(3)struct file_operations,包括所有设备驱动程序入口函数的地址。(4)struct file,包括关联/dev节点的信息。设备实例:系统CMOS& & & & 借助CMOS设备驱动程序,你可以像访问普通文件一样访问两个PC CMOS存储体,应用程序可以在/dev/cmos/0和/dev/cmos/1上运行,使用I/O系统调用访问两个存储体中的数据。因为BIOS分配给CMOS域的存储粒度是比特级的,所以驱动程序能够进行比特级的访问。因此,read()可以获取指定数目的比特,并根据读取的比特数移动内部文件指针。& & & & 通过两个I/O地址(一个索引索引寄存器和一个数据寄存器)访问CMOS,必须在索引寄存器中指定准备访问的CMOS存储器的便宜,并通过数据寄存器来交换数据。& & & & 因为每个驱动程序方法都有一个对应的由应用程序使用的系统调用,我们将看看系统调用和相应的驱动程序方法。驱动程序初始化init()函数是注册机制的基础。它负责完成如下工作:(1)申请分配主设备号(2)为特定设备相关的数据结构分配内存(3)将入口函数(open(),read()等)与字符驱动程序的cdev抽象相关联(4)将主设备号与驱动程序的cdev相关(5)在/dev和/sys下创建节点。如在第4章中所讨论的,/dev管理从2.2版本内核中的静态设备节点,演变2.4版本中的动态命名,最后又称为2.6版本中的用户空间的守护进程(udevd)。(6)初始化硬件。在本例的简单CMOS驱动程序中不涉及此部分。代码清单如下:CMOS驱动程序初始化#include &linux/fs.h&#include &linux/cdev.h&#include &linux/types.h&#include &linux/slab.h&#include &asm/uaccess.h&#include &linux/pci.h&#define NUM_CMOS_BANKS 2/* per-device (per-bank) structure *//*和特定设备相关的数据结构内核用cdev结构来表示字符设备。字符驱动程序通常将cdev结构包含于字符设备特定的数据结构中。本例中,cdev定义于cmos_dev中。cmos_init()为本驱动程序服务的每一个设备(在本例中是CMOS存储体)分配相关数据结构的内存:包括设备特定的数据结构及其cdev结构*/struct cmos_dev {& & & & unsigned short curent_ & & & &/* Current pointer within the bank */& & & & & & & & & & & & & & & & & & & /* Size of the bank */& & & & int bank_ & & & & & & & & & & & & & & & /* CMOS bank number */& & & & & & & & & & & & & & & & & & & &/* The cdev structure*/& & & & char name[10]; & & & & & & & & & & & & & & & & & /* Name of I/O region */& & & & /* ......... */ & & & & & & & & & & & & & & & & & & & & & & /* Mutexes, spinlocks, wait queues, ... */} *cmos_devp[NUM_CMOS_BANKS];/* File operation structure, Defined in linux/fs.h *//*驱动程序入口地址*/static struct file_operation cmos_fops = {& & & & .owner = THIS_MODULE, & & & & & & &/* Owner *//*驱动程序模块的地址。知晓拥有者的身份可以让内核帮助驱动程序减轻一些事物管理的负担,如在处理设备的打开和释放时跟踪使用计数*/& & & & .open = cmos_open, & & & & & & & & & & & &/* Open method */& & & & .release = cmos_release, & & & & & & & /* Release method */& & & & .read = cmos_read, & & & & & & & & & & & & /* Read method */& & & & .write = cmos_write, & & & & & & & & & & & &/* Write method */& & & & .llseek = cmos_llseek, & & & & & & & & & &/* Seek method */& & & & .ioctl = cmos_ioctl, & & & & & & & & & & & & & /* Ioctl method */};static dev_t cmos_dev_ & & & & &/* Allotted device number */struct class *cmos_ & & & & & & & & & & /* Tie with the device model */#define CMOS_BANK_SIZE & & & & & & &(0xFF*8)#define DEVICE_NAME & & & & & & & & & & &"cmos"#define CMOS_BANK0_INDEX_PORT & & & &0x70#define CMOS_BANK0_DATA_PORT & & & & 0x71#define CMOS_BANK1_INDEX_PORT & & & 0x72#define CMOS_BANK1_DATA_PORT & & & & 0x73unsigned char addrports[NUM_CMOS_BANKS] = { CMOS_BANK0_INDEX_PORT,& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &CMOS_BANK1_INDEX_PORT };unsigned char dataports[NUM_CMOS_BANKS] = { CMOS_BANK0_DATA_PORT,& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &CMOS_BANK1_DATA_PORT };/*&* Driver Initialization*/int __initcmos_init(void)/*cmos_init()完成的大部分工作是通用的,因此如果替换掉和CMOS数据结构相关的部分,你可以使用这些代码作为模版开发其它的字符设备驱动程序 */{& & & & int i,& & & & /* Request dynamic allocation of a device major number */& & & & if (alloc_chrdev_region(&cmos_dev_number, 0, NUM_CMOS_BANKS, DEVICE_NAME) & 0 ) {& & & & & & & & & printk(KERN_DEBUG "cAN'T REGISTER DEVICE \n "); return -1;& & & & }/*调用alloc_chrdev_region()动态申请一个未用的主设备号。如果掉用成功,dev_number讲包含系统分配的主设备号。alloc_chrdev_region()的第二个参数是请求的起始次设备号,第三个参数是支持的次设备号数目。最后一个参数是和CMOS关联的设备名称,它将出现在/proc/devices中:bash& cat /proc/devices | grep cmos253 cmos253是动态分配给CMOS设备的主设备号。在2.6内核以前,不支持动态设备节点分配,因此字符驱动程序使用register_chrdev()调用去静态申请指定的主设备号。*/& & & & /* Populate sysfs entries */& & & & cmos_class = class_create(THIS_MODULE, DEVICE_NAME);/*class_create()为此设备构建了sysfs入口点*/& & & & for (i=0; i&NUM_CMOS_BANKS; i++) {& & & &&& & & &&/* Allocate memory for the per-device structure */& & & &&& & & &&cmos_devp[i] = kmalloc(sizeof(struct cmos_dev), GFP_KERNEL);& & & &&& & & &&if (!cmos_devp[i]) {& & & &&& & & &&& & & &&printk("Bad Kmalloc\n"): return -ENOMEM;& & & &&& & & &&}& & & &&& & & & /* Request I/O region */& & & &&& & & &&sprintf(cmos_devp[i]-&name, "cmos%d", i);& & & &&& & & &&if (! (retuest_region(addrports[i], 2, cmos_devp[i]-&name))) {& & & &&& & & &&& & & &&printk("cmos: I/O port 0x%x is not free.\n", addrports[i]);& & & &&& & & &&& & & &&return -EIO;& & & &&& & & & }/*通过调用request_region()申请I/O地址后,设备驱动程序在申请的I/O地址中操作运行,此机制确保其它程序不能申请此区域,直至通过release_region()释放占用的区域,request_region()通常被I/O总线驱动程序调用,例如PCI和ISA,以指明对于处理器地址空间中板上内存的拥有权(更多细节请见第10章)。这里申请每个CMOS存储体的I/O。request_region()的最后一个参数是一个被/pro/ioports使用的标识符,如果你查看文件/proc/ioports将会看到:bash& grep cmos /proc/ioports & &: & &cmos0 & &: & &cmos1*/& & & &&& & & &&& & & & /* Fill in the bank number to correlate this device& & & &&& & & & & &with the corresponding CMOS bank */& & & &&& & & &&cmos_devp[i]-&bank_number =& & & &&& & & &&& & & &&/* Connect the file operations with the cdev*/& & & &&& & & & cdev_init(&&cmos_devp[i]-&cdev, &cmos_fops);/*cdev_init()将文件操作(cmos_fops)和cdev关联*/& & & &&& & & & cmos_devp[i]-&cdev.owner = THS_MODULE;& & & &&& & & &&& & & & /* Connect the major/minor number the the cdev */& & & &&& & & & ret = cdev_add(&cmos_devp[i]-&cdev, (cmos_dev_number + i), 1);/*cdev_add将通过alloc_chrdev_region()分配的主/次设备号和cdev连接在一起*/& & & &&& & & & if (ret) {& & & &&& & & &&& & & &&printk("Bad cdev\n");& & & &&& & & &&& & & &&& & & &&& & & & }& & & &&& & & &&/* Send uevents to udev, so it'll create /dev nodes */& & & &&& & & & device_create(cmos_class, NULL, MKDEV(MAJOR(cmos_dev_NUMBER), I), "cmos%d", i);/*device_create()导致了两个uevent的产生,cmos0和cmos1.正如第四章所介绍的,udevd监听uevent,在查阅规则数据库后创建设备节点,为了在接收到相应的uevent(cmos0和cmos1)后创建与两个CMOS存储体(/dev/cmos/0和/dev/cmos/1)对应的设备节点,需要将如下的内容添加进udev规则目录中(/etc/udev/rules.d/):& & & & KERNEL=="cmos[0-1]*", NAME="cmos/%n"*/& & & &&}& & & &&printk("CMOS driver Initialized. \n");& & & &&return 0;}/*Driver Exit */void __exitcmos_cleanup(void){& & & &&& & & &&/* Release the major number */& & & &&unregister_chrdev_region((cmos_dev_number), NUM_CMOS_BANKS);& & & &&/*Release I/O region */& & & &&for (i=0; i&NUM_CMOS_BANKS; i++) {& & & &&& & & &&device_destroy (cmos_class, MKDEV(MAJOR(cmos_dev_number), i));& & & &&& & & &&release_region(addrports[i], 2);& & & &&& & & &&cdev_del(&cmos_devp[i]-&cdev);& & & &&& & & &&kfree(cmos_devp[i]);& & & &&& & & & }& & & & /*Destroy cmos-class */& & & & class_destroy(cmos_class);& & & & return();}module_init(cmos_init);module_exit(cmos_cleanup);打开与释放& & & & 当应用程序打开设备节点时,内核调用相应驱动程序的open()函数。可以在shell中执行以下代码来触发cmos_open()的执行:bash& cat /dev/cmos/0当应用程序关闭一个已经打开的设备时,内核调用release()函数。因此当读取CMOS存储体0的内容后,cat关闭和/dev/cmos/0关联的文件描述符,内核调用cmos_release()。& & & & 下面的代码显示了cmos_open()和cmos_release()的具体实现:/** open CMOS bank*/intcmos_open(struct inode *inode, struct file *file){& & & & struct cmos_dev *cmos_/*有两点值得关注。首先是cmos_dev的获取。cmos_open()的输入参数inode包含cdev结构在初始化过程中分配的地址。cdev定义与cmos_dev中。为了获取容器结构cmos_dev的地址,cmos_open()使用了内核的辅助函数container_of()*/& & & &&/* Get the per-device structure that contains this cdev */& & & & cmos_devp = container_of(inode-&i_cdev, struct cmos_dev, cdev);/*另一个值得关注的第二个输入参数--file结构中的private_data域的使用。你可以使用此数据域来存储cmos_dev地址。阅读cmos_release()(以及其它函数)可以发现如何用private_data来直接获取属于相应CMOS存储体的cmos_dev结构体的处理程序*/& & & & /* Easy access to cmos_devp from rest of the entry points */& & & & file-&private_data = cmos_& & & & /* Initialize some fields */& & & & cmos_devp-&size = CMOS_BANK_SIZE;& & & & cmos_devp-&current_pointer = 0;& & & & return 0;}/**Release CMOS bank*/int cmos_release (struct inode *inode, struct file *file){& & & & struct cmos_dev *cmos_devp = file-&private-& & & & /* Reset file pointer */& & & & cmos_devp-&current_pointer = 0;& & & & return 0;}数据交换& & & & read()和write()是负责在用户空间和设备之间交换数据的主要字符驱动函数。扩展的read()/write()函数系列包括其它的几个函数:fsync(),aio_read(),aio_write()和mmap()。& & & & CMOS驱动程序在一个简单的存储设备上操作,不必处理其它一般的字符驱动程序必须面对的复杂问题。& & & & CMOS数据访问例程不必“休眠--等待”设备I/O完成,其它字符驱动程序的read()和write()则必须支持阻塞和非阻塞操作模式。除非设备文件在非阻塞方式(O_NONBLOCK)下打开,否则read()和write()将会使调用进程休眠至I/O操作完成。& & & & CMOS驱动程序操作基于同步方式,不必依赖中断。然而,很多驱动程序的数据访问函数依靠中断来获取数据,并且需要通过等待队列等数据结构和中断上下文代码来通信。& & & & 代码清单中展示了CMOS驱动程序的read()和write()函数。不能从内核中直接访问用户空间的缓冲区,反之亦然。因此为了将CMOS存储器数据复制到用户空间,cmos_read()需要调用copy_to_user()。cmos_write()通过copy_from_user()完成相反的工作。因为copy_to_user()和copy_from_user()可能会睡眠,所以在调用这两个函数的时候不能使用自旋锁。& & & & 如前文所述,通过操作一对I/O地址可以实现对CMOS内存的访问。为了从I/O地址中读取不同大小的数据,内核提供了一组和结构无关的函数: in[b|w|l|sb|sl]()。类似地,可以通过out[b|w|sb|sl]()去写I/O区域。代码清单5-3中port_data_in()和port_data_out()使用inb()和outb()进行数据传输。代码清单5-3 & 读和写/** Read from a CMOS Bank at bit-level granularity*/ssize_t cmos_read(struct file *file, char *buf, size_t count, loff_t *ppos){& & & & ……}/** Write to a CMOS bank at bit-level granularity. 'count' holds the number of bits to be written.*/ssize_t cmos_write(struct file *file, const char *buf, size_t count, loff_t *ppos){……}& & & & 如果一个字符驱动程序的write()成功返回,就表示驱动程序已经完成了将数据传送下去的任务。然而,这并不能保证数据已经被成功地写到了设备中。如果某个应用程序需要确保成功,可以调用fsync()。想用的fsync()驱动程序函数确保数据从驱动程序缓冲区中排出,并且写到设备。CMOS驱动程序不需要fsync(),因为在CMOS驱动程序中,驱动程序写出等同于设备写出。如果用户程序有数据存储在多个缓冲区中丙戌要发送至设备,可以通过多个驱动程序写操作来完成。但是由于以下原因,这种做法不可行。& & & & (1)多个系统调用的开销和相应的上下文切换。& & & & (2)驱动程序和设备紧密相连,它应该能够以更合理的方式完成以上工作,从不同的缓冲区收集数据,并将其分发至设备。& & & & 由于以上原因,Linux和其它Unix版本支持read()和write()的向量版本。Linux字符设备驱动程序通常提供两个专门的函数以完成向量操作:readv()和writev()。从2.6.19版内核发布开始,这两个函数已经集成进Linux AIO(Asynchronous I/O,异步I/O)层。Linux AIO话题范围很广,超出了本章的讨论范围,我们只介绍AIO提供的异步向量能力。& & & & 向量驱动程序函数的原型如下:& & & & ssize_t aio_read(struct kiocb *iocb, const struct iovec *vector, unsigned long count, loff_t offset);& & & & ssize_t aio_write(struct kiocb *iocb, const struct iovec *vector, unsigned long count, loff_t offset);& & & & aio_read()/aio_write()的第一个参数描述了AIO操作,跌入个参数是一个iovec数组,这个参数是向量函数所使用的主要的数据结构,包含了数据缓冲区的地址和长度。实际上,此机制等同于第10章所讨论的分散/聚集(S/G)式DMA在用户空间的实现。可以在include/linux/uio.h文件里找到iovec的定义,在drivers/net/tun.c 文件里有向量字符驱动程序函数的示例实现。& & & & 另一个数据访问函数是mmap(),它将设备内存和用户的虚拟内存关联在一起。应用程序可以调用相应的系统调用,也可以调用mmap(),直接在返回的内存区操作,以访问设备驻留的内存。很多驱动程序都没有实现mmap(),因此这里就不深入讨论了。在drivers/char/mem.c文件里可以看到mmap()的一个实现示例。19.3节演示了如何使用mmap()。我们的CMOS驱动程序示例没有实现mmap()。& & & & 你克恩嗯已经注意到port_data_in()和port_data_out()在宏unlikely()内进行了存储体序号的完整性检查。宏likely()和unlikely()负责将相关条件为真/假的可能性报告给GCC。然后,GCC根据这一信息决定要执行的代码分支。因为我们将存储体完整性检查失败标记为“不可能”,GCC会将else{}中的代码插入代码流的当前位置。如果你使用的是likely()而不是unlikely(),结果正好相反。查找& & & & 内核使用内部指针跟踪当前文件访问的位置。应用程序使用lseek()系统调用去申请内部文件指针的重定位。使用lseek()服务,你可以将文件指针重设至文件内的任意偏移位置。字符驱动程序相对应的lseek()是lseek()函数。cmos_llseek()实现了CMOS驱动程序中的lseek()函数。& & & & 如前文所属,CMOS的内部文件指针按照比特移动而不是按照字节移动。也就是说,如果从CMOS驱动程序中读1字节数据,文件指针移动8次,而应用程序根据此规律来进行查找。根据CMOS存储体的大小,cmos_llseek()也实现了针对文件结束标志的相关操作。& & & & 为了理解llseek()的功能,我们来看看lseek()系统调用所支持的命令。& & & & (1)SEEK_SET, 设置文件指针至指定的位置& & & & (2)SEEK_CUR,计算相对于当前位置的偏移。& & & & (3)SEEK_END,计算相对于文件结尾的偏移。此命令可以巧妙地将文件指针移出文件结尾,却毫无意义。此技术经常用于创建大文件。CMOS驱动程序不支持SEEK_END。& & & & 我们可以联系前面的定义,阅读代码清单中cmos_llseek()的代码。/** &Seek to a bit offset within a CMOS bank*/static loff_t cmos_llseek(struct file *file, loff_t offset, int orig){}控制& & & & 另一个常见的字符驱动程序函数被称作I/O控制(ioctl)。当应用程序需要请求某些设备特定的动作时,这个例程接收并实现应用程序的命令。因为CMOS内存被BIOS用来存储启动设备顺序之类的关键信息,所以通常用CRC(Cyclic Redundancy Check,循环冗余校验)算法保护CMOS内存。为了检测数据是否遭到破坏,CMOS驱动程序支持两个ioctl命令。& & & & (1)调整校验和,用于CMOS内容被修改后重新计算CRC。计算出的校验和被存储在CMOS存储体1预先指定的偏移上。& & & & (2)验证校验和,用于检查CMOS内容是否完好。通过比较针对当前内容的计算出的CRC和以前存储的CRC来完成。& & & & 当应用程序需要进行校验和相关操作时,通过ioctl()系统调用发送命令给驱动程序。代码清单中cmos_ioctl()函数是CMOS驱动程序ioctl函数的具体实现。adjust_cmos_crc(int bank, unsigned short seed)实现了标准的CRC算法,未在清单中列出。/** Ioctls to adjust and verify CRC16s*/static int cmos_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned&检测数据是否可得& & & & 很多用户应用程序功能复杂,open()/read()/write()/close()这些系统调用不能满足其需求。对这些应用程序而言,当设备上有数据到来或者驱动程序准备好接收新数据时,系统最好能够采用同步或者异步的方式通知它们。这里介绍两个能够感知数据是否可获得的字符驱动程序方法:poll()和fasync()。前者是同步的,后者是异步的。
阅读(1822)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Linux通用字符设备驱动程序实例',
blogAbstract:'本实例引用自《精通Linux设备驱动程序开发》Sreekrishnan Venkateswaran 宋宝华[等]译 北京:人民邮电出版社,2010.06& & & & 从程序角度看,字符设备驱动程序包含如下内容。初始化例程init(),负责初始化设备并且将驱动程序和内核的其它部分通过注册函数实现无缝连接。入口函数(或方法)集,如open(),read(),ioctl(),llseek()和write(),这些函数直接对应相应的I/O系统调用,由用户应用程序通过对应的/dev节点调用。中断例程,底半部例程,定时器处理例程,内核辅助线程以及其它的组成部分。它们大部分对用户程序是透明的。',
blogTag:'字符设备驱动',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:3,
publishTime:6,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:1,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:true,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}君,已阅读到文档的结尾了呢~~
基于h.264的公交车3g网络视..
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
基于h.264的公交车3g网络视频监控终端的设计与实现
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口【图文】字符设备驱动程序_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
字符设备驱动程序
上传于||文档简介
&&关​于​字​符​设​备​的​介​绍​,​关​键​函​数​以​及​编​写​流​程​。​希​望​对​大​家​有​帮​助​。
大小:2.53MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢

我要回帖

更多关于 main注册字符驱动程序 的文章

 

随机推荐