2Devices as ‘special’ files Unix programs treat most devices as filesProvides a familiar programming interfaceStandard C functions: open(), read(), etcBut such devices need to have ‘filenames’Device files are located in ‘/dev’ directory
3Example: our ‘led’ device We created a ‘char’ device-driver: ‘led.c’It operated a standard keyboard’s LEDsIt was a ‘write-only’ character deviceApplications saw it as a file: ‘/dev/led’We tested it using: $ echo 7 > /dev/ledThat command ‘turned on’ all three LEDs
5Kernel uses different ID-scheme Kernel uses number-pairs (major,minor)The ‘major’ number identifies the driverThe ‘minor’ number identifies the deviceOne driver can control multiple devicesRange for ‘major’ numbers isCertain of these values are ‘reserved’
6Assigning ‘major’ numbers Driver-author can select a major numberKernel is told during driver ‘registration’But author must be careful: no duplication!Registration fails if number already usedView currently used major numbers with$ cat /proc/devices
7‘Dynamic’ module loading Linux lets module be loaded ‘on demand’This could cause ‘contention’ for numbersExample: your driver uses major=6But line-printer driver (‘lp.c’) uses major=6During printing your module won’t installAnd printing fails if your module is installed
8‘Official’ device-numbers There is a ‘registry’ of device-numbersSee file ‘devices.txt’ in kernel sourcesLook in: /usr/src/linux/DocumentationMaintaining this registry is a ‘big hassle’(e.g., delays, arguments, too few numbers)So some alternative solution was needed
9Dynamic assignment Module author can let kernel choose major This is why major-number 0 is never usedIf programmer requests major-number 0,kernel assigns an available major-numberKernel informs driver during ‘registration’
10Driver 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
11‘Chicken-and-Egg’ problem? A driver’s device-file(s) must be createdCreator must know device major-number(Also creator will need ‘root’ privileges!)Example: root# mknod /dev/led c 15 0Creates a character device-node havingmajor-number=15 and minor-number=0
12Obstacles for usHow 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?
13Overcoming those obstacles Our driver will know its major-number‘init_module()’ will ‘register’ our driverReturn-value will be the major-numberWe could use ‘printk()’ to display its valueThen a user could create the device-fileBUT: will the user be allowed to do it?‘mknod’ and ‘chmod’ need root privileges
14One convenient solution Let our module setup its own device-file(s)Our module will know the major-numberand our module has ‘root’ privilegesBUTCan modules execute ‘mknod’? ‘chmod’?
15Kernel System Calls Kernel function is named ‘sys_mknod’ In kernel this ‘symbol’ isn’t exportedModule loader can’t link our module to itWhich kernel symbols ARE exported?Use: $ cat /proc/ksymsUgh! Hundreds of exported kernel symbolsBetter: $ grep sys_mknod /proc/ksyms
16‘sys_call_table’ is exported Try: $ cat sys_call_table /proc/ksymsWe CAN link our with ‘sys_call_table’Declare: extern void *sys_call_table;I.e., ‘sys_call_table’ is an array of pointersA pointer to ‘sys_mknod’ is in this array!But where?
17Header-file: ‘asm/unistd.h’ Kernel-header defines symbolic constantsExamples:#define __NR_mknod 14#define __NR_chmod 15These are indexes into ‘sys_call_table’So function-pointers can be ‘looked up’
18Programming 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];
19One further ‘gotcha’ System-call expect user-space arguments E.g., filename is a string from user-spaceKernel will check for an “illegal’ argumentA system-call from kernel-space will fail!PAGE_OFFSET is origin of kernel-spaceNormally PAGE_OFFSET is 0xC
20Raising the ‘user-space’ roof Top of user-space is a task-variableEach task has its own local copyKept in the ‘struct task_struct’ structureAssigned during task-creation (e.g., fork() )Kernel can change this variable’s value!Syntax: set_fs( get_ds() );Needs header: #include <asm/uaccess.h>
22How to remove a device-file Another ‘privileged’ commandExample: root# unlink /dev/ledWe can let our ‘cleanup_module()’ do itBut ‘cleanup’ and ‘init’ are different tasks:root# /sbin/insmod led.oroot# /sbin/rmmod led‘insmod’ will call our init_module()‘rmmod’ will call our cleanup_module()
24‘pseudo-code’ versus C Previous slides showed algorithm-stepsBUT C language has special requirementWithin each C program-block:all of block’s local variables are declared(and, optionally, initialized)BEFORE any executable-statements appearThis differs from C++ (which is less strict)
25Now: an in-class exercise See online version of our ‘stash.c’ driverAccessible on our class webpageIt was written and tested for kernelThat kernel exported system-call functions‘sys_call_table’ lookups weren’t neededCan you modify ‘stash.c’ for ?