This page contains some information I learned about porting device drivers from kernel 2.0 to 2.2 and 2.4. This only includes changes which affected the drivers I wrote. This focusses on the changes necessessary to get older drivers to compile rather than more extensive changes needed to bring drivers up to latest practice.
In the examples below, "stepper" as the prefix of many variable and function names was the name of the driver module.
The driver I was porting used the following parts of the kernel device driver api:
On newer versions of the 2.4 series of kernel such as 2.4.18 included in redhat 8.0 , all hell brakes loose if the compiler searches /usr/include for include files for kernel modules.
In my make file, I changed
KERNEL_FLAGS=-O -DMODULE -D__KERNEL__ -DKERNEL2_2
to
KERNEL_FLAGS=-O -DMODULE -D__KERNEL__ -DKERNEL2_4 -nostdinc -I /usr/src/linux-2.4/include/ -I /usr/lib/gcc-lib/i386-redhat-linux/3.2/include/
I also added an
#ifdef KERNEL2_4
#define KERNEL2_4
#endif
to the source as a quick fix.
The number of arguments to the lseek, read, write, etc. functions has changed. The old inode parameter was eliminated. If you need it (i didn't use it in my code), I believe you can replace the old references to inode with file-<>inode or something like that.
#if defined(KERNEL2_4) || defined(KERNEL2_2)
static loff_t stepper_lseek(
struct file *file, loff_t offset, int orig)
#else
static int stepper_lseek(struct inode *inode,
struct file *file, off_t offset, int orig)
#endif
Note that this is very ugly. A better way is to define wrapper functions.
The declaration need for the user supplied release file operation has changed - it now returns "int" instead of void. I think it is ok to just return(0) at the end of the function; I am guessing a non-zero return means some kind of error has occurred.
#ifdef KERNEL2_2
int stepper_release(struct inode *inode, struct file *file)
#else
void stepper_release(struct inode *inode, struct file *file)
#endif
At various points, fields have been added to the file_operations structure which contains pointers to your file operation functions. Unfortunately, they didn't put them at the end. In 2.2 kernels, flush was inserted after open. There is a new module_name parameter at the very beginning in newer 2.4 kernels. Apparently, device drivers can be made more portable by only initializing the fields in this structure you actually use and letting the compiler initialize the rest to zero (NULL):
static struct file_operations stepper_fops = { llseek: stepper_lseek, read: stepper_read, write: stepper_write, ioctl: stepper_ioctl, open: stepper_open, release: stepper_release, };Wait Queues
Errors related to parameters on calls to wake_up() and wake_up_interruptible() are due to a change in the data type they take.
#if defined(KERNEL2_4) || defined(KERNEL2_2) // kernel 2.4 wait_queue_head_t read_wait; /* used to block user read if buffer underflow occurs */ #else struct wait_queue *read_wait = NULL ; /* used to block user read if buffer underflow occurs */ #endifThere is a very important incompatibility in kernel 2.2: wait queues must now be initialized using
init_waitquit_head(&my_queue) or by declaring them using DECLARE_WAIT_QUEUE_HEAD() at compile time if they are permanent variables (not on stack). This one is nasty because there are no errors at compile time - you just get a kernel Oops or worse a panic when you run your driver.In one place, I took advantage of the fact that you could tell if another part of the driver was waiting by looking at the value of the wait queue pointer. If you learn more about the new structures, you can probably find out how to do the equivalent but I took a simpler approach: I just defined another variable, waiting_for_bottom_half_to_die and set it just before waiting on the wait queue. This is probably only a temporary solution since it may raise the problem of race conditions.
Timer Tick Handlers
The first entry in the tq_struct has changed from a pointer to another struct to simply including the struct itself. This means you need to include an initialializer for the other struct.
static void timer_tick_handler(void *junk); #ifdef KERNEL2_4 static struct tq_struct tick_queue_entry = { {NULL, NULL}, 0, timer_tick_handler, NULL }; #else static struct tq_struct tick_queue_entry = { NULL, 0, timer_tick_handler, NULL }; #endifcurrent->timeout
In kernel 2.2 and newer, you the method of introducing a delay in a top half process (similar to sleep() or usleep()) has changed. I had used such a delay in cleanup_module() as a crude workaround to prevent the module from unloading before read/write calls had finished which otherwise results in deallocated data being referenced.
#ifdef KERNEL2_2 current->state = TASK_INTERRUPTIBLE; schedule_timeout(100); #else /* Delay 1s for read and write to exit */ current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + 100; schedule(); #endifrequest_region(), release_region(), and check_region()
If you get these errors when loading the module:
then you need to #include./stepper.o: unresolved symbol request_region ./stepper.o: unresolved symbol release_region ./stepper.o: unresolved symbol check_region . At which point, you maay discover that request_region now requires three arguments instead of two. The new third argument is the driver name. uaccess
In kernels > 2.2, you need to include <asm/uaccess.h> On newer versions this file is also not present in /usr/include/ but is found if you search /usr/src/linux-2.4/include/ (which the modified compile flags will fix).
#ifdef KERNEL2_2 #include <asm/uaccess.h> #endifThis header file include the access to/from user space memory buffers (see below).
Access to user space memory buffers
The method of gaining access to user space memory buffers has changed totally. The functions
put_fs_byte(),get_fs_byte(),memcpy_fromfs(), andmemcpy_tofs()no longer exist. You probably want put_user(), get_user(), copy_from_user(), and copy_to_user()x = get_fs_byte(user_p); get_user(x,user_p); put_fs_byte(value, user_p); rc=put_user(value, user_p); (difference in return values) memcpy_fromfs(to_p, from_p, count); copy_from_user(to_p, from_p, count); memcpy_tofs(a,b,c); copy_to_user(a,b,c);Apparently, you no longer need to verify regions before copying.
Compatibility macros
The file
linux/compatmac.h, new in kernel 2.3, contains some compatibility macros. These don't allow old code to compile on new systems but they supposedly make it easier to write a new driver (or port an old one) on a new kernel while still retaining the ability to compile on older kernels. Be warned that these include some macros which could very easily lead to unintended behavior and it relies on you having the header file on older systems which predate the include file.notes on #ifdef KERNEL2_2 and #ifdef KERNEL2_$
The crude
#ifdef KERNEL2_2and#ifdef KERNEL2_4tests I used should actualy look more like:#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)) ... #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)) ... #endifInterrupted Programs
A problem arises if a a read() method is sleeping waiting for more input to become availible and the process which called read() is terminated. It is necessary for the driver to call signal_pending(current) after it wakes up to see if there are any signals. If a signal is pending you can return(-ERESTARTSYS). You can also look at the return value from sleep_interruptible to tell if a signal is pending, which might be more portable to kernels prior to 2.2 (which did not have signal_pending()). This can also apply to write() methods that are sleeping.
Problems with older code
One of the problems with older drivers such as the one I am converting is that they were created in something of an information vacuum. There was little documentation availible. And while there were examples availible in the form of existing drivers, their quality and similarity to the driver being written varies. Now the Linux Device Drivers book is availible both in print and online. Though it is already slightly out of date, it does provide much needed documentation.
Links
This file is maintained by Mark Whitis (whitis@freelabs.com).
Senior Engineer for hire
Software Development - Electronic Design - Embedded Systems - Device Drivers - System/Network Administration and Security - Motor Control, RobotCNC - Linux/Un*x - 25+ years experience
The author of these pages is looking for a new gig.
[RESUME]
Engineers and electronic hobbyists: The new Open Symbol Project is creating open schematic symbols and PCB footprints for a variety of different CAD packages.
Mark Whitis's Website Home Page Linux Book: Linux Programming Unleashed My Resume Genealogical Data Contact Info Security About All email messages received must pass the turing test or they will be considered SPAM. If it could have been written by a machine, it was.
Under no circumstances are you to email me with questions regarding windoze, any other microsoft operating system or application, or any software which runs under any form of windoze.
*