PrintNumber | ErrorLocation | Error | Correction | DateAdded |
---|---|---|---|---|
2 | p iv | update copright page | fixed | 6/25/2008 |
2 | p xxvii | Sometimes, for simplicity, the book uses generic references. So if the text points you to the arch/your-arch/ directory, it should be translated, for example, to arch/x86/ if you are compiling the kernel for the x86 architecture. Similarly, any mention of the include/asm-your-arch/ directory should be read as include/asm-arm/ if you are, for instance, building the kernel for the ARM architecture. The * symbol and X are | Sometimes, for simplicity, the book uses generic references. So, if the text points you to the arch/your-arch/ directory, it should be translated, for example, to arch/x86/ if you are compiling the kernel for the x86 architecture. Similarly, any mention of the include/asm-your-arch/ directory should be read as include/asm-arm/ if you are, for instance, building the kernel for the ARM architecture. The * symbol and X are | 6/25/2008 |
2 | p xxviii | occasionally used as wildcard characters in filenames. So, if a chapter asks you to look at include/linux/time*.h, look at the header files, time.h, timer.h, times.h, and timex.h residing in the include/linux/ directory. If a section talks about /dev/input/eventX or /sys/devices/platform/i8042/serioX/, X is the interface number that the kernel assigns to your device in the context of your system configuration. |
occasionally used as wildcard characters in filenames. So, if a chapter asks you to look at include/linux/time*.h, look at the header files, time.h, timer.h, times.h, and timex.h residing in the include/linux/ directory. If a section talks about /dev/input/eventX or /sys/devices/platform/i8042/serioX/, X is the interface number that the kernel assigns to your device in the context of your system configuration. |
6/25/2008 |
2 | p xxx | Sreekrishnan Venkateswaran has a masters degree in computer science from the Indian Institute of Technology, Kanpur, India. During the past 12 years that he has been working for IBM, he has ported Linux to various embedded devices such as a wristwatch, handheld, music player, VoIP phone, pacemaker programmer, and remote patient monitoring system. Sreekrishnan was a contributing editor and kernel columnist to the Linux Magazine for more than 2 years. Currently, he manages the embedded solutions group at IBM India. |
Sreekrishnan Venkateswaran has a masters degree in computer science from the Indian Institute of Technology, Kanpur, India. During the past 12 years that he has been working for IBM, he has ported Linux to devices such as wristwatches, PDAs, and music players to VoIP phones, pacemaker programmers, and remote patient monitoring systems. Sreekrishnan was a contributing editor and kernel columnist to the Linux Magazine for more than 2 years. Currently, he manages the embedded solutions group at IBM India. |
6/25/2008 |
2 | p 3 | There are different flavors of free software. One such flavor is called public domain software. Software released under the public domain is not copyrighted, and no restrictions are imposed on its usage. You can use it for free, make changes to it, and even restrict the distribution of your modified sources. As you can see, the no restrictions clause introduces the power to introduce restrictions downstream. |
There are different flavors of free software. One such flavor is called public domain software. Software released under the public domain is not copyrighted, and no restrictions are imposed on its usage. You can use it for free, make changes to it, and even restrict the distribution of your modified sources. As you can see, the no restrictions clause introduces the power to impose restrictions downstream. |
6/25/2008 |
2 | p 6 | First, go to www.kernel.org and get the latest stable tree. The sources are archived as tar files compressed in both gzip (.gz) and bzip2 (.bz2) formats. Obtain the source files by uncompressing and untarring the zipped tar ball. In the following commands, replace X.Y.Z with the latest kernel version, such as 2.6.23: |
First, go to www.kernel.org and get the latest stable tree. The sources are archived as tar files compressed in both gzip (.gz) and bzip2 (.bz2) formats. Obtain the source files by uncompressing and untarring the zipped tar ball. In the following commands, replace X.Y.Z with the latest kernel version, such as 2.6.24: |
6/25/2008 |
2 | p 10 | GCC options You may ask GCC to generate preprocessed source code using the -E option. Preprocessed code contains header file expansions and reduces the need to hop-skip through nested include files to expand multiple levels of macros. Here is a usage example to pre-process drivers/char/mydrv.c and produce expanded output in mydrv.i: | GCC options You may ask GCC to generate preprocessed source code using the -E option. Preprocessed code contains header file expansions and reduces the need to hop-skip through nested include files to expand multiple levels of macros. Here is a usage example to pre-process drivers/char/mydrv.c and produce expanded output in mydrv.i: | 6/25/2008 |
2 | p 12 | The following utilities are available to manipulate modules: insmod, rmmod, lsmod, modprobe, modinfo, and depmod. The first two are utilities to insert and remove modules, whereas lsmod lists the modules that are currently loaded. modprobe is a cleverer version of insmod that also inserts dependent modules after examining the contents of /lib/modules/X.Y.Z/modules.dep. For example, assume that you need to mount a Virtual File Allocation Table (VFAT) partition present on a USB pen drive. Use modprobe to load the VFAT filesystem driver: |
The following utilities are available to manipulate modules: insmod, rmmod, lsmod, modprobe, modinfo, and depmod. The first two are utilities to insert and remove modules, whereas lsmod lists the modules that are currently loaded. modprobe is a cleverer version of insmod that also inserts dependent modules after examining the contents of /lib/modules/X.Y.Z/modules.dep. For example, assume that you need to mount a Virtual File Allocation Table (VFAT) partition present on a USB pen drive. Use modprobe to load the VFAT filesystem driver: |
6/25/2008 |
2 | p 13 | As you see in the lsmod output, modprobe inserts three modules rather than one. modprobe first figures out that it has to insert /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko. But when it peeks into the dependency file /lib/modules/X.Y.Z/modules.dep, it finds the following line and realizes that it has to load two other dependent modules first: /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko: /lib/modules/X.Y.Z/kernel/fs/fat/fat.ko /lib/modules/X.Y.Z/kernel/fs/nls/nls_base.ko It then proceeds to load fat.ko and nls_base.ko before attempting to insert vfat.ko, thus automatically loading all the modules you need to mount your VFAT partition. Use the modinfo utility to extract verbose information about the modules you just loaded: bash> modinfo vfat filename: /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko license: GPL description: VFAT filesystem support ... depends: fat, nls_base To compile a kernel driver as a module, toggle the corresponding menu choice button to <M> while configuring the kernel. Most of the device driver examples in this book are implemented as kernel modules. To build a module mymodule.ko from its source file mymodule.c, create a one-line Makefile and execute it as follows: |
As you see in the lsmod output, modprobe inserts three modules rather than one. modprobe first figures out that it has to insert /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko. But when it peeks into the dependency file /lib/modules/X.Y.Z/modules.dep, it finds the following line and realizes that it has to load two other dependent modules first: /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko: /lib/modules/X.Y.Z/kernel/fs/fat/fat.ko /lib/modules/X.Y.Z/kernel/fs/nls/nls_base.ko It then proceeds to load fat.ko and nls_base.ko before attempting to insert vfat.ko, thus automatically loading all the modules you need to mount your VFAT partition. Use the modinfo utility to extract verbose information about the modules you just loaded: bash> modinfo vfat filename: /lib/modules/X.Y.Z/kernel/fs/vfat/vfat.ko license: GPL description: VFAT filesystem support ... depends: fat, nls_base To compile a kernel driver as a module, toggle the corresponding menu choice button to <M> while configuring the kernel. Most of the device driver examples in this book are implemented as kernel modules. To build a module mymodule.ko from its source file mymodule.c, create a one-line Makefile and execute it as follows: |
6/25/2008 |
2 | p 18 | The first-level kernel initializations are done in real mode assembly. Subsequent startup is performed in protected mode by the function start_kernel() defined in init/main.c, the source file you modified in the previous chapter. start_-kernel() begins by initializing the CPU subsystem. | The first-level kernel initializations are done in real mode assembly. Subsequent startup is performed in protected mode by the function start_kernel() defined in init/main.c, the source file you modified in the previous chapter. start_-kernel() begins by initializing the CPU subsystem. | 6/25/2008 |
2 | p 20 | only lines 3, 7, 9, 13, 18, 22, 27, 30, 58, and 60 should be bold | fixed | 6/25/2008 |
2 | p 22 | Command-line arguments affect the code path traversed during boot. As a simple example, assume that the command-line argument of interest is called bootmode. If this parameter is set to 1, you would like to print some debug messages during boot and switch to a runlevel of 3 at the end of the boot. (Wait until the boot messages are printed out by the init process to learn the semantics of runlevels.) If bootmode is instead set to 0, you would prefer the boot to be relatively laconic, and the runlevel set to 2. Because you are already familiar with init/main.c, lets add the following modification to it: |
Command-line arguments affect the code path traversed during boot. As a simple example, assume that the command-line argument of interest is called bootmode. If this parameter is set to 1, you would like to print some debug messages during boot and switch to a runlevel of 3 at the end of the boot. (Wait until the boot messages are printed out by the init process to learn the semantics of runlevels.) If bootmode is instead set to 0, you would prefer the boot to be relatively laconic, and the runlevel set to 2. Because you are already familiar with init/main.c, lets add the following modification to it: |
6/25/2008 |
2 | 23 | During boot, the kernel calculates the number of times the processor can execute an internal delay loop in one jiffy, which is the time interval between two consecutive ticks of the system timer. As you would expect, the calculation has to be calibrated to the processing speed of your CPU. The result of this calibration is stored in a kernel variable called loops_per_jiffy. One place where the kernel makes use of loops_per_jiffy is when a device driver desires to delay execution for small durations in the order of microseconds. |
During boot, the kernel calculates the number of times the processor can execute an internal delay loop in one jiffy, which is the time interval between two consecutive ticks of the system timer. As you would expect, the calculation has to be calibrated to the processing speed of your CPU. The result of this calibration is stored in a kernel variable called loops_per_jiffy. One place where the kernel makes use of loops_per_jiffy is when a device driver desires to delay execution for small durations in the order of microseconds. |
6/25/2008 |
2 | p 24 | The preceding code begins by assuming that loops_per_jiffy is greater than 4096, which translates to a processor speed of roughly one million instructions per second (MIPS). It then waits for a fresh jiffy to start and executes the delay loop, __delay(loops_per_jiffy). If the delay loop outlasts the jiffy, the previous value of loops_per_jiffy (obtained by bitwise right-shifting it by one) fixes its most significant bit (MSB). Otherwise, the function continues by checking whether it will obtain the MSB by bitwise left-shifting loops_per_jiffy. When the kernel thus figures out the MSB of loops_per_jiffy, it works on the lower-order bits and fine-tunes its precision as follows: |
The preceding code begins by assuming that loops_per_jiffy is greater than 4096, which translates to a processor speed of roughly one million instructions per second (MIPS). It then waits for a fresh jiffy to start and executes the delay loop, __delay(loops_per_jiffy). If the delay loop outlasts the jiffy, the previous value of loops_per_jiffy (obtained by bitwise right-shifting it by one) fixes its most significant bit (MSB). Otherwise, the function continues by checking whether it will obtain the MSB by bitwise left-shifting loops_per_jiffy. After the kernel thus figures out the MSB of loops_per_jiffy, it works on the lower-order bits and fine-tunes its precision as follows: |
6/25/2008 |
2 | p 26 | Initramfs, like the page cache over which its built, grows and shrinks dynamically unlike initrd and, hence, reduces memory wastage. Also, unlike initrd, which requires you to include the associated filesystem driver (e.g., EXT2 drivers if you have an EXT2 filesystem on your initrd), initramfs needs no filesystem support. The initramfs code is tiny because its just a small layer on top of the page cache. You can pack your initial root filesystem into a compressed cpio archive and pass it to the kernel command line using the initrd= argument or build it as part of the kernel image using the INITRAMFS_SOURCE menu option during kernel configuration. With the latter, you may either provide the filename of a cpio archive or the path name to a directory tree containing your initramfs layout. During boot, the kernel extracts the files into an initramfs root filesystem (also called rootfs) and executes a top-level /init program if it finds one. This method of obtaining an initial rootfs is especially useful for embedded platforms, where all system resources are at a premium. To create an initramfs image, use mkinitramfs. Look at Documentation/filesystems/ramfs-rootfs-initramfs.txt for more documentation. In this case, we are using initramfs by supplying a compressed cpio archive of the initial root filesystem to the kernel using the initrd= command-line argument. After unpacking the contents of the archive into rootfs, the kernel frees the memory where the archive resides (387K in this case) and announces the above boot message. The freed pages are then doled out to other parts of the kernel that request memory. As discussed in Chapter 18, initrd and initramfs are sometimes used to hold the actual root filesystem on embedded devices during development. |
Initramfs, like the page cache over which its built, grows and shrinks dynamically unlike initrd, and hence reduces memory wastage. Also, unlike initrd, which requires you to include the associated filesystem driver (e.g., EXT2 drivers if you have an EXT2 filesystem on your initrd), initramfs needs no filesystem support. The initramfs code is tiny because its just a small layer on top of the page cache. You can pack your initial root filesystem into a compressed cpio archive and pass it to the kernel command line using the initrd= argument or build it as part of the kernel image using the INITRAMFS_SOURCE menu option during kernel configuration. With the latter, you may either provide the filename of a cpio archive or the path name to a directory tree containing your initramfs layout. During boot, the kernel extracts the files into an initramfs root filesystem (also called rootfs) and executes a top-level /init program if it finds one. This method of obtaining an initial rootfs is especially useful for embedded platforms, where all system resources are at a premium. To create an initramfs image, use mkinitramfs. Look at Documentation/filesystems/ramfs-rootfs-initramfs.txt for more documentation. In this case, we are using initramfs by supplying a compressed cpio archive of the initial root filesystem to the kernel using the initrd= command-line argument. After unpacking the contents of the archive into rootfs, the kernel frees the memory where the archive resides (387K in this case) and announces the above boot message. The freed pages are then doled out to other parts of the kernel that request memory. As we will discuss in Chapter 18, initrd and initramfs are sometimes used to hold the actual root filesystem on embedded devices during development. |
6/25/2008 |
2 | p 27 | The main goal of an I/O scheduler is to increase system throughput by minimizing disk seek times, which is the latency to move the disk head from its existing position to the disk sector of interest. The 2.6 kernel provides four different I/O schedulers: Deadline, Anticipatory, Complete Fair Queuing, and Noop. As the preceding kernel message indicates, the kernel sets Anticipatory as the default I/O scheduler. We look at I/O scheduling in Chapter 14, Block Drivers. |
The main goal of an I/O scheduler is to increase system throughput by minimizing disk seek times, which is the latency to move the disk head from its existing position to the disk sector of interest. The 2.6 kernel provides four different I/O schedulers: Deadline, Anticipatory, Complete Fair Queuing, and Noop. As the preceding kernel message indicates, the kernel sets Anticipatory as the default I/O scheduler. We look at I/O scheduling in Chapter 14, Block Drivers. |
6/25/2008 |
2 | p 30 | The kernel accomplishes useful work using a combination of process contexts and interrupt contexts. Kernel code that services system calls issued by user applications runs on behalf of the corresponding application processes and is said to execute in process context. Interrupt handlers, on the other hand, run asynchronously in interrupt context. Processes contexts are not tied to any interrupt context and vice versa. | The kernel accomplishes useful work using a combination of process contexts and interrupt contexts. Kernel code that services system calls issued by user applications runs on behalf of the corresponding application processes and is said to execute in process context. Interrupt handlers, on the other hand, run asynchronously in interrupt context. Processes contexts are not tied to any interrupt context and vice versa. | 6/25/2008 |
2 | p 31 | The working of many parts of the kernel is critically dependent on the passage of time. The Linux kernel makes use of different timers provided by the hardware to provide time-dependent services such as busy-waiting and sleep-waiting. The processor wastes cycles while it busy-waits but relinquishes the CPU when it sleep-waits. Naturally, the former is done only when the latter is not feasible. The kernel also facilitates scheduling of functions that desire to run after a specified time duration has elapsed. |
The working of many parts of the kernel is critically dependent on the passage of time. The Linux kernel makes use of different timers supported by the hardware to provide time-dependent services such as busy-waiting and sleep-waiting. The processor wastes cycles while it busy-waits but relinquishes the CPU when it sleep-waits. Naturally, the former is done only when the latter is not feasible. The kernel also facilitates scheduling of functions that desire to run after a specified time duration has elapsed. |
6/25/2008 |
2 | p 35 | You may use mod_timer() to change the expiration of my_timer, del_timer() to cancel my_timer, and timer_pending() to see whether my_timer is pending at the moment. If you look at kernel/timer.c, you will find that schedule_timeout() internally uses these same APIs. |
You may use mod_timer() to change the expiration of my_timer, del_timer() to cancel my_timer, and timer_pending() to see whether my_timer is pending at the moment. If you look at kernel/timer.c, you will find that schedule_timeout() internally uses these same APIs. |
6/25/2008 |
2 | p 36 | Busy-waiting for short durations is accomplished by measuring the time the processor takes to execute an instruction and looping for the necessary number of iterations. As discussed earlier in this chapter, the kernel performs this measurement during boot and stores the value in a variable called loops_per_jiffy. The short-delay APIs use loops_per_jiffy to decide the number of times they need to busy-loop. To achieve a 1-microsecond delay during a handshake process, the USB host controller driver, drivers/usb/host/ehci-hcd.c, calls udelay(), which internally uses loops_per_jiffy: | Busy-waiting for short durations is accomplished by measuring the time the processor takes to execute an instruction and looping for the necessary number of iterations. As discussed earlier in this chapter, the kernel performs this measurement during boot and stores the value in a variable called loops_per_jiffy. The short-delay APIs use loops_per_jiffy to decide the number of times they need to busy-loop. To achieve a 1-microsecond delay during a handshake process, the USB host controller driver drivers/usb/host/ehci-hcd.c, calls udelay(), which internally uses loops_per_jiffy: | 6/25/2008 |
2 | p 43 | If preemption is enabled, mere disabling of interrupts wont protect your critical region from being trampled over. There is the possibility of multiple threads simultaneously entering the critical section in process context. Referring back to Figure 2.4 in this scenario, Thread A and Thread B now need to protect themselves against each other in addition to guarding against Thread C. The solution apparently, is to disable kernel preemption before the start of the critical section and reenable it at the end, in addition to disabling/reenabling interrupts. For this, Thread A and Thread B use the irq variant of spinlocks: |
If preemption is enabled, mere disabling of interrupts wont protect your critical region from being trampled over. There is the possibility of multiple threads simultaneously entering the critical section in process context. Referring back to Figure 2.4 in this scenario, Thread A and Thread B now need to protect themselves from each other in addition to guarding against Thread C. The solution apparently, is to disable kernel preemption before the start of the critical section and reenable it at the end, in addition to disabling/reenabling interrupts. For this, Thread A and Thread B use the irq variant of spinlocks: |
6/25/2008 |
2 | p 46 | Like regular spinlocks, reader-writer locks also have corresponding irq variantsnamely, read_lock_irqsave(), read_lock_irqrestore(), write_lock_irqsave(), | Like regular spinlocks, reader-writer locks also have corresponding irq variantsread_lock_irqsave(), read_lock_irqrestore(), write_lock_irqsave(), | 6/25/2008 |
2 | p 47 | Sequence locks or seqlocks, introduced in the 2.6 kernel, are reader-writer locks where writers are favored over readers. This is useful if write operations on a variable far outnumber read accesses. An example is the jiffies_64 variable discussed earlier in this chapter. Writer threads do not wait for readers who may be inside a critical section. Because of this, reader threads may discover that their entry inside a critical section has failed and may need to retry: |
Sequence locks or seqlocks, introduced in the 2.6 kernel, are reader-writer locks where writers are favored over readers. This is useful if write operations on a variable far outnumber read accesses. An example is the jiffies_64 variable discussed earlier in this chapter. Writer threads do not wait for readers who may be inside a critical section. Because of this, reader threads may discover that their entry inside a critical section has failed and may need to retry: |
6/25/2008 |
2 | p 49 | Some device drivers have to be aware of the existence of memory zones. In addition, many drivers need the services of memory-allocation functions. In this section, lets briefly discuss both. |
Some device drivers have to be aware of the existence of memory zones. In addition, many drivers need the services of memory allocation functions. In this section, lets briefly discuss both. |
6/25/2008 |
2 | p 51 | 1. GFP_KERNELUsed by process context code to allocate memory. If this flag is specified, kmalloc() is allowed to go to sleep and wait for pages to get freed up. 2. GFP_ATOMICUsed by interrupt context code to get hold of memory. In this mode, kmalloc() is not allowed to sleep-wait for free pages, so the probability of successful allocation with GFP_ATOMIC is lower than with GFP_KERNEL. |
1. GFP_KERNEL: Used by process context code to allocate memory. If this flag is specified, kmalloc() is allowed to go to sleep and wait for pages to get freed up. 2. GFP_ATOMIC: Used by interrupt context code to get hold of memory. In this mode, kmalloc() is not allowed to sleep-wait for free pages, so the probability of successful allocation with GFP_ATOMIC is lower than with GFP_KERNEL. |
6/25/2008 |
2 | p 58 | The events/n threads (where n is the CPU number) help implement work queues, which are another way of deferring work in the kernel. Parts of the kernel that desire deferred execution of work can either create their own work queue or make use of the default events/n worker thread. Work queues are also dissected in Chapter 4. |
The events/n threads (where n is the CPU number) help implement work queues, which are another way of deferring work in the kernel. Parts of the kernel that desire deferred execution of work can either create their own work queue or make use of the default events/n worker thread. Work queues are also dissected in Chapter 4. |
6/25/2008 |
2 | p 63 | User Mode Helpers Mykthread invokes run_umode_handler() in Listing 3.1 to notify user space about detected events: |
User Mode Helpers To notify user space of detected events, mykthread invokes run_umode_handler() in Listing 3.1. |
6/25/2008 |
2 | p 69 | After executing a submitted work function, the worker thread removes the corresponding node from the list using list_del(). Note that mydrv_wq.lock is released and reacquired in the time window when the submitted work function is executed. This is because work functions can go to sleep resulting in potential deadlocks if newly scheduled code tries to acquire the same spinlock. |
After executing a submitted work function, the worker thread removes the corresponding node from the list using list_del(). Note that mydrv_wq.lock is released for the time window during which the submitted work function is executed. This is because work functions can go to sleep resulting in potential deadlocks if newly scheduled code tries to acquire the same spinlock. |
6/25/2008 |
2 | p 74 | If you are using work queues, you will get linker errors unless you declare your module as licensed under GPL. This is because the kernel exports these functions only to GPLed code. If you look at the kernel work queue implementation, you will see this restriction expressed in statements such as this: |
If you are using work queues, you will get linker errors unless you declare your module as licensed under GPL. This is because the kernel exports these functions only to GPLed code. If you look at the kernel work queue implementation, you will see this restriction expressed in statements such as this: | 6/25/2008 |
2 | p 75 | An example user of notifiers is the High-level Data Link Control (HDLC) protocol driver drivers/net/wan/hdlc.c, which registers itself with the net device notifier chain to sense carrier changes. |
An example user of notifiers is the High-level Data Link Control (HDLC) protocol driver drivers/net/wan/hdlc.c, which registers itself with the net device notifier chain to sense carrier changes. |
6/25/2008 |
2 | p 81 | Kthread Helpers Kthread helpers add a coating over the raw thread creation routines and simplify the task of thread management. |
Kthread Helpers Kthread helpers add a coating over the raw thread creation routines and simplify the task of thread management. |
6/25/2008 |
2 | p 82 | - daemonize("my_thread"); - while (1) { + /* Continue work if no other thread has + * invoked kthread_stop() */ + while (!kthread_should_stop()) { /* ... */ - /* Quit if let go */ - if (pink_slip) { - break; - } /* ... */ } __set_current_state(TASK_RUNNING); remove_wait_queue(&my_thread_wait, &wait); |
- daemonize("my_thread"); - while (1) { + /* Continue work if no other thread has + * invoked kthread_stop() */ + while (!kthread_should_stop()) { /* ... */ - /* Quit if let go */ - if (pink_slip) { - break; - } /* ... */ } /* Bail out of the wait queue */ __set_current_state(TASK_RUNNING); remove_wait_queue(&my_thread_wait, &wait); |
6/25/2008 |
2 | p 97 | spinlock_t roller_lock = SPIN_LOCK_UNLOCKED; static DECLARE_WAIT_QUEUE_HEAD(roller_poll); static irqreturn_t roller_interrupt(int irq, void *dev_id) { int i, PA_t, PA_delta_t, movement = 0; /* Get the waveforms from bits 0, 1 and 2 of Port D as shown in Figure 4.3 */ PA_t = PORTD & 0x07; /* Wait until the state of the pins change. (Add some timeout to the loop) */ for (i=0; (PA_t==PA_delta_t); i++){ PA_delta_t = PORTD & 0x07; } |
spinlock_t roller_lock = SPIN_LOCK_UNLOCKED; static DECLARE_WAIT_QUEUE_HEAD(roller_poll); static irqreturn_t roller_interrupt(int irq, void *dev_id) { int i, PA_t, PA_delta_t, movement = 0; /* Get the waveforms from bits 0, 1 and 2 of Port D as shown in Figure 4.3 */ PA_t =PA_delta_t = PORTD & 0x07; /* Wait until the state of the pins change. (Add some timeout to the loop) */ for (i=0; (PA_t==PA_delta_t); i++){ PA_delta_t = PORTD & 0x07; } |
6/25/2008 |
2 | p 99 | Lets end this section by introducing some functions that enable and disable interrupts on a particular IRQ. enable_irq(ROLLER_IRQ) enables interrupt generation when the roller wheel moves, while disable_irq(ROLLER_IRQ) does the reverse. disable_irq_nosync(ROLLER_IRQ) disables roller interrupts but does not wait for any currently executing instance of roller_interrupt() to return. This nosync flavor of disable_irq() is faster but can potentially cause race conditions. Use this only when you know that there can be no races. An example user of disable_irq_nosync() is drivers/ide/ide-io.c, which blocks interrupts during initialization, because some systems have trouble with that. |
Lets end this section by introducing some functions that enable and disable interrupts on a particular IRQ. enable_irq(ROLLER_IRQ) enables interrupt generation when the roller wheel moves, while disable_irq(ROLLER_IRQ) does the reverse. disable_irq_nosync(ROLLER_IRQ) disables roller interrupts but does not wait for any currently executing instance of roller_interrupt() to return. This nosync flavor of disable_irq() is faster but can potentially cause race conditions. Use this only when you know that there can be no races. An example user of disable_irq_nosync() is drivers/ide/ide-io.c, which blocks interrupts during initialization, because some systems have trouble with that. |
6/25/2008 |
2 | p 101 | Listing 4.3 Using Tasklets to Offload Work from Interrupt Handlers struct roller_device_struct { /* Device-specific structure */ /* ... */ struct tasklet_struct tsklt; /* ... */ } void __init roller_init() { |
Listing 4.3 Using Tasklets to Offload Work from Interrupt Handlers struct roller_device_struct { /* Device-specific structure */ /* ... */ struct tasklet_struct tsklt; /* ... */ }; void __init roller_init() { |
6/25/2008 |
2 | p 104 | To understand how to use udev, lets look at an example. Assume that you have a USB DVD drive and a USB CD-RW drive. Depending on the order in which you hotplug these devices, one of them is assigned the name /dev/sr0, and the other gets the name /dev/sr1. During pre-udev days, you had to figure out the associated names before you could use the devices. But with udev, you can consistently view the DVD (as say, /dev/usbdvd) and the CD-RW (as say, /dev/usbcdrw) irrespective of the order in which they are plugged in or out. |
To understand how to use udev, lets look at an example. Assume that you have a USB DVD drive and a USB CD-RW drive. Depending on the order in which you hotplug these devices, one of them is assigned the name /dev/sr0, and the other gets the name /dev/sr1. During pre-udev days, you had to figure out the associated names before you could use the devices. But with udev, you can consistently view the DVD (as say, /dev/usbdvd) and the CD-RW (as say, /dev/usbcdrw) irrespective of the order in which they are plugged in or out. |
6/25/2008 |
2 | p 106 | /dev/usbdvd, and your CD-RW drive always appears as /dev/usbcdrw. You can deterministically mount them from shell scripts using commands such as these: |
/dev/usbdvd, and your CD-RW drive always appears as /dev/usbcdrw. You can deterministically mount them from shell scripts using commands such as this: |
6/25/2008 |
2 | p 110 | bus_register() adds a corresponding entry to /sys/bus/, while device_-register() adds entries under /sys/devices/. struct bus_type, struct device, and struct device_driver are the main data structures used respectively by buses, devices, and drivers. Take a peek inside include/linux/device.h for their definitions. |
bus_register() adds a corresponding entry to /sys/bus/, while device_-register() adds entries under /sys/devices/. struct bus_type, struct device, and struct device_driver are the main data structures used respectively by buses, devices, and drivers. Take a peek inside include/linux/device.h for their definitions. |
6/25/2008 |
2 | p 114 | Power management is critical on devices running on battery, such as laptops and handhelds. Linux drivers need to be aware of power states and have to transition across states in response to events such as standby, sleep, and low battery. Drivers utilize power-saving features supported by the underlying hardware when they switch to modes that consume less power. For example, the storage driver spins down the disk, whereas the video driver blanks the display. |
Power management is critical on devices running on battery, such as laptops and handhelds. Linux drivers need to be aware of power states and have to transition across states in response to events such as standby, sleep, and low battery. Drivers utilize power-saving features supported by the underlying hardware when they switch to modes that consume less power. For example, the storage driver spins down the disk, whereas the video driver blanks the display. |
6/25/2008 |
2 | p 115 | For a fuller understanding of how APM is implemented on x86 Linux, look at arch/x86/kernel/apm_32.c, include/linux/apm_bios.h, and include/asm-x86/mach-default/apm.h in the kernel tree. If you are curious to know how APM is implemented on BIOS-less architectures such as ARM, look at include/linux/apm-emulation.h and its users. The kernels ACPI implementation lives in drivers/acpi/. |
For a fuller understanding of how APM is implemented on x86 Linux, look at arch/x86/kernel/apm_32.c, include/linux/apm_bios.h, and include/asm-x86/mach-default/apm.h in the kernel tree. If you are curious to know how APM is implemented on BIOS-less architectures such as ARM, look at include/linux/apm-emulation.h and its users. The kernels ACPI implementation lives in drivers/acpi/. |
6/25/2008 |
2 | p 121 | Lets implement a char driver to access the system CMOS. The BIOS on PC-compatible hardware (see Figure 5.1) uses the CMOS to store information such as startup options, boot order, and the system date, which you can configure via the BIOS setup menu. Our example CMOS driver lets you access the two PC CMOS banks as though they are regular files. Applications can operate on /dev/cmos/0 and /dev/cmos/1, and use I/O system calls to access data from the two banks. Because the BIOS assigns semantics to the CMOS area at bit-level granularity, the driver is capable of bit-level access. So, a read() obtains the specified number of bits and advances the internal file pointer by the number of bits read. |
Lets implement a char driver to access the system CMOS. The BIOS on PC-compatible hardware (see Figure 5.1) uses the CMOS to store information such as startup options, boot order, and the system date, which you can configure via the BIOS setup menu. Our example CMOS driver lets you access the two PC CMOS banks as though they are regular files. Applications can operate on /dev/cmos/0 and /dev/cmos/1, and use I/O system calls to access data from the two banks. Because the BIOS assigns semantics to the CMOS area at bit-level granularity, the driver is capable of bit-level access. So, a read() obtains the specified number of bits and advances the internal file pointer by the number of bits read. |
6/25/2008 |
2 | p 123 | Listing 5.1CMOS Driver Initialization #include <linux/fs.h> /* Per-device (per-bank) structure */ struct cmos_dev { unsigned short current_pointer; /* Current pointer within the bank */ unsigned int size; /* Size of the bank */ int bank_number; /* CMOS bank number */ struct cdev cdev; /* The cdev structure */ char name[10]; /* Name of I/O region */ /* ... */ /* Mutexes, spinlocks, wait queues, .. */ } *cmos_devp; |
Listing 5.1CMOS Driver Initialization #include <linux/fs.h> #include <linux/cdev.h> /* Per-device (per-bank) structure */ struct cmos_dev { unsigned short current_pointer; /* Current pointer within the bank */ unsigned int size; /* Size of the bank */ int bank_number; /* CMOS bank number */ struct cdev cdev; /* The cdev structure */ char name[10]; /* Name of I/O region */ /* ... */ /* Mutexes, spinlocks, wait queues, .. */ } *cmos_devp; |
6/25/2008 |
2 | p 124 | for (i=0; i<NUM_CMOS_BANKS; i++) { /* Allocate memory for the per-device structure */ cmos_devp = kmalloc(sizeof(struct cmos_dev), GFP_KERNEL); if (!cmos_devp) { printk("Bad Kmalloc\n"); return 1; } |
for (i=0; i<NUM_CMOS_BANKS; i++) { /* Allocate memory for the per-device structure */ cmos_devp = kmalloc(sizeof(struct cmos_dev), GFP_KERNEL); if (!cmos_devp) { printk("Bad Kmalloc\n"); return -ENOMEM; } |
6/25/2008 |
2 | p 125 | /* Connect the major/minor number to the cdev */ if (cdev_add(&cmos_devp->cdev, (dev_number + i), 1)) { printk("Bad cdev\n"); return 1; } /* Send uevents to udev, so it'll create /dev nodes */ class_device_create(cmos_class, NULL, (dev_number + i), NULL, "cmos%d", i); } printk("CMOS Driver Initialized.\n"); return 0; } /* Driver Exit */ void __exit cmos_cleanup(void) { int i; /* Remove the cdev */ cdev_del(&cmos_devp->cdev); /* Release the major number */ unregister_chrdev_region(MAJOR(dev_number), NUM_CMOS_BANKS); /* Release I/O region */ for (i=0; i<NUM_CMOS_BANKS; i++) { class_device_destroy(cmos_class, MKDEV(MAJOR(dev_number), i)); release_region(addrports[i], 2); } |
/* Connect the major/minor number to the cdev */ if (cdev_add(&cmos_devp->cdev, (cmos_dev_number + i), 1)) { printk("Bad cdev\n"); return 1; } /* Send uevents to udev, so it'll create /dev nodes */ class_device_create(cmos_class, NULL, (cmos_dev_number + i), NULL, "cmos%d", i); } printk("CMOS Driver Initialized.\n"); return 0; } /* Driver Exit */ void __exit cmos_cleanup(void) { int i; /* Remove the cdev */ cdev_del(&cmos_devp->cdev); /* 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++) { class_device_destroy(cmos_class, (cmos_dev_number) + i)) release_region(addrports[i], 2); } |
6/25/2008 |
2 | p 126 | First, cmos_init() invokes alloc_chrdev_region() to dynamically request an unused major number. dev_number contains the allotted major number if the call is successful. The second and third arguments to alloc_chrdev_region() specify the start minor number and the number of supported minor devices, respectively. The last argument is the device name used to identify the CMOS in /proc/devices: |
First, cmos_init() invokes alloc_chrdev_region() to dynamically request an unused major number. cmos_dev_number contains the allotted device number if the call is successful. The second and third arguments to alloc_chrdev_region() specify the start minor number and the number of supported minor devices, respectively. The last argument is the device name used to identify the CMOS in /proc/devices: | 6/25/2008 |
2 | p 127 | Device drivers that need to operate on a range of I/O addresses stake claim to the addresses via a call to request_region(). This regulatory mechanism ensures that requests by others for the same region fail until the occupant releases it via a call to release_region(). request_region() is commonly invoked by I/O bus drivers such as PCI and ISA to mark ownership of on-card memory in the processors address space (more on this in Chapter 10, Peripheral Component Interconnect). cmos_init() requests access to the I/O region of each CMOS bank by calling request_region(). The last argument to request_region() is an identifier used by /proc/ioports, so you will see this if you peek at that file: |
Device drivers that need to operate on a range of I/O addresses stake claim to the addresses via a call to request_region(). This regulatory mechanism ensures that requests by others for the same region fail until the occupant releases it via a call to release_region(). request_region() is commonly invoked by I/O bus drivers such as PCI and ISA to mark ownership of on-card memory in the processors address space (more on this in Chapter 10, Peripheral Component Interconnect). cmos_init() requests access to the I/O region of each CMOS bank by calling request_region(). The last argument to request_region() is an identifier used by /proc/ioports, so you will see this if you peek at that file: |
6/25/2008 |
2 | p 140 | You may supply a bunch of file descriptors to select() and ask it to keep an eye on them until there is a change in the associated data state. You may also request a timeout to override data availability. If you ask for a timeout of NULL, select() blocks forever. Refer to the man or info pages of select() for detailed documentation. The call to select() in the preceding snippet induces the X server to poll for data from a connected mouse within a timeout. |
You may supply a bunch of file descriptors to select() and ask it to keep an eye on them until there is a change in the associated data state. You may also request a timeout to override data availability. If you ask for a timeout of NULL, select() blocks forever. Refer to the man or info pages of select() for detailed documentation. The call to select() in the preceding snippet induces the X server to poll for data from a connected mouse within a timeout. |
6/25/2008 |
2 | p 141 | Most I/O system calls are POSIX-compliant and are not Linux-specific (programs such as X Windows after all, run on many UNIX flavors, not just on Linux), but the internal driver methods are operating system-specific. On Linux, the poll() driver method is the pillar under the select() system call. In the previous X server scenario, the mouse drivers poll() method looks like this: |
Most I/O system calls are POSIX-compliant and are not Linux-specific (programs such as X Windows after all, run on many UNIX flavors, not just on Linux), but the internal driver methods are specific to the operating system. On Linux, the poll() driver method is the pillar under the select() system call. In the previous X server scenario, the mouse drivers poll() method looks like this: |
6/25/2008 |
2 | p 150 | unregister_chrdev_region(MAJOR(dev_number), 1); class_device_destroy(led_class, MKDEV(MAJOR(dev_number), 0)); class_destroy(led_class); return; } module_init(led_init); module_exit(led_cleanup); MODULE_LICENSE("GPL"); led_init() is similar to cmos_init() developed in Listing 5.1, but for a couple of things: 1. As you saw in Chapter 4, the new device model distinguishes between drivers and devices. led_init() registers the LED driver with parport via a call to -parport_register_driver().When the kernel finds the LED board during led_attach(), it registers the device by invoking parport_register_device(). 2. led_init() creates the device node /dev/led, which you can use to control the state of individual LEDs. |
unregister_chrdev_region(dev_number, 1); class_device_destroy(led_class,dev_number); class_destroy(led_class); return; } module_init(led_init); module_exit(led_cleanup); MODULE_LICENSE("GPL"); led_init() is similar to cmos_init() developed in Listing 5.1, but for a couple of things: 1. As you saw in Chapter 4, the new device model distinguishes between drivers and devices. led_init() registers the LED driver with parport via a call to -parport_register_driver().When the kernel finds the LED board during led_attach(), it registers the device by invoking parport_register_device(). 2. led_init() creates the device node /dev/led, which you can use to control the state of individual LEDs. |
6/25/2008 |
2 | p 154 | /* Driver Initialization */ int __init led_init(void) { struct class_device *c_d; /* Create the pardevice class - /sys/class/pardevice */ led_class = class_create(THIS_MODULE, "pardevice"); if (IS_ERR(led_class)) printk("Bad class create\n"); |
int __init led_init(void) { struct class_device *c_d; If (alloc_chrdev_region (&dev_number, 0, 1, DEVICE_NAME < 0) { printk(KERN_DEBUG Cant register new device\n); return -1 /* Create the pardevice class - /sys/class/pardevice */ led_class = class_create(THIS_MODULE, "pardevice"); if (IS_ERR(led_class)) printk("Bad class create\n"); |
6/25/2008 |
2 | p 155 | /* Destroy class device corresponding to /sys/class/pardevice/led/ */ class_device_destroy(led_class, MKDEV(MAJOR(dev_number), 0)); |
/* Destroy class device corresponding to /sys/class/pardevice/led/ */ class_device_destroy(led_class, dev_number); |
6/25/2008 |
2 | p 157 | in Documentation/rtc.txt, is a set of standard ioctls that conforming applications such as hwclock leverage by operating on /dev/rtc. The API also specifies attributes in sysfs (/sys/class/rtc/) and procfs (/proc/driver/rtc). The RTC API guarantees that user space tools are independent of the underlying platform and the RTC chip. The bottom-layer RTC driver is bus-specific. The embedded device discussed in the section Device Example: Real Time Clock in Chapter 8, The Inter-Integrated Circuit Protocol, has an RTC chip connected to the I2C bus, which is driven by an I2C client driver. The kernel has a dedicated RTC subsystem that provides the top-layer char driver and a core infrastructure that bottom-layer RTC drivers can use to tie in with the top layer. The main components of this infrastructure are the rtc_class_ops structure and the registration functions, rtc_device_[register|unregister](). Bottom-layer RTC drivers scattered under different bus-specific directories are being unified with this subsystem under drivers/rtc/. The RTC subsystem allows the possibility that a system can have more than one RTC. It does this by exporting multiple interfaces, /dev/rtcN and /sys/class/rtc/rtcN, where N is the number of RTCs on your system. Some embedded systems, for example, have two RTCs: one built in to the microcontroller to support sophisticated operations such as periodic interrupt generation, and another no-frills low-power battery-backed external RTC for timekeeping. Because RTC-aware applications operate over /dev/rtc, set up a symbolic link so that one of the created /dev/rtcX nodes can be accessed as /dev/rtc. To enable the RTC subsystem, turn on CONFIG_RTC_CLASS during kernel configuration. The Legacy PC RTC Driver On PC systems, you have the option of bypassing the RTC subsystem by using the legacy RTC driver, drivers/char/rtc.c. This driver provides top and bottom layers for the RTC on PC--compatible systems and exports /dev/rtc and /proc/driver/rtc to user applications. To enable this driver, turn on CONFIG_RTC during kernel configuration. Pseudo Char Drivers Several commonly used kernel facilities are not connected with any physical hardware, and these are elegantly implemented as char devices. The null sink, the perpetual zero |
in Documentation/rtc.txt, is a set of standard ioctls that conforming applications such as hwclock leverage by operating on /dev/rtc. The API also specifies attributes in sysfs (/sys/class/rtc/) and procfs (/proc/driver/rtc). The RTC API guarantees that user space tools are independent of the underlying platform and the RTC chip. The bottom-layer RTC driver is bus-specific. The embedded device discussed in the section Device Example: Real Time Clock in Chapter 8, The Inter-Integrated Circuit Protocol, has an RTC chip connected to the I2C bus, which is driven by an I2C client driver. The kernel has a dedicated RTC subsystem that provides the top-layer char driver and a core infrastructure that bottom-layer RTC drivers can use to tie in with the top layer. The main components of this infrastructure are the rtc_class_ops structure and the registration functions, rtc_device_[register|unregister](). Bottom-layer RTC drivers scattered under different bus-specific directories are being unified with this subsystem under drivers/rtc/. The RTC subsystem allows the possibility that a system can have more than one RTC. It does this by exporting multiple interfaces, /dev/rtcN and /sys/class/rtc/rtcN, where N is the number of RTCs on your system. Some embedded systems, for example, have two RTCs: one built in to the microcontroller to support sophisticated operations such as periodic interrupt generation, and another no-frills low-power battery-backed external RTC for timekeeping. Because RTC-aware applications operate over /dev/rtc, set up a symbolic link so that one of the created /dev/rtcX nodes can be accessed as /dev/rtc. To enable the RTC subsystem, turn on CONFIG_RTC_CLASS during kernel configuration. The Legacy PC RTC Driver On PC systems, you have the option of bypassing the RTC subsystem by using the legacy RTC driver, drivers/char/rtc.c. This driver provides top and bottom layers for the RTC on PC--compatible systems and exports /dev/rtc and /proc/driver/rtc to user applications. To enable this driver, turn on CONFIG_RTC during kernel configuration. Pseudo Char Drivers Several commonly used kernel facilities are not connected with any physical hardware, but are elegantly implemented as char devices. The null sink, the perpetual zero |
6/25/2008 |
2 | p 158 | Instead, it gathers environmental noise (interval between interrupts, key clicks, and so on) for maintaining a reservoir of disorder (called an entropy pool) that seeds the random stream. To see the kernels input subsystem (discussed in Chapter 7) contributing to the entropy pool when it detects a keyboard press or mouse movement, look at input_event() defined in drivers/input/input.c: |
Instead, it gathers environmental noise (interval between interrupts, key clicks, and so on) for maintaining a reservoir of disorder (called an entropy pool) that seeds the random stream. To see the kernels input subsystem (discussed in Chapter 7) contributing to the entropy pool when it detects a keyboard press or mouse movement, look at input_event() defined in drivers/input/input.c: |
6/25/2008 |
2 | p 159 | To see how the core interrupt handling layer contributes inter-interrupt periods to the entropy pool, look at handle_IRQ_event() defined in kernel/irq/handle.c: irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) { /* ... */ if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); /* Contribute to entropy pool */ /* ... */ } The generation of strongly random numbers depends on the size of the entropy pool: bash> od x /dev/random 0000000 7331 9028 7c89 4791 7f64 3deb 86b3 7564 0000020 ebb9 e806 221a b8f9 af12 cb30 9a0e cc28 0000040 68d8 0bbf 68a4 0898 528e 1557 d8b3 57ec 0000060 b01d 8714 b1e1 19b9 0a86 9f60 646c c269 The output stops after a few lines, signaling that the entropy pool is exhausted. To replenish the entropy pool and restart the random stream, jab the keyboard several times after switching to an unused terminal or push the mouse around the screen. A dump of /dev/urandom, however, produces a continuous pseudo random stream that never stops. /dev/mem and /dev/kmem are classic pseudo char devices that are tools that let you peek inside system memory. These char nodes export raw interfaces connected to physical memory and kernel virtual memory, respectively. To manipulate system memory, you may mmap() these nodes and operate on the returned regions. As an exercise, change the hostname of your system by accessing /dev/mem. All the char devices discussed in this section (null, zero, random, urandom, mem, and kmem) have different minor numbers but the same statically assigned major number, 1. Look at drivers/char/mem.c and drivers/char/random.c for their implementation. Two other pseudo drivers belong to the same major number family: /dev/full, which emulates an always full device; and /dev/port, which peeks at system I/O ports. We use the latter in Chapter 19. |
To see how the core interrupt handling layer contributes inter-interrupt periods to the entropy pool, look at handle_IRQ_event() defined in kernel/irq/handle.c: irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) { /* ... */ if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); /* Contribute to entropy pool */ /* ... */ } The generation of strongly random numbers depends on the size of the entropy pool: bash> od x /dev/random 0000000 7331 9028 7c89 4791 7f64 3deb 86b3 7564 0000020 ebb9 e806 221a b8f9 af12 cb30 9a0e cc28 0000040 68d8 0bbf 68a4 0898 528e 1557 d8b3 57ec 0000060 b01d 8714 b1e1 19b9 0a86 9f60 646c c269 The output stops after a few lines, signaling that the entropy pool is exhausted. To replenish the entropy pool and restart the random stream, jab the keyboard several times after switching to an unused terminal or push the mouse around the screen. A dump of /dev/urandom, however, produces a continuous pseudo random stream that never stops. /dev/mem and /dev/kmem are classic pseudo char devices that are tools that let you peek inside system memory. These char nodes export raw interfaces connected to physical memory and kernel virtual memory, respectively. To manipulate system memory, you may mmap() these nodes and operate on the returned regions. As an exercise, change the hostname of your system by accessing /dev/mem. All the char devices discussed in this section (null, zero, random, urandom, mem, and kmem) have different minor numbers but the same statically assigned major number, 1. Look at drivers/char/mem.c and drivers/char/random.c for their implementation. Two other pseudo drivers belong to the same major number family: /dev/full, which emulates an always full device; and /dev/port, which peeks at system I/O ports. We use the latter in Chapter 19. |
6/25/2008 |
2 | p 165 | The drivers write() method pulses the watchdogs input pin whenever application software writes to the associated device node. To aid manufacturing and field diagnostics, the watchdog is wired such that it can be disabled by wiggling a processor GPIO pin. |
The drivers write() method pulses the watchdogs input pin whenever application software writes to the associated device node. To aid manufacturing and field diagnostics, the watchdog may be wired such that it can be disabled by wiggling a processor GPIO pin. |
6/25/2008 |
2 | p 166 | A related support in 2.6 kernels is the sensing of soft lockups, which are instances when scheduling does not occur for 10 or more seconds. A kernel thread watchdog/N, where N is the CPU number, touches a per-CPU timestamp every second. If the thread doesnt touch the timestamp for more than 10 seconds, the system is deemed to have locked up. Soft lockup detection (implemented in kernel/softlockup.c) will aid us while debugging a kernel crash in the section Kdump in Chapter 21, Debugging Device Drivers. There are several more misc drivers in the kernel. The Qtronix infrared keyboard driver, drivers/char/qtronix.c, is another example of a char driver that has a misc form factor. Do a grep on misc_register() in the drivers/char/ directory to find other misc device drivers present in the kernel. Character Caveats Driver methods, and hence the associated system calls issued by user applications, may fail or partially succeed. Your application has to factor this in to avoid unpleasant surprises. Lets look at some common pitfalls: |
A related support in 2.6 kernels is the sensing of soft lockups, which are instances when scheduling does not occur for 10 or more seconds. A kernel thread watchdog/N, where N is the CPU number, touches a per-CPU timestamp every second. If the thread doesnt touch the timestamp for more than 10 seconds, the system is deemed to have locked up. Soft lockup detection (implemented in kernel/softlockup.c) will aid us while debugging a kernel crash in the section Kdump in Chapter 21, Debugging Device Drivers. There are several more misc drivers in the kernel. The Qtronix infrared keyboard driver, drivers/char/qtronix.c, is another example of a char driver that has a misc form factor. Do a grep on misc_register() in the drivers/char/ directory to find other misc device drivers present in the kernel. Character Caveats Driver methods, and hence the associated system calls issued by user applications, may fail or partially succeed. Your application has to factor this in to avoid unpleasant surprises. Lets look at some common pitfalls: |
6/25/2008 |
2 | p 167 | Input drivers are responsible for devices such as keyboards, mice, and joysticks. They live in a separate source directory, drivers/input/ and, hence, get a distinct chapter, Chapter 7. |
Input drivers are responsible for devices such as keyboards, mice, and joysticks. They live in a separate source directory, drivers/input/, and hence get a distinct chapter, Chapter 7. |
6/25/2008 |
2 | p 178 | There are two important steps that a UART driver has to do to tie itself with the kernel: |
There are two important steps that a UART driver has to take to tie itself with the kernel: |
6/25/2008 |
2 | p 181 | 2. A platform driver. The platform driver registers itself into the platform using platform_driver_register(). The platform_driver structure, also de-fined in include/linux/platform_device.h, represents a platform driver: |
2. A platform driver. The platform driver registers itself into the platform using platform_driver_register(). The platform_driver structure, also de-fined in include/linux/platform_device.h, represents a platform driver: |
6/25/2008 |
2 | p 194 | To work with a system console on a Linux desktop, you need the services of virtual terminals (VTs) if you are in text mode or pseudo terminals (PTYs) if you are in graphics mode. VTs and PTYs are implemented as tty drivers and live in drivers/char/vt.c and drivers/char/pty.c, respectively. |
To work with a system console on a Linux desktop, you need the services of virtual terminals (VTs) if you are in text mode or pseudo terminals (PTYs) if you are in graphics mode. VTs and PTYs are implemented as tty drivers and live in drivers/char/vt.c and drivers/char/pty.c, respectively. |
6/25/2008 |
2 | p 205 | The serial core resides in drivers/serial/, but tty implementations and low-level drivers are scattered across the source tree. The driver files referred to in Figure 6.3, for example, live in four different directories: drivers/serial/, drivers/char/, drivers/usb/serial/, and drivers/net/irda/. The drivers/serial/ directory, which now also contains UART drivers, didnt exist in the 2.4 kernel; UART-specific code used to be dispersed between drivers/char/ and arch/your-arch/ directories. The present code partitioning is more logical because UART drivers are not the only folks that access the serial layerdevices such as USB-to-serial converters and IrDA dongles also need to talk to the serial core. |
The serial core resides in drivers/serial/, but tty implementations and low-level drivers are scattered across the source tree. The driver files referred to in Figure 6.3, for example, live in four different directories: drivers/serial/, drivers/char/, drivers/usb/serial/, and drivers/net/irda/. The drivers/serial/ directory, which now also contains UART drivers, didnt exist in the 2.4 kernel; UART-specific code used to be dispersed between drivers/char/ and arch/your-arch/ directories. The present code partitioning is more logical because UART drivers are not the only folks that access the serial layerdevices such as USB-to-serial converters and IrDA dongles also need to talk to the serial core. |
6/25/2008 |
2 | p 208 | Figure 7.1 illustrates the operation of the input subsystem. The subsystem contains two classes of drivers that work in tandem: event drivers and device drivers. Event drivers are responsible for interfacing with applications, whereas device drivers are responsible for low-level communication with input devices. The mouse event generator, mousedev, is an example of the former, and the PS/2 mouse driver is an example of the latter. Both event drivers and device drivers can avail the services of an efficient, bug-free, reusable core, which lies at the heart of the input subsystem. |
Figure 7.1 illustrates the operation of the input subsystem. The subsystem contains two classes of drivers that work in tandem: event drivers and device drivers. Event drivers are responsible for interfacing with applications, whereas device drivers are responsible for low-level communication with input devices. The mouse event generator, mousedev, is an example of the former, and the PS/2 mouse driver is an example of the latter. Both event drivers and device drivers can avail the services of an efficient, bug-free, reusable core, which lies at the heart of the input subsystem. |
6/25/2008 |
2 | p 210 | Listing 7.1 contains coord.c, which continuously generates random X and Y coordinates. Mice, unlike joysticks or touch screens, produce relative coordinates, so that is what coord.c does. The vms driver is shown in Listing 7.2. |
Listing 7.1 contains coord.c, which continuously generates random X and Y coordinates. Mice, unlike joysticks or touch screens, produce relative coordinates, so that is what coord.c does. The vms driver is shown in Listing 7.2. |
6/25/2008 |
2 | p 211 | figure font change | fixed | 6/25/2008 |
2 | p 212 | struct input_dev *vms_input_dev; /* Representation of an input device */ static struct platform_device *vms_dev; /* Device structure */ /* Sysfs method to input simulated coordinates to the virtual mouse driver */ static ssize_t write_vms(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { int x,y; sscanf(buffer, "%d%d", &x, &y); |
struct input_dev *vms_input_dev; /* Representation of an input device */ static struct platform_device *vms_dev; /* Device structure */ /* Sysfs method to input simulated coordinates to the virtual mouse driver */ static ssize_t write_vms(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { int x,y; sscanf(buffer, "%d%d", &x, &y); |
6/25/2008 |
2 | p 213 | vms_dev = platform_device_register_simple("vms", -1, NULL, 0); if (IS_ERR(vms_dev)) { PTR_ERR(vms_dev); printk("vms_init: error\n"); } /* Create a sysfs node to read simulated coordinates */ sysfs_create_group(&vms_dev->dev.kobj, &vms_attr_group); |