版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567scull的设计scull0~scull3由全局且持久的内存区组成,可被多次打开,关闭后再打开,仍能保持原数据scullpipe0toscullpipe3FIFO(先进先出)设备,类似管道,可由一个进程读,另一个进程写Scullsingle、scullpriv、sculluid、scullwuid与scull0类似,但在open操作方面有些限制1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567主设备号与次设备号$ls–l/devcrw-rw-rw-1rootroot1,3Apr112002nullcrw1rootroot10,1Apr112002psauxcrw1rootroot4,1Oct2803:04tty1crw-rw-rw-1roottty4,64Apr112002ttys0crw-rw1rootuucp4,65Apr112002ttyS1crw--w1vcsatty7,1Apr112002vcs1crw--w1vcsatty7,129Apr112002vcsa1crw-rw-rw-1rootroot1,5Apr112002zero
主设备号通常用于标识设备对应的驱动程序,号相同的设备共用一个驱动程序;次设备号用于标识驱动程序所服务的具体设备主设备号次设备号主设备号与次设备号设备编号的内部表达内核中,设备编号数据类型为dev_t,定义在<linux/types.h>中操作设备编号的宏MAJOR(dev_tdev);由dev_t数得到主设备号MINOR(dev_tdev);由dev_t数得到次设备号MKDEV(intmajor,intminor);由主、次设备号得到dev_t数主设备号次设备号12位20位dev_t主设备号与次设备号静态分配设备编号intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);first:主设备号范围起始值,次设备号通常为0count:连续设备号的个数name:设备编号范围关联的设备名称,将出现在/proc/devices和/sysfs中成功返回0,失败返回负数静态分配需要预先知道可使用的设备号,如何知道?内核源代码树的Documenttation/devices.text可查到尚有哪些号可用主设备号与次设备号动态分配设备编号intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);dev:调用成功后保存分配到的设备编号范围的第一个数firstminor:第一个次设备号,通常用是0count与name与静态分配时相同释放设备编号voidunregister_chrdev_region(dev_tfirst,unsignedintcount);
主设备号与次设备号scull设备号分配if(scull_major){dev=MKDEV(scull_major,scull_minor);result=register_chrdev_region(dev,scull_nr_devs,"scull");}else{result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");scull_major=MAJOR(dev);}
if(result<0){printk(KERN_WARNING"scull:can'tgetmajor%d\n",scull_major);returnresult;}scull_major为全局变量,初始化值为SCULL_MAJOR,定义在scull.h中,设为0时为动态分配(默认),否则为静态分配。主设备号与次设备号scull_load静态分配的问题是:若驱动程序仅自己使用,选择一个未用的号来用没什么问题,但若驱动程序被广泛使用,则可能造成冲突动态分配的问题是:由于分配的主设备号不能始终一至,所以无法预先创建设备节点一旦分配了设备号,就可从/proc/devices中读到,因此可写一脚本代替insmod,在载入模块后读出设备号并创建节点。即用一脚本完成加载模块、读出主设备号及创建设备节点等操作。scull设备的这一脚本叫scull_load主设备号与次设备号/proc/devices文件如下:Characterdevices: 1mem 2pty 3ttyp 4ttyS 6lp 7vcs 10misc 13input 14sound 21sg 180usbBlockdevices:2fd8sd11sr65sd66sd
主设备号与次设备号/*Scull_load*/#!/bin/shmodule="scull"device="scull"mode="664"/sbin/insmod./$module.ko$*||exit1rm-f/dev/${device}[0-3]major=$(awk"\$2==\"$module\"{print\$1}"/proc/devices)mknod/dev/${device}0c$major0mknod/dev/${device}1c$major1mknod/dev/${device}2c$major2mknod/dev/${device}3c$major3group="staff“grep-q'^staff:'/etc/group||group="wheel“chgrp$group/dev/${device}[0-3]chmod$mode/dev/${device}[0-3]#awk‘条件类型1{动作1}条件类型2{动作2}…’filename#grep[参数]‘搜索字符串’filename主设备号与次设备号也可编写一个init脚本放在/etc/init.d下,可在系统初始化时加载模块、并根据主设备号创建节点若只涉及单个驱动,因动态设备号的分配并不是真正随机生成的,故只需在第一次加载时创建节点,以后加载时无需再创节点1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567一些重要的数据结构大部分的驱动程序操作涉及到3个重要的内核数据结构,分别是:file_operations:文件操作结构,保存文件操作方法file:文件结构,对应一个打开的文件inode:节点结构,对应一个文件在编写真正的驱动程序前,需要对这3个结构有一个基本的认识。一些重要的数据结构file_operations,在<linux/fs.h>中定义structfile_operations{ structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*); ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*); int(*readdir)(structfile*,void*,filldir_t); unsignedint(*poll)(structfile*,structpoll_table_struct*); int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong); int(*mmap)(structfile*,structvm_area_struct*);一些重要的数据结构 int(*open)(structinode*,structfile*); int(*flush)(structfile*); int(*release)(structinode*,structfile*); int(*fsync)(structfile*,structdentry*,int); int(*aio_fsync)(structkiocb*,int); int(*fasync)(int,structfile*,int); int(*lock)(structfile*,int,structfile_lock*); ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*); ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*); ssize_t(*sendfile)(structfile*,loff_t*,size_t,read_actor_t,void*); ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int); unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong); int(*check_flags)(int); int(*dir_notify)(structfile*,unsignedlong);};一些重要的数据结构structmodule*owner第一个file_operations成员不是一个操作,是一个指向拥有这个结构的模块的指针。这个成员用来在它的操作还在被使用时阻止模块被卸载。几乎所有时间中,它被初始化为THIS_MODULE,一个在<linux/module.h>中定义的宏。loff_t(*llseek)(structfile*,loff_t,int);llseek方法用于改变文件中的当前读/写位置,并且将新位置作为(正的)返回值。loff_t参数是一个“longoffset”,并且就算在32位平台上也至少有64位宽。出错时返回一个负的返回值。如果这个函数指针是NULL,seek调用会以无法预测的方式修改file结构中的位置计数器(在"file结构"一节中描述)。一些重要的数据结构ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);用来从设备中获取数据。非负返回值代表成功读取的字节数(返回值是一个“signedsize”类型,常常是目标平台本地的整数类型)。该函数指针为NULL时将导致调用失败并返回-EINVAL(“Invalidargument”)ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t);初始化一个异步读--在函数返回前可能不会结束的读操作。如果这个方法是NULL,所有的读操作会由read代替进行(同步地)。一些重要的数据结构ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);发送数据给设备。非负返回值代表成功发送的字节数.ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*);设备上的一个异步写入方法。int(*readdir)(structfile*,void*,filldir_t);对于设备文件,这个成员应当为NULL;它用来读取目录,仅对文件系统有用一些重要的数据结构unsignedint(*poll)(structfile*,structpoll_table_struct*);poll方法是poll,epoll和select3个系统调用的后端实现,用来查询对一个或多个文件描述符的读或写是否会被阻塞。poll应当返回一个位掩码,用于指示非阻塞的读或写是否可能,并且也会向内核提供将调用进程置于睡眠状态直到I/O变为可能时的信息。如果一个驱动的poll方法为NULL,则设备会被认为可读可写,且不会被阻塞。int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);ioctl方法提供了操作设备的非读写方法(如格式化软盘)。另外,内核还能识别一些ioctl命令而不必调用fops表的ioctl。如果设备不提供ioctl方法,对于内核未预先定义的请求,ioctl系统调用将返回错误(-ENOTTY,即设备无这样的ioctl)一些重要的数据结构int(*mmap)(structfile*,structvm_area_struct*);mmap用来请求将设备内存映射到进程的地址空间。如果这个方法是NULL,mmap系统调用返回-ENODEV。int(*open)(structinode*,structfile*);通常是对设备文件进行的第一个操作,但并不要求驱动一定要声明一个对应的方法。如果这个项是NULL,设备的打开永远成功,但系统不会通知驱动程序int(*release)(structinode*,structfile*);在file结构被释放时调用这个操作。如同open,release可以为NULL一些重要的数据结构int(*fsync)(structfile*,structdentry*,int);这个方法是fsync系统调用的后端实现,用户调用它来刷新任何待处理的数据。如果为NULL,fsync系统调用返回-EINVAL。int(*aio_fsync)(structkiocb*,int);这是fsync方法的异步版本int(*fasync)(int,structfile*,int);这个操作用来通知设备其FASYNC标志发生了改变。异步通知是一个高级的主题,在第6章中描述。如果驱动不支持异步通知,这个成员可以是NULL。一些重要的数据结构int(*lock)(structfile*,int,structfile_lock*);lock方法用来实现文件加锁;加锁对常规文件是必不可少的特性,但是设备驱动几乎从不实现它。ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);这些方法用来实现分散/汇聚型的读写操作。应用程序有时需要进行涉及多个内存区的单个读或写操作,这些系统调用允许它们这样做而不必额外增加数据拷贝操作。如果这些函数指针为NULL,就会调用read和write方法(可能多于一次)。一些重要的数据结构ssize_t(*sendfile)(structfile*,loff_t*,size_t,read_actor_t,void*);这个方法实现sendfile系统调用的读。其用最少的拷贝操作从一个文件描述符搬移数据到另一个。例如,web服务器可以利用这个方法将某一个文件的内容发送到网络连接。设备驱动常常使sendfile为NULL。ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);sendpage是sendfile的另一半;它由内核调用来发送数据到对应的文件,一次一页。设备驱动实际上不实现sendpage。一些重要的数据结构unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);这个方法的目的是在进程的地址空间找一个合适的位置,以便将底层设备中的内存段映射到该位置。该任务通常由内存管理代码完成,但该方法的存在可允许驱动程序强制满足特定设备需要的任何对齐需求。大部分驱动可以置这个方法为NULL。int(*check_flags)(int)这个方法允许模块检查传递给fnctl(F_SETFL...)调用的标志一些重要的数据结构int(*dir_notify)(structfile*,unsignedlong);这个方法在应用程序使用fcntl来请求目录改变通知时调用。只对文件系统有用;驱动不需要实现dir_notify。一些重要的数据结构scull设备驱动只实现最重要的设备方法。它的file_operations结构是如下初始化的:structfile_operationsscull_fops={ .owner=THIS_MODULE, .llseek=scull_llseek, .read=scull_read, .write=scull_write, .ioctl=scull_ioctl, .open=scull_open, .release=scull_release,};
一些重要的数据结构structfile,definedin<linux/fs.h>文件结构代表一个打开的文件,由内核在open时创建,并传递给在文件上操作的任何函数,直到最后关闭在文件的所有实例都关闭后,内核释放这个数据结构在内核源码中,指向structfile的指针常常称为file或者filp(“filepointer”)。本书用filp表示这个指针,以避免和结构自身混淆。因此,file指的是结构,而filp是结构指针一些重要的数据结构structfile{ mode_tf_mode; loff_tf_pos; unsignedintf_flags; structfile_operations*f_op; void*private_data; structdentry*f_dentry;};一些重要的数据结构mode_tf_mode;文件模式。它通过FMODE_READ和FMODE_WRITE位来标示文件是否可读或可写(或两者都是)loff_tf_pos;当前读写位置。loff_t在所有平台下都是64位(在gcc术语里是longlong)。驱动如果需要知道文件中的当前位置,可以读这个值,但不应该修改它。read/write会使用它们接收到的最后那个指针参数来更新这一位置,而不是直接对filp->f_pos进行操作。这一规则的一个例外是llseek,它的目的就是修改文件位置一些重要的数据结构unsignedintf_flags;这些是文件标志,例如O_RDONLY,O_NONBLOCK,和O_SYNC。为了检查用户请求的是否非阻塞操作,驱动程序应当检查O_NONBLOCK标志。而其他标志很少用到。注意,检查读/写权限应该查看f_mode,而非f_flags。所有的标志在头文件<linux/fcntl.h>中定义。一些重要的数据结构structfile_operations*f_op;与文件关联的操作。内核在执行open操作时对指针赋值,以后需要处理这些操作时就读取这个指针。filp->f_op中的值决不会为方便引用而保存起来。这意味着你可在需要的时候修改文件关联的操作,在返回调用者之后新方法就会生效。例如,对应于主设备号1(/dev/null,/dev/zero,等等)的open代码根据要打开的次设备号替代filp->f_op中的操作。这种技巧允许相同主设备号下的设备实现多种操作行为,而不会增加系统调用的负担。这种替换文件操作的能力在面向对象编程中称为"方法重载"。一些重要的数据结构void*private_data;open系统调用在调用驱动程序的open方法之前将这个指针设置为NULL。驱动程序可以将这个字段用于任何目的或者忽略它。驱动程序可以用这个成员来指向已分配的数据,但是一定要在内核销毁file结构之前,在release方法中释放内存。private_data是一个有用的资源,可用于在系统调用间保存状态信息,本书大部分例子模块都使用它。structdentry*f_dentry;文件对应的目录项(dentry)结构。除了用filp->f_dentry->d_inode的方式来访问索引节点inode结构之外,设备驱动开发者一般无需要关心dentry结构一些重要的数据结构structinode,<linux/fs.h>内核用inode结构在内部表示文件,因此它和file结构不同,后者表示打开的文件。对于单个文件,可能会有许多个表示打开的文件描述符file结构,但它们都指向单个inode结构。inode结构包含了大量有关文件的信息。作为一个通用的规则,这个结构只有2个成员对于编写驱动代码有用:structinode{ dev_ti_rdev; structcdev*i_cdev; …};一些重要的数据结构dev_ti_rdev;对于表示设备文件的节点inode结构,这个成员包含了实际的设备编号structcdev*i_cdev;structcdev是内核的内部结构,代表字符设备;当inode指向一个字符设备文件时,这个成员包含一个指向structcdev结构的指针内核开发者已经增加了2个宏,可用来从一个inode中获取主次编号:unsignedintiminor(structinode*inode);unsignedintimajor(structinode*inode);1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567字符设备的注册内核字符设备结构与接口(在<linux/cdev.h>中)structcdev{ structkobjectkobj; structmodule*owner; conststructfile_operations*ops; structlist_headlist; dev_tdev; unsignedintcount;};structcdev*cdev_alloc(void);voidcdev_init(structcdev*,conststructfile_operations*);intcdev_add(structcdev*,dev_t,unsigned);voidcdev_del(structcdev*);字符设备的注册分配structcdev结构(动态):structcdev*my_cdev=cdev_alloc();my_cdev->ops=&my_fops;初始化structcdev结构voidcdev_init(structcdev*cdev,structfile_operations*fops);将structcdev结构的信息告诉内核intcdev_add(structcdev*dev,dev_tnum,unsignedintcount);num:该设备对应的第一个设备编号count:与该设备关联的设备编号的数量字符设备的注册cdev_add使用注意事项:这个调用可能失败。如果它返回一个负的错误码,则设备并没有增加到系统中,故需要检测它的返回值cdev_add一返回,设备就“活”了,它的操作就会被内核调用。因此,如果你的驱动程序没有完全准备好处理设备上的操作,你不应当调用cdev_add.要从系统中去除一个字符设备时,调用:voidcdev_del(structcdev*dev);字符设备的注册Scull设备注册先定义自己的设备结构scull_dev,并包含内核字符设备结构cdevstructscull_dev{structscull_qset*data;/*Pointertofirstquantumset*/intquantum;/*thecurrentquantumsize*/intqset;/*thecurrentarraysize*/unsignedlongsize;/*amountofdatastoredhere*/unsignedintaccess_key;/*usedbysculluidandscullpriv*/structsemaphoresem;/*mutualexclusionsemaphore*/
structcdevcdev;/*Chardevicestructure*/};字符设备的注册初始化structcdevstaticvoidscull_setup_cdev(structscull_dev*dev,intindex){interr,devno=MKDEV(scull_major,scull_minor+index);cdev_init(&dev->cdev,&scull_fops);dev->cdev.owner=THIS_MODULE;dev->cdev.ops=&scull_fops;err=cdev_add(&dev->cdev,devno,1);/*Failgracefullyifneedbe*/if(err)printk(KERN_NOTICE"Error%daddingscull%d",err,index);}字符设备的注册intscull_init_module(void){ intresult,i; dev_tdev=0; if(scull_major){ dev=MKDEV(scull_major,scull_minor); result=register_chrdev_region(dev,scull_nr_devs,"scull"); }else{ result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull"); scull_major=MAJOR(dev); } if(result<0){ printk(KERN_WARNING"scull:can'tgetmajor%d\n",scull_major); returnresult; }字符设备的注册scull_devices=kmalloc(scull_nr_devs*sizeof(structscull_dev),GFP_KERNEL);if(!scull_devibces){ result=-ENOMEM; gotofail; } memset(scull_devices,0,scull_nr_devs*sizeof(structscull_dev));
for(i=0;i<scull_nr_devs;i++){scull_devices[i].quantum=scull_quantum;scull_devices[i].qset=scull_qset;init_MUTEX(&scull_devices[i].sem);scull_setup_cdev(&scull_devices[i],i);}structscull_dev*scull_devices;#defineSCULL_NR_DEVS4intscull_nr_devs=SCULL_NR_DEVSstructscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;unsignedintaccess_key;structsemaphoresem;structcdevcdev;};字符设备的注册return0;fail: scull_cleanup_module();returnresult;}module_init(scull_init_module);module_exit(scull_cleanup_module);字符设备的注册voidscull_cleanup_module(void){ inti;dev_tdevno=MKDEV(scull_major,scull_minor);if(scull_devices){ for(i=0;i<scull_nr_devs;i++){ scull_trim(scull_devices+i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); }unregister_chrdev_region(devno,scull_nr_devs);}字符设备的注册注册一个字符设备的早期(2.6前)方法intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major:主设备号,可以对应256个次设备号,主次设备号均不能大于255name:驱动程序名称fops:默认的操作结构该调用将为主设备号注册0~255作为次号,并为设备建立一个cdev结构intunregister_chrdev(unsignedintmajor,constchar*name);
1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567open和releaseopen方法进行设备的初始化,在大部分驱动程序中,完成如下工作检查设备特定的错误初始化设备(若是首次打开)更新f_op指针(若有必要)分配并填写filp->private_data里的数据结构open原型int(*open)(structinode*inode,structfile*filp);inode中包含有cdev结构,但是需要的是scull_devstructinode{dev_ti_rdev;structcdev*i_cdev;…};open和release利用container_of(pointer,container_type,container_field);可由cdev结构得到包含它的scull_dev结构
structscull_dev*dev; dev=container_of(inode->i_cdev,structscull_dev,cdev);得到scull_dev后,将其保存在file结构中,方便以后访问filp->private_data=dev;structscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;unsignedintaccess_key;structsemaphoresem;
structcdevcdev;};open和releasescull的open如下intscull_open(structinode*inode,structfile*filp){structscull_dev*dev;dev=container_of(inode->i_cdev,structscull_dev,cdev);filp->private_data=dev;
if((filp->f_flags&O_ACCMODE)==O_WRONLY){scull_trim(dev);/*删除旧量子及量子集(若有),并初始化scull_dev}return0;}open和releaserelease方法释放由open分配的,保存在filp->private_data中的内容关闭设备(在最后一次调用时)scull的release如下:intscull_release(structinode*inode,structfile*filp){return0;}1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567structscull_dev{structscull_qset*data;intquantum,qset;unsignedlongsize;…structcdevcdev;};structscull_qset{void**data;structscull_qset*next;};对应一内存区,量子数组,量子集SCULL_QUANTUM=4000BSCULL_QSET=1000scull的内存使用intscull_trim(structscull_dev*dev){structscull_qset*next,*dptr;intqset=dev->qset;inti;for(dptr=dev->data;dptr;dptr=next){if(dptr->data){for(i=0;i<qset;i++)kfree(dptr->data[i]);kfree(dptr->data);dptr->data=NULL;}next=dptr->next;kfree(dptr);}
dev->size=0;dev->quantum=scull_quantum;dev->qset=scull_qset;dev->data=NULL;return0;}该函数删除旧量子及量子集(若有),并初始化设备结构structscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;…};1324第3章字符设备驱动程序scull的设计主设备号与次设备号一些重要的数据结构字符设备的注册open和releasescull的内存使用read和write567read和writeread从设备读取数据到用户空间,write从用户空间写数据到设备,原型如下:ssize_tread(structfile*filp,char__user*buff,size_tcount,loff_t*offp);ssize_twrite(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp);filp:文件指针count:请求传输的字节数offp:指明用户在文件中进行存取操作的位置buff:指向用户空间缓冲区read和writebuff是用户空间指针,内核代码不能直接引用read和write内核空间与用户空间进行数据交换的函数是:unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount);unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);用一些特殊的与架构相关的方法来实现,确保在内核和用户空间安全、正确地交换数据拷贝前对指针进行检查,若指针无效,则不拷贝;另外,若在拷贝过程中遇到无效地址,则仅会复制部分数据,返回值为还需要拷贝的内存数值。read和writessize_tscull_read(structfile*filp,char__user*buf,size_tcount,loff_t*f_pos){structscull_dev*dev=filp->private_data;structscull_qset*dptr;intquantum=dev->quantum,qset=dev->qset;intitemsize=quantum*qset;intitem,s_pos,q_pos,rest;ssize_tretval=0;if(down_interruptible(&dev->sem))return-ERESTARTSYS;if(*f_pos>=dev->size)gotoout;if(*f_pos+count>dev->size)count=dev->size-*f_pos;…item=(long)*f_pos/itemsize;rest=(long)*f_pos%itemsize;s_pos=rest/quantum;q_pos=rest%quantum;dptr=scull_follow(dev,item);if(dptr==NULL||!dptr->data||!dptr->data[s_pos])gotoout;if(count>quantum-q_pos)count=quantum-q_pos;if(copy_to_user(buf,dptr->data[s_pos]+q_pos,count)){retval=-EFAULT;gotoout;}*f_pos+=count;retval=count;out:up(&dev->sem);returnretval;}read和writessize_tscull_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*f_pos){structscull_dev*dev=filp->private_data;structscull_qset*dptr;intquantum=dev->quantum,qset=dev->qset;intitemsize=quantum*qset;intitem,s_pos,q_pos,rest;ssize_tretval=-ENOMEM;
if(down_interruptible(&dev->sem))
return-ERESTARTSYS;item=(long)*f_pos/itemsize;rest=(long)*f_pos%itemsize;
s_pos=rest/quantum;q
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 商家入驻平台合同范本
- 河北农业大学现代科技学院《生物信息学》2023-2024学年第一学期期末试卷
- 潘彭与茅艳二零二四年度离嚸婚姻关系解除协议
- 2024年二手房买卖定金合同及购房协议3篇
- 2024年度新能源汽车供应链管理合同3篇
- 二零二四年度二手房自行车库买卖合同2篇
- 会议场地租赁合同范本
- 2024年度虚拟现实购物体验服务合同2篇
- 2024版建筑装饰工程设计合同2篇
- 苗木种植基地土地租赁合同2024年度
- 锂电PACK设计标准
- 兽医流行病学病因推断
- 青少年科技创新大赛选题及分析(课堂PPT)
- 信息技术课课堂教学评价表
- 施工进度计划书
- 管道施工HSE主要风险识别及预防措施
- 小学课题研究:《小学数学计算教学有效性的研究》开题报告
- 血栓弹力图课件-PPT
- 煤矿选煤厂各岗位风险源辨识卡
- 红旗驾驶员先进事迹
- 光荣升旗手PPT课件
评论
0/150
提交评论