1 / 25

Linux device-driver issues

Linux device-driver issues. Devices 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’ directory.

Télécharger la présentation

Linux device-driver issues

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Linux device-driver issues

  2. Devices 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’ directory

  3. Example: 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 LEDs

  4. Example 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 ); }

  5. 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’

  6. 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

  7. ‘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

  8. ‘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 needed

  9. Dynamic 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’

  10. 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’) tells kernel to pick an unused major-number

  11. ‘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 having major-number=15 and minor-number=0

  12. Obstacles 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 normal program can open, read/write to our devices?

  13. 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 privileges

  14. One 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’?

  15. 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

  16. ‘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?

  17. 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’

  18. 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];

  19. 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 0xC0000000

  20. Raising 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>

  21. ‘init_module’ algorithm char 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 );

  22. 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.o root# /sbin/rmmod led • ‘insmod’ will call our init_module() • ‘rmmod’ will call our cleanup_module()

  23. Algorithm for ‘cleanup’ const char modname[] = “led”; unregister_chrdev( major, modname ); sys_unlink = sys_call_table[ __NR_unlink ]; set_fs( get_ds() ); const char devname[] = “/dev/led”; sys_unlink( devname );

  24. ‘pseudo-code’ versus C • Previous slides showed algorithm-steps • BUT C language has special requirement • Within each C program-block: all of block’s local variables are declared (and, optionally, initialized) BEFORE any executable-statements appear • This differs from C++ (which is less strict)

  25. Now: an in-class exercise • See online version of our ‘stash.c’ driver • Accessible on our class webpage • http://nexus.cs.usfca.edu/~cruse/cs635/ • It was written and tested for kernel 2.4.18 • That kernel exported system-call functions • ‘sys_call_table[]’ lookups weren’t needed • Can you modify ‘stash.c’ for 2.4.20?

More Related