Linux device-driver issuesDevices as ‘special’ filesExample: our ‘led’ deviceExample C++ applicationKernel uses different ID-schemeAssigning ‘major’ numbers‘Dynamic’ module loading‘Official’ device-numbersDynamic assignmentDriver registration‘Chicken-and-Egg’ problem?Obstacles for usOvercoming those obstaclesOne convenient solutionKernel System Calls‘sys_call_table’ is exportedHeader-file: ‘asm/unistd.h’Programming SyntaxOne further ‘gotcha’Raising the ‘user-space’ roof‘init_module’ algorithmHow to remove a device-fileAlgorithm for ‘cleanup’‘pseudo-code’ versus CNow: an in-class exerciseLinux device-driver issuesDevices as ‘special’ files•Unix programs treat most devices as files•Provides a familiar programming interface•Standard C functions: open(), read(), etc•But such devices need to have ‘filenames’•Device files are located in ‘/dev’ directoryExample: our ‘led’ device•We created a ‘char’ device-driver: ‘led.c’•It operated a standard keyboard’s LEDs •It was a ‘write-only’ character device•Applications saw it as a file: ‘/dev/led’•We tested it using: $ echo 7 > /dev/led•That command ‘turned on’ all three LEDsExample C++ application int main( void ){int fd = open( “/dev/led”, O_WRONLY );if ( fd < 0 ) { perror( “open” ); exit(1); }char indicator = 7;write( fd, &indicator, 1 );close( fd );}Kernel uses different ID-scheme•Kernel uses number-pairs (major,minor)•The ‘major’ number identifies the driver•The ‘minor’ number identifies the device•One driver can control multiple devices•Range for ‘major’ numbers is 0..255•Certain of these values are ‘reserved’Assigning ‘major’ numbers•Driver-author can select a major number•Kernel is told during driver ‘registration’•But author must be careful: no duplication!•Registration fails if number already used•View currently used major numbers with$ cat /proc/devices‘Dynamic’ module loading•Linux lets module be loaded ‘on demand’•This could cause ‘contention’ for numbers•Example: your driver uses major=6•But line-printer driver (‘lp.c’) uses major=6 •During printing your module won’t install•And printing fails if your module is installed‘Official’ device-numbers•There is a ‘registry’ of device-numbers•See file ‘devices.txt’ in kernel sources•Look in: /usr/src/linux/Documentation•Maintaining this registry is a ‘big hassle’ (e.g., delays, arguments, too few numbers)•So some alternative solution was neededDynamic assignment•Module author can let kernel choose major•This is why major-number 0 is never used•If programmer requests major-number 0,kernel assigns an available major-number•Kernel informs driver during ‘registration’Driver registration•int register_chrdev( unsigned int major,const char *driver_name, struct file_operations *fops );•Returns: major-number (or error-code) •Using 0 as first argument (‘major’) tellskernel to pick an unused major-number‘Chicken-and-Egg’ problem?•A driver’s device-file(s) must be created•Creator must know device major-number•(Also creator will need ‘root’ privileges!)•Example: root# mknod /dev/led c 15 0•Creates a character device-node havingmajor-number=15 and minor-number=0Obstacles for us•How to we find out what major-number the kernel dynamically assigned to our driver?•How can we create special files in ‘/dev’that allow applications to use our driver?•How to we set the ‘file permissions’ so a normalprogram can open, read/write to our devices?Overcoming those obstacles•Our driver will know its major-number•‘init_module()’ will ‘register’ our driver•Return-value will be the major-number•We could use ‘printk()’ to display its value•Then a user could create the device-file•BUT: will the user be allowed to do it?•‘mknod’ and ‘chmod’ need root privilegesOne convenient solution•Let our module setup its own device-file(s)•Our module will know the major-number and our module has ‘root’ privileges BUT•Can modules execute ‘mknod’? ‘chmod’?Kernel System Calls •Kernel function is named ‘sys_mknod’•In kernel 2.4.20 this ‘symbol’ isn’t exported•Module loader can’t link our module to it•Which kernel symbols ARE exported?•Use: $ cat /proc/ksyms•Ugh! Hundreds of exported kernel symbols•Better: $ grep sys_mknod /proc/ksyms‘sys_call_table’ is exported•Try: $ cat sys_call_table /proc/ksyms•We CAN link our with ‘sys_call_table’•Declare: extern void *sys_call_table[];•I.e., ‘sys_call_table’ is an array of pointers•A pointer to ‘sys_mknod’ is in this array!•But where?Header-file: ‘asm/unistd.h’•Kernel-header defines symbolic constants •Examples: #define __NR_mknod 14 #define __NR_chmod 15 •These are indexes into ‘sys_call_table’•So function-pointers can be ‘looked up’Programming Syntax•Declare static function-pointer variables:static int (*sys_mknod)( const char *, … );static int (*sys_chmod)( const char *, … );•Initialize these function-pointer variables:sys_mknod = sys_call_table[ __NR_mknod];sys_chmod = sys_call_table[ __NR_chmod];One further ‘gotcha’•System-call expect user-space arguments•E.g., filename is a string from user-space •Kernel will check for an “illegal’ argument•A system-call from kernel-space will fail!•PAGE_OFFSET is origin of kernel-space•Normally PAGE_OFFSET is 0xC0000000Raising the ‘user-space’ roof•Top of user-space is a task-variable•Each task has its own local copy •Kept in the ‘struct task_struct’ structure•Assigned during task-creation (e.g., fork() )•Kernel can change this variable’s value!•Syntax: set_fs( get_ds() );•Needs header: #include <asm/uaccess.h>‘init_module’ algorithmchar nm = “led”;struct file_operations fops = { write: write, };int major = register_chrdev(0, nm, &fops );Dev_t dev_id = MKDEV( major, minor );sys_mknod = sys_call_table[ __NR_mknod];set_fs( get_ds() );sys_mknod( “/dev/led”, S_IFCHR, dev_id );How to remove a device-file•Another ‘privileged’ command•Example: root# unlink /dev/led•We can let our ‘cleanup_module()’ do it•But ‘cleanup’ and ‘init’ are different tasks:root# /sbin/insmod led.oroot# /sbin/rmmod led•‘insmod’ will call our init_module()•‘rmmod’ will call
View Full Document