180 likes | 312 Views
Embedded Systems Programming. Writing Device Drivers. Writing a new device driver. When writing a device driver you can either Create a new, bespoke driver from scratch Or Port an existing driver Generally pick option 2 unless The hardware is so new to make porting impossible
E N D
Embedded Systems Programming Writing Device Drivers
Writing a new device driver • When writing a device driver you can either • Create a new, bespoke driver from scratch • Or • Port an existing driver • Generally pick option 2 unless • The hardware is so new to make porting impossible • You are really experienced in device driver writing • The driver has to do something really special or diffferent
Linux Execution Paths • Execution paths
Kernel Modules • Kernel modules are inserted and unloaded dynamically • Kernel code extensibility at run time • insmod / lsmod/ rmmod commands. Look at /proc/modules • Kernel and servers can detect and install them automatically, for example, cardmgr (pc card services manager) • Modules execute in kernel space • Access to kernel resources (memory, I/O ports) and global variables ( look at /proc/ksyms) • Export their own visible variables, register_symtab (); • Can implement new kernel services (new system calls, policies) or low level drivers (new devices, mechanisms) • Use internal kernel basic interface and can interact with other modules (pcmcia memory_cs uses generic card services module) • Need to implement init_module and cleanup_module entry points, and specific subsystem functions (open, read, write, close, ioctl …)
#include <linux/delay.h> #include <linux/module.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/arch/io.h> #include <asm/arch/hardware.h> #include <asm/uaccess.h> #define DRIVER_AUTHOR "craig duffy craig.duffy@uwe.ac.uk"#define DRIVER_DESC "FPGA DIO driver" #define LED_ADDRESS 0xf2400680 #define RGGG 0xf000 static int fpga_dio_init(void) { static int fpga_j; static short unsigned pattern; printk(KERN_ALERT "fpga dio loaded\n"); pattern=RGGG; pattern = pattern >> 4; for ( fpga_j=0; fpga_j != 16 ; fpga_j++) { printk("pattern %x\n",pattern); udelay(400); writew(pattern,LED_ADDRESS); pattern = pattern >> 8; pattern--; pattern = pattern << 8; }*/ return 0; } static void fpga_dio_exit(void) { printk(KERN_ALERT "fpga dio unloaded\n"); } module_init(fpga_dio_init);module_exit(fpga_dio_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_SUPPORTED_DEVICE("fpga_dio");
Module programming • Be careful: a kernel fault is fatalto the current process and sometimes the whole system • Modules should support concurrency (support calls by different processes). Distinct data structuresfor each process (since the same code is executed) to ensure data is not corrupted. • Driver code must be reentrant: keep status in local (stack allocated) variables or dynamic memory allocation: kmalloc / kfree • This allows the process executing to suspend (e.g., wait for pcmcia card interrupt) and other processes to execute the same code. • It is not a good idea to assume your code won’t be interrupted. • Sleep_on(wait_queue) or interruptible_sleep_on(wait_queue) to yield the cpu to another process • /proc/ioportscontains information about registered ports. /proc/iomemcontains info about I/O memory
Register Capability • You can register a new device driver from the kernel: • int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); • A negative return value indicates an error, 0 or positive indicates success. • major: the major number being requested (a number < 128 or 256). • name: the name of the device (which appears in /proc/devices). • fops: a pointer to a global jump table used to invoke driver functions. • Then give to the programs a name by which they can request the driver through a device node in /dev • To create a char device node with major 254 and minor 0, use: • mknod /dev/memory_common c 254 0 • Minor numbers should be in the range of 0 to 255.
Finding a device driver • Obviously you need to be able to categorise the device • Some Linux categories are not clear • You can look at other open systems – for example NetBSD • This is useful as they may have ported the driver themselves • Get as much reliable information about the device • Data sheets, tested, stand alone code
Example of a Parallel port • It is quite often useful to use a parallel port as an interface to some specialised hardware. • There are a number of approaches you could take to controlling such hardware from Linux • A simple, low level kernel device driver • Porting the parallel port package, ppdev, • Writing a full blown device driver through the file system • Writing a driver through the /procfs interface
A simple low level kernel driver • Under Linux it is always possible to write directly to the hardware • This uses some low level calls to read and write data • inb(), outb(), inw(), outw() • The areas of memory are maped by the kernel and ioremap/iounmap shopuld be used • Data has to be copied to and from user space unto kernel space
Problems with low level driver • It is a good idea to use to test the hardware but isn’t a long term solution for driver development • Requires root access to run the kernel • No file system interface • Will be very limited functionality • Can’t use the kernel very effectively • Isn’t portable • Can crash the kernel
Porting an existing device • With the parallel port one can use ppdev • This give a high level interface into the file system through /dev/parport0-n • Don’t need root access • Can have well structured driver • Should be ported (in-time) with new kernel releases • Less likely to cause kernel panics
Problems with ppdev • Unless it is a very standard parallel port application it is likely to be difficult to get the correct level of functionality • Will require understanding a lot of details of parallel port and the driver which won’t be useful for your app
Writing a device driver • This allows you to create a new device in the system • Uses VFS calls to access the drive • Can open, read, write and close • Uses ioctl calls • Driver can be a module • Only loaded when needed • Easier development