************************************************************ * * * Guide To Linux Driver Writing -- Character Devices * * * * or, * * * * The Wacky World of Driver Development (I) * * * * Last Revision: Apr 11, 1993 * * * ************************************************************ This document (C) 1993 Robert Baruch. This document may be freely copied as long as the entire title, copyright, this notice, and all of the introduction are included along with it. Suggestions, criticisms, and comments to baruch@nynexst.com. This document, nor the work performed by Robert Baruch using Linux, nor the results of said work are connected in any way to any of the Nynex companies. Information may settle during transportation. This product should not be used in conjunction with a dietary regime except under supervision by your doctor. Right, now that that's over with, let's get into the fun stuff! ======================== Introduction ======================== There is a companion guide to this Guide, the Linux Character Device Tutorial. This tutorial contains working examples of driver code. It introduces the reader gently into each aspect of character device driver writing through experiments which are carried out by the programmer. This Guide should serve as a reference to both beginning and advanced driver writers. -=-=-=-=-=-=- Some words of thanks: Many thanks to: Donald J. Becker (becker@metropolis.super.org) Don Holzworth (donh@gcx1.ssd.csd.harris.com) Michael Johnson (johnsonm@stolaf.edu) Karl Heinz Kremer (khk@raster.kodak.com) All the driver writers! ...and of course, Linus "Linux" Torvalds and all the guys who helped develop Linux into a BLOODY KICKIN' O/S! -=-=-=-=-=-=- ...and now a word of warning: Messing about with drivers is messing with the kernel. Drivers are run at the kernel level, and as such are not subject to scheduling. Further, drivers have access to various kernel structures. Before you actually write a driver, be *damned* sure of what you are doing, lest you end up having to re-format your harddrive and re-install Linux! The information in this Guide is as up-to-date as I could make it. It also has no stamp of approval whatsoever by any of the designers of the kernel. I am not responsible for damage caused to anything as a result of using this Guide. ======================== End of Introduction ======================== Kernal-callable functions: -------------------------- Note: There is no close for a character device. There is only release. See the file data structure below to find out how to determine the number of processes which have the device open. -=-=-=-=-=-=-=- init : Initializes the driver on bootup. unsigned long driver_init(unsigned long kmem_start, unsigned long kmem_end) Arguments: kmem_start -- the start of kernel memory kmem_end -- the end of kernel memory Returns: The new start of kernel memory. This will be different from the kmem_start argument if you want to allocate memory for the driver. The arguments you use depends on what you want to do. Remember that since you are going to add your init function to kernel/chr_dev/mem.c, you can make your call anything you like, but you have access to the kernel memory start and end. Generally, the init function initializes the driver and hardware, and displays some message telling of the availability of the driver and hardware. In addition, the register_chrdev function is usually called here. ************** open : Open a device static int driver_open(struct inode * inode, struct file * file) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device Returns: 0 on success, -errno on error. This function is called whenever a process performs open (or fopen) on the device special file. If there is no open function for the driver, nothing spectacular happens. As long as the /dev file exists, the open will succeed. ************** read : Read from a device static int driver_read(struct inode * inode, struct file * file, char * buffer, int count) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device buffer -- pointer to the buffer in user space to read into count -- the number of bytes to read Returns: -errno on error >=0 : the number of bytes actually read If there is no read function for the driver, read calls will return EINVAL. ************** write : Write to a device static int driver_write(struct inode * inode, struct file * file, char * buffer, int count) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device buffer -- pointer to the buffer in user space to write from count -- the number of bytes to write Returns: -errno on error >=0 : the number of bytes actually written If there is no write function for the driver, write calls will return EINVAL. ************** lseek : Change the position offset of the device static int driver_lseek(struct inode * inode, struct file * file, off_t offset, int origin) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device offset -- offset from origin to move to (bytes) origin -- origin to move from : 0 = from origin 0 (beginning) 1 = from current position 2 = from end Returns: -errno on error >=0 : the position after the move See Also: Data Structure 'file' If there is no lseek function for the driver, the kernel will take the default seek action, which is to alter the file->f_pos element. For origins of 2, the default action results in -EINVAL if file->f_inode is NULL, or it sets file->f_pos to file->f_inode->i_size + offset otherwise. ************** ioctl : Various device-dependent services static int driver_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device cmd -- the user-defined command to perform arg -- the user-defined argument. You may use this as a pointer to user space, since sizeof(long)== sizeof(void *). Returns: -errno on error >=0 : whatever you like! (user-defined) For cmd, FIOCLEX, FIONCLEX, FIONBIO, and FIOASYNC are already defined. See the file linux/fs/ioctl.c, sys_ioctl to find out what they do. If there is no ioctl call for the driver, and the ioctl command performed is not one of the four types listed here, ioctl will return -EINVAL. ************** select : Performs the select call on the device: static int driver_select(struct inode *inode, struct file *file, int sel_type, select_table * wait) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device sel_type -- the select type to perform : SEL_IN (read) SEL_OUT (write) SEL_EX (exception) wait -- see the section "Some Notes" for select. Returns: 0 if the device is not ready to perform the sel_type operation !=0 if it is. See the "Some Notes" section 'way below on information on how to use the select call in drivers. If there is no select call for the driver, select will act as if the driver is ready for the operation. ************** release : Release device (no process holds it open) static void driver_release(struct inode * inode, struct file * file) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device The release call is activated only when the process closes the device as many times as it has opened it. That is, if the process has opened the device five times, then only when close is called for the fifth time will release be called (that is, provided there were no more calls to open!). If there is no release call for the driver, nothing spectacular happens. ************** readdir : Get the next directory entry static int driver_readdir(struct inode *inode, struct file *file, struct dirent *dirent, int count) Arguments: inode -- pointer to the inode structure for this device file -- pointer to the file structure for this device dirent -- pointer to a dirent ("directory entry") structure count -- number of entries to read (currently always 1) Returns: 0 on success -errno on failure. If there is no readdir function for the driver, readdir will return -ENOTDIR. This is really for file systems, but you can probably use it for whatever you like in a non-fs device, as long as you return a dirent structure. See Also: dirent (data structure) ************** mmap : Forget this. According to the source (src/linux/mm/mmap.c), for character devices only /dev/[k]mem may be mapped. Besides, I'm not too clear on what it will do. ---------------------------------------------------------------------- Data structures: ---------------- dirent : Information about files in a directory. #include struct dirent { long d_ino; /* Inode of file */ off_t d_off; unsigned short d_reclen; char d_name[NAME_MAX+1]; /* Name of file */ }; ************** file : Information about open files According to the Hacker's Guide to Linux, this structure is mainly used for writing filesystems, not drivers. However, there is no reason it cannot be used by drivers. #include struct file { mode_t f_mode; dev_t f_rdev; /* needed for /dev/tty */ off_t f_pos; /* Curr. posn in file */ unsigned short f_flags; /* The flags arg passed to open */ unsigned short f_count; /* Number of opens on this file */ unsigned short f_reada; struct inode * f_inode; /* pointer to the inode struct */ struct file_operations * f_op; /* pointer to the fops struct */ }; ************** file_operations : Tells the kernel which function to call for which kernel function. #include struct file_operations { int (*lseek) (struct inode *, struct file *, off_t, int); int (*read) (struct inode *, struct file *, char *, int); int (*write) (struct inode *, struct file *, char *, int); int (*readdir) (struct inode *, struct file *, struct dirent *, int); int (*select) (struct inode *, struct file *, int, select_table *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int); int (*mmap) (void); int (*open) (struct inode *, struct file *); void (*release) (struct inode *, struct file *); int (*fsync) (struct inode *, struct file *); }; ************** inode : Information about the /dev/xxx file (or inode) #include struct inode { dev_t i_dev; unsigned long i_ino; /* Inode number */ umode_t i_mode; /* Mode of the file */ nlink_t i_nlink; uid_t i_uid; gid_t i_gid; dev_t i_rdev; /* Device major and minor numbers */ off_t i_size; time_t i_atime; time_t i_mtime; time_t i_ctime; unsigned long i_blksize; unsigned long i_blocks; struct inode_operations * i_op; struct super_block * i_sb; struct wait_queue * i_wait; struct file_lock * i_flock; struct vm_area_struct * i_mmap; struct inode * i_next, * i_prev; struct inode * i_hash_next, * i_hash_prev; struct inode * i_bound_to, * i_bound_by; unsigned short i_count; unsigned short i_flags; /* Mount flags (see fs.h) */ unsigned char i_lock; unsigned char i_dirt; unsigned char i_pipe; unsigned char i_mount; unsigned char i_seek; unsigned char i_update; union { struct pipe_inode_info pipe_i; struct minix_inode_info minix_i; struct ext_inode_info ext_i; struct msdos_inode_info msdos_i; struct iso_inode_info isofs_i; struct nfs_inode_info nfs_i; } u; }; See Also: Driver Calls: MAJOR, MINOR, IS_RDONLY, IS_NOSUID, IS_NODEV, IS_NOEXEC, IS_SYNC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Driver calls: ************** add_timer : Cause a function to be executed when a given amount of time has passed. #include void add_timer(long jiffies, void (*fn)(void)) Arguments: jiffies -- The number of jiffies to time out after. fn -- The function in kernel space to run after timeout. Note! This is NOT process-specific! If you are looking for a way to have a process go to sleep and timeout, look for ? Excessive use of this function will cause the kernel to panic if there are too many timeouts active at once. ************** cli : Macro, Prevent interrupts from occuring #include #define cli() __asm__ __volatile__ ("cli"::) See Also: sti ************** free_irq : Free a registered interrupt #include void free_irq(unsigned int irq) Arguments: irq -- the interrupt level to free up See Also: request_irq ************** get_fs_byte, get_fs_word, get_fs_long : Get data from user space Purpose: Allows a driver to access data in user space (which is in a different segment than the kernel!) #include inline unsigned char get_fs_byte(const char * addr) inline unsigned short get_fs_word(const unsigned short *addr) inline unsigned long get_fs_long(const unsigned long *addr) Arguments: addr -- the address in user space to get data from Returns: the value in user space. See Also: memcpy_fromfs, memcpy_tofs, put_fs_byte, put_fs_word, put_fs_long ************** inb, inb_p : Inputs a byte from a port #include inline unsigned int inb(unsigned short port) inline unsigned int inb_p(unsigned short port) Arguments: port -- the port to input a byte from Returns: Byte received in the low byte. High byte unused. See Also: outb, outb_p ************** IS_RDONLY, IS_NOSUID, IS_NODEV, IS_NOEXEC, IS_SYNC: Macros, check the status of the device on the filesystem #include #define IS_RDONLY(inode) (((inode)->i_sb) && ((inode)->i_sb->s_flags & MS_RDONLY)) #define IS_NOSUID(inode) ((inode)->i_flags & MS_NOSUID) #define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV) #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC) #define IS_SYNC(inode) ((inode)->i_flags & MS_SYNC) ************** kfree, kfree_s : Free memory which has been kmalloced. #include #define kfree(x) kfree_s((x), 0) void kfree_s(void * obj, int size) Arguments : obj -- pointer to kernel memory you want to free size -- size of block you want to free (0 if you don't know or are lazy -- slows things down) ************** kmalloc : Allocate memory in kernel space #include void * kmalloc(unsigned int len, int priority) Arguments: len -- the length of the memory to allocate. Must not be bigger than 4096. priority -- GFP_KERNEL or GFP_ATOMIC. GFP_ATOMIC causes kmalloc to return NULL if the memory could not be found immediately. GFP_KERNEL is the usual priority. Returns: NULL on failure, a pointer to kernel space on success. ************** memcpy_fromfs, memcpy_tofs : Copies memory from user(fromfs)/kernel(tofs) space to kernel/user space #include inline void memcpy_fromfs(void * to, const void * from, unsigned long n) inline void memcpy_tofs(void * to, const void * from, unsigned long n) Arguments: to -- Address to copy data to from -- Address to copy data from n -- number of bytes to copy See Also: get_fs_byte, get_fs_word, get_fs_long, put_fs_byte, put_fs_word, put_fs_long Warning! Get the order of arguments right! ************** MAJOR, MINOR : Macros, get major/minor device number from inode i_dev entry. #include #define MAJOR(a) (((unsigned)(a))>>8) #define MINOR(a) ((a)&0xff) ************** outb, outb_p : Outputs a byte to a port #include inline void outb(char value, unsigned short port) inline void outb_p(char value, unsigned short port) Arguments: value -- the byte to write out port -- the port to write it out on See Also: inb, inb_p ************** printk : Kernel printf #include int printk(const char *fmt, ...) Arguments: fmt -- printf-style format ... -- var-arguments, printf-style Returns: Number of characters printed. ************** put_fs_byte, put_fs_word, put_fs_long : Put data into user space Purpose: Allows a driver to put a byte, word, or long into user space, which is at a different segment than the kernel. #include inline void put_fs_byte(char val,char *addr) inline void put_fs_word(short val,short * addr) inline void put_fs_long(unsigned long val,unsigned long * addr) Arguments: addr -- the address in user space to get data from Returns: the value in user space. See Also: memcpy_fromfs, memcpy_tofs, get_fs_byte, get_fs_word, get_fs_long Warning! Get the order of arguments right! ************** register_chrdev : Register a character device with the kernel #include #include int register_chrdev(unsigned int major, const char *name, struct file_operations *fops) Arguments: major -- the major device number to register as name -- the name of the device (currently unused) fops -- a file_operations structure for the device. Returns: -EINVAL if major is >= MAX_CHRDEV (defined in fs.h as 32) -EBUSY if major device has already been allocated 0 on success. ************** request_irq : Request to perform a function on an interrupt #include #include int request_irq(unsigned int irq, void (*handler)(int)) Arguments: irq -- the interrupt to request. handler -- the function to handle the interrupt. The interrupt handler should be of the form void handler(int). Unless you really know what you are doing, don't use the int argument. Returns: -EINVAL if irq>15 or handler==NULL -EBUSY if irq is already allocated. 0 on success. See Also: free_irq ************** select_wait : Add a process to the select-wait queue #include inline void select_wait(struct wait_queue ** wait_address, select_table * p) Arguments: wait_address -- Address of a wait_queue pointer p -- Address of a select_table Devices which use select should define a struct wait_queue pointer and initialize it to NULL. select_wait adds the current process to a circular list of waits. The pointer to the circular list is wait_address. If p is NULL, select_wait does nothing, otherwise the current process is put to sleep. See Also: sleep_on, interruptible_sleep_on, wake_up, wake_up_interruptible ************** sleep_on, interruptible_sleep_on : Put the current process to sleep. #include void sleep_on(struct wait_queue ** p) void interruptible_sleep_on(struct wait_queue ** p) Arguments: q -- Pointer to the driver's wait_queue (see select_wait) sleep_on puts the current process to sleep in an uninterruptible state. That is, signals will not wake the process up. The only thing which will wake a process up in this state is a hardware interrupt (which would call the interrupt handler of the driver) -- and even then the interrupt routine needs to call wake_up to put the process in a running state. interruptible_sleep_on puts the current process to sleep in an interruptible state, which means that not only will hardware interrupts get through, but also signals and process timeouts ("alarms") will cause the process to wake up (and execute interrupt or signal handlers). A call to wake_up_interruptible is necessary to wake up the process and allow it to continue running where it left off. See Also: select_wait, wake_up, wake_up_interruptible ************** sti : Macro, Allow interrupts to occur #include #define sti() __asm__ __volatile__ ("sti"::) See Also: cli ************** sys_getegid, sys_getgid, sys_getpid, sys_getppid, sys_getuid, sys_geteuid : Funky functions which get various information about the current process, #include int sys_getegid(void) int sys_getgid(void) int sys_getpid(void) int sys_getppid(void) int sys_getuid(void) int sys_geteuid(void) sys_getegid gets the effective gid of the process. sys_getgid gets the group ID of the process. sys_getpid gets the process ID of the process. sys_getppid gets the process ID of the process' parent. sys_geteuid gets the effective uid of the process. sys_getuid gets the user ID of the process. ************** wake_up, wake_up_interruptible : Wake up _all_ processes waiting on the wait queue. #include void wake_up(struct wait_queue **q) void wake_up_interruptible(struct wait_queue **q) Arguments: q -- Pointer to the driver's wait_queue (see select_wait) See Also: select_wait, sleep_on, interruptible_sleep_on -------------------------------------------------------- ========== Some notes ========== Interrupts, Drivers, and You! ----------------------------- First, a brief exposition on the Meaning of Interrupts. There are three ways by which a program running in the CPU may be interrupted. The first is the external interrupt. This is caused by an external device (that is, external to the CPU) signalling for attention. These are referred to as "interrupt requests" or "IRQs". The second method is the exception, which is caused by something internal to the CPU, usually in response to a condition generated by execution of an instruction. The third method is the software interrupt, which is a deliberately executed interrupt -- the INT instruction in assembly. System calls are implemented using software interrupts; when a system call is desired, Linux places the system call number in EAX, and performs an INT 0x80 instruction. Since drivers usually deal with hardware devices, it is logical that driver interrupts should refer to external interrupts. There are 16 available IRQs -- IRQ0 through IRQ15. The following table lists the official uses of the various IRQs: IRQ0 -- timer 0 IRQ1 -- keyboard IRQ2 -- AT slave 8259 ("cascade") IRQ3 -- COM2 IRQ4 -- COM1 IRQ5 -- LPT2 IRQ6 -- floppy IRQ7 -- LPT1 IRQ8-12 ?????? IRQ13 -- coprocessor error IRQ14,15 ?????? Writing drivers which can be interrupted requires care. Be aware that every line you write can be interrupted, and thus cause variable changes to occur. If you really want to protect critical sections from being interrupted, use the cli() and sti() driver calls. Suppose you wanted to test some kind of funky condition, where success of the condition leads to going to sleep, and being woken up by an interrupt. Consider this code: void driver_interrupt(int unused) { if (!driver_stuff.int_flag) return; /* Spurious interrupts are not unheard of */ driver_stuff.int_flag=0; weird_wacky(); /* Do some weird and wacky stuff here to handle the interrupt */ disable_ints(); /* Disable the device from issuing interrupts */ wake_up(&driver_stuff.wait_queue); /* Sets process to TASK_RUNNING */ } if (conditions_are_ripe()) { driver_stuff.int_flag = 1; enable_ints(); /* Enable device to interrupt us */ sleep_on(&driver_stuff.wait_queue); /* Sets process to TASK_UNINTERRUPTIBLE */ } Assume we just leave the conditions_are_ripe code, determining that the conditions are ripe! We have just enabled the device to interrupt the machine. So we are now about to enter the sleep_on code, and what should happen but the pesky device issues an interrupt. Ka-chunk! and we enter the driver_interrupt routine, which does some weird and wacky stuff to handle the interrupt, and then we disable the device's interrupts. Ka-ching! we enter the wake_up function which sets the process up to run again. Boink! we exit the interrupt handler and commence where we left off (just about to enter the sleep_on code). Vooosh! we're now sleeping the process, awaiting an interrupt which will never occur, since the interrupt handler disabled the device from interrupts! What to do? Use cli() and sti() to protect the critical sections of code: cli(); if (conditions_are_ripe()) { driver_stuff.int_flag = 1; enable_ints(); /* Enable device to interrupt us */ sleep_on(&driver_stuff.wait_queue); /* Sets process to TASK_UNINTERRUPTIBLE */ } else sti(); First we clear interrupts. This is not the same as disabling device interrupts! This actually prevents a hardware interrupt from causing the CPU to execute interrupt code. In effect, the interrupt is deferred. Now we can do our check and perform sleep_on, secure in the knowledge that the interrupt handler cannot be called. The sleep_on (and interruptible_ sleep_on) call has a sti() in it in the right place, so you don't have to worry about calling sti() before sleep_on, and running into a race condition again. Of course, with any interruptible device driver, you must be careful never to spend too much time in the interrupt routine if you are expecting more than one interrupt, because you may miss your second interrupt. -=-=-=-=-=-=- Drivers and signals: -------------------- When a process is sleeping in an interruptible state, any signal can wake it up. This is the sequence of events which occurs when a sleeping process receives a signal: (1) Set current->signal. (2) Set the process to a runnable state. (3) Execute the rest of the driver call. (4) Run the signal handler. (5) If the driver call in step 3 returned -ERESTARTNOHAND or -ERESTARTNOINTR, then return from the driver call with EINTR. If the driver call in step 3 returned -ERESTARTSYS, then restart the driver call. Otherwise, just return with whatever was returned from the driver call. In the driver, you can tell if a sleep has been interrupted by a signal with the following code: if (current->signal & ~current->blocked) { /* Do things based on sleep interrupted by signal */ } -=-=-=-=-=-=- Drivers and timeouts: --------------------- Suppose you wanted to sleep on an interrupt, but also time out after a period of time. You could always use the add_timer, but that's frowned upon because there are only a limited number of timers available -- currently there are 64. The usual solution is to manually alter the current process's timeout: current->timeout = jiffies + X; interruptible_sleep_on(&driver_stuff.wait_queue); (Interruptible sleep_on must be used here to allow a timeout to interrupt the sleep). This will cause the scheduler to set the task running again when X jiffies has gone by. Even if the timeout goes off and the process is allowed to continue running, it is probably a good idea to call wake_up_interruptible in case the process needs to be rescheduled. To find out if it was a timeout which caused the process to wake up, check current->timeout. If it is 0, a timeout occurred. Otherwise it should remain what you set it at. If a timeout did not occur, and something else woke the process up, you should set current->timeout to 0 to prevent the timeout from continuing. The disadvantage of this method is that the process can only have one timeout at a time. Over *all* drivers. -=-=-=-=-=-=- The driver_select call: ----------------------- When a process issues a select call, it is checking to see if the given devices are ready to perform the given operations. For example, suppose you want a driver to have a command written to it, and to disallow further commands until the current command is complete. Well, in the write call you would block commands if there is already a command operating (for example, waiting for a board to do something). But that would require the process to write over and over again until it succeeds. That just burns cycles. The select call allows a process to determine the availability of read and write. In the above example, one merely has to select for write on that device's file descriptor (as returned by open), and the process would be put to sleep until the device is ready to be written to. The kernel will call the driver's driver_select call when the process issues a select call. The arguments to the driver_select call are detailed above. If the wait argument is non-NULL, and there is no error condition caused by the select, driver_select should put the process to sleep, and arrange to be woken up when the device becomes ready (usually through an interrupt). If, however, the wait argument is NULL, then the driver should quickly see if the device is ready, and return even if it is not. The select_wait function does this already for you (see further). Putting the process to sleep does not require calling a sleep_on function. It is the select_wait function which is called, with the p argument being equal to the wait argument passed to driver_select. select_wait is pretty much equivalent to interruptible_sleep_on in that it adds the current process to the wait queue and sleeps the process in an interruptible state. The internals of the differences between select_wait and interruptible_sleep_on are relatively irrelevant here. Suffice it to say that to wake the process up from the select, one needs to perform the wake_up_interruptible call. When that happens, the process is free to run. However, in the case of interruptible_sleep_on, the process will continue running after the call to interruptible_sleep_on. In the case of select_wait, the process does not. driver_select is called as a "side effect" of the select call, and so completes even when it calls select_wait. It is not select_wait which sleeps the process, but the select call. Nevertheless, it is required to call select_wait to add the process to the wait-queue, since select will not do that. All one needs to remember for driver_select is: (1) Call select_wait if the device is not ready, and return 0. (2) Return 1 if the device is ready. Calling select with a timeout is really no different to the driver than calling it without select. But there is one crucial difference. Remember timing out on interrupts above? Well, interrupt timeouts and select timeouts cannot co-exist. They both use current->timeout to wake the process up after a period of time. Remember that! -=-=-=-=-=-=- Installation notes: ------------------- Before you sit down and write your first driver, first make sure you understand how to recompile the kernel. Then go ahead and recompile it! Recompilation of the kernel is described in the FAQ. If you can't recompile the kernel, you can't install your driver into the kernel. [Although I hear tell of a package on sunsite which can load and unload drivers while the kernel is running. Until I test out this package, I won't include instructions for it here.] For character devices, you need to go into the mem.c file in the (source)/linux/kernel/chr_dev directory, to the chr_dev_init function, and add your init function to it. Recompile the kernel, and away you go! (BTW, would you manually have to do a mknod to make the /dev/xxx entry for your driver? Can you do it in the init function?) In general, one installs a device special file in /dev manually, by using mknod: mknod /dev/xxx c major minor If you registered your character driver as major device X, then all accesses to /dev/xxx where major==X will call your driver functions.