Linux Device Drivers 2nd Edition (779877), страница 44
Текст из файла (страница 44)
The rest of the waitqueue interface has remained relatively unchanged. The sysdep.h header definesthe needed macros in order to compile and run your modules with Linux 2.2 andLinux 2.0 without cluttering the code with lots of #ifdefs.The wait_event macro did not exist in the 2.0 kernel. For those who need it, wehave provided an implementation in sysdep.h17222 June 2001 16:36http://openlib.org.uaBackward CompatibilityAsynchronous NotificationSome small changes have been made in how asynchronous notification works forboth the 2.2 and 2.4 releases.In Linux 2.3.21, kill_fasync got its third argument.
Prior to this release, kill_fasyncwas called askill_fasync(struct fasync_struct *queue, int signal);Fortunately, sysdep.h takes care of the issue.In the 2.2 release, the type of the first argument to the fasync method changed. Inthe 2.0 kernel, a pointer to the inode structure for the device was passed, insteadof the integer file descriptor:int (*fasync) (struct inode *inode, struct file *filp, int on);To solve this incompatibility, we use the same approach taken for read and write:use of a wrapper function when the module is compiled under 2.0 headers.The inode argument to the fasync method was also passed in when called fromthe release method, rather than the -1 value used with later kernels.The fsync MethodThe third argument to the fsync file_operations method (the integer datasync value) was added in the 2.3 development series, meaning that portable codewill generally need to include a wrapper function for older kernels.
There is atrap, however, for people trying to write portable fsync methods: at least one distributor, which will remain nameless, patched the 2.4 fsync API into its 2.2 kernel.The kernel developers usually (usually . . . ) try to avoid making API changeswithin a stable series, but they have little control over what the distributors do.Access to User Space in Linux 2.0Memory access was handled differently in the 2.0 kernels. The Linux virtual memory system was less well developed at that time, and memory access was handleda little differently. The new system was the key change that opened 2.1 development, and it brought significant improvements in performance; unfortunately, itwas accompanied by yet another set of compatibility headaches for driver writers.The functions used to access memory under Linux 2.0 were as follows:verify_area(int mode, const void *ptr, unsigned long size);This function worked similarly to access_ok, but performed more extensivechecking and was slower.
The function returned 0 in case of success and17322 June 2001 16:36http://openlib.org.uaChapter 5: Enhanced Char Driver Operations-EFAULT in case of errors. Recent kernel headers still define the function, butit’s now just a wrapper around access_ok. When using version 2.0 of the kernel, calling verify_ar ea is never optional; no access to user space can safely beperformed without a prior, explicit verification.put_user(datum, ptr)The put_user macro looks much like its modern-day equivalent.
It differed,however, in that no verification was done, and there was no return value.get_user(ptr)This macro fetched the value at the given address, and returned it as its returnvalue. Once again, no verification was done by the execution of the macro.verify_ar ea had to be called explicitly because no user-area copy function performed the check. The great news introduced by Linux 2.1, which forced theincompatible change in the get_user and put_user functions, was that the task ofverifying user addresses was left to the hardware, because the kernel was nowable to trap and handle processor exceptions generated during data copies to userspace.As an example of how the older calls are used, consider scull one more time.
Aversion of scull using the 2.0 API would call verify_ar ea in this way:int err = 0, tmp;/** extract the type and number bitfields, and don’t decode* wrong cmds: return ENOTTY before verify_area()*/if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;/** the direction is a bit mask, and VERIFY_WRITE catches R/W* transfers.
‘Type’ is user oriented, while* verify_area is kernel oriented, so the concept of "read" and* "write" is reversed*/if (_IOC_DIR(cmd) & _IOC_READ)err = verify_area(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));else if (_IOC_DIR(cmd) & _IOC_WRITE)err = verify_area(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));if (err) return err;Then get_user and put_user can be used as follows:case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */tmp = scull_quantum;scull_quantum = get_user((int *)arg);put_user(tmp, (int *)arg);break;17422 June 2001 16:36http://openlib.org.uaBackward Compatibilitydefault: /* redundant, as cmd was checked against MAXNR */return -ENOTTY;}return 0;Only a small portion of the ioctl switch code has been shown, since it is little different from the version for 2.2 and beyond.Life would be relatively easy for the compatibility-conscious driver writer if itweren’t for the fact that put_user and get_user are implemented as macros in allLinux versions, and their interfaces changed.
As a result, a straightforward fix usingmacros cannot be done.One possible solution is to define a new set of version-independent macros. Thepath taken by sysdep.h consists in defining upper-case macros: GET_USER,_ _GET_USER, and so on. The arguments are the same as with the kernel macrosof Linux 2.4, but the caller must be sure that verify_ar ea has been called first(because that call is needed when compiling for 2.0).Capabilities in 2.0The 2.0 kernel did not support the capabilities abstraction at all.
All permissionschecks simply looked to see if the calling process was running as the superuser; ifso, the operation would be allowed. The function suser was used for this purpose;it takes no arguments and returns a nonzero value if the process has superuserprivileges.suser still exists in later kernels, but its use is strongly discouraged.
It is better todefine a version of capable for 2.0, as is done in sysdep.h:# define capable(anything) suser()In this way, code can be written that is portable but which works with modern,capability-oriented systems.The Linux 2.0 select MethodThe 2.0 kernel did not support the poll system call; only the BSD-style select callwas available. The corresponding device driver method was thus called select, andoperated in a slightly different way, though the actions to be performed are almostidentical.The select method is passed a pointer to a select_table, and must pass thatpointer to select_wait only if the calling process should wait for the requested condition (one of SEL_IN, SEL_OUT, or SEL_EX).The scull driver deals with the incompatibility by declaring a specific select methodto be used when it is compiled for version 2.0 of the kernel:17522 June 2001 16:36http://openlib.org.uaChapter 5: Enhanced Char Driver Operations#ifdef _ _USE_OLD_SELECT_ _int scull_p_poll(struct inode *inode, struct file *filp,int mode, select_table *table){Scull_Pipe *dev = filp->private_data;if (mode == SEL_IN) {if (dev->rp != dev->wp) return 1; /* readable */PDEBUG("Waiting to read\n");select_wait(&dev->inq, table); /* wait for data */return 0;}if (mode == SEL_OUT) {/** The buffer is circular; it is considered full* if "wp" is right behind "rp".
"left" is 0 if the* buffer is empty, and it is "1" if it is completely full.*/int left = (dev->rp + dev->buffersize - dev->wp) % dev->buffersize;if (left != 1) return 1; /* writable */PDEBUG("Waiting to write\n");select_wait(&dev->outq, table); /* wait for free space */return 0;}return 0; /* never exception-able */}#else /* Use poll instead, already shown */The _ _USE_OLD_SELECT_ _ preprocessor symbol used here is set by the sysdep.h include file according to kernel version.Seeking in Linux 2.0Prior to Linux 2.1, the llseek device method was called lseek instead, and itreceived different parameters from the current implementation.
For that reason,under Linux 2.0 you were not allowed to seek a file, or a device, past the 2 GBlimit, even though the llseek system call was already supported.The prototype of the file operation in the 2.0 kernel was the following:int (*lseek) (struct inode *inode, struct file *filp , off_t off,int whence);Those working to write drivers compatible with 2.0 and 2.2 usually end up defining separate implementations of the seek method for the two interfaces.2.0 and SMPBecause Linux 2.0 only minimally supported SMP systems, race conditions of thetype mentioned in this chapter did not normally come about.
The 2.0 kernel didhave a spinlock implementation, but, since only one processor could be running17622 June 2001 16:36http://openlib.org.uaQuick Referencekernel code at a time, there was less need for locking.Quick ReferenceThis chapter introduced the following symbols and header files.#include <linux/ioctl.h>This header declares all the macros used to define ioctl commands. It is currently included by <linux/fs.h>._IOC_NRBITS_IOC_TYPEBITS_IOC_SIZEBITS_IOC_DIRBITSThe number of bits available for the different bitfields of ioctl commands.There are also four macros that specify the MASKs and four that specify theSHIFTs, but they’re mainly for internal use.