Character Driver Issues Implementing ‘/dev/physmem’ Goals for ‘physmem’ • • • • Let user-programs ‘read’ physical memory Make this convenient for the programmer Treat physical memory as just one big file Then can use standard C library functions: i.e., open(), lseek(), read(), close() • But don’t give user-programs write-access! The ‘mknod’ command • Device-file: usually created by command # mknod /dev/physmem c 251 0 • ‘mknod’ takes 4 command-line arguments: the pathname of the special file ‘c’ indicates it’s a character device 251 is driver’s ID-number (i.e., major) 0 is the device’s ID-number (i.e., minor) • Need ‘root’ privileges to create /dev file Default file permissions • Without ‘root’ privileges, we can’t try it • But if we could, we’d see file-permissions • Example: # mknod /dev/xxx c 251 0 # ls -l /dev/xxx crw-r—r-- 1 root root 251, 0 25 Feb dev/xxx • We don’t want users to ‘write’ to physmem Modifying file-permissions • Supervisor can change file-permissions • ‘chmod’ is the privileged command to use • Example: # chmod 0444 /dev/xxx # ls -l /dev/xxx cr--r—r-- 1 root root 251, 0 25 Feb dev/xxx • Now the device-file has ‘read-only’ access A ‘smarter’ driver • Our drivers can create their own files: syscall: mknod( pathname, mode, devid); • Our drivers can modify file-permissions: syscall: chmod( pathname, mode); • Our drivers can also delete their own files: syscall: unlink( pathname ); Some manifest constants • Header-file: #include <linux/stat.h> #define S_IFCHR 0020000 #define S_IRUSR 0000400 #define S_IRGRP 0000040 #define S_IROTH 0000004 By ‘or’-ing the latter three values, can get: #define S_IRUGO 0000444 Driver’s ‘read()’ function • • • • Must conform to standard prototype Otherwise: can’t ‘register_chrdev()’ Return-value has type ‘ssize_t’ Function takes four arguments: struct file *filp; // pointer to file-structure char *buf; // pointer to user-buffer size_t count; // size of user-buffer loff_t *pos; // points to file-position Algorithm for ‘read()’ • Step 1: Check for an invalid request: if ( *pos >= filesize ) return –EINVAL; • Step 2: Adjust ‘count’ for remaining bytes: If ( *pos + count > filesize ) count = filesize - *pos; Algorithm (continued) • Step 3: Find location of next data bytes void *from = phys_to_virt( *pos ); • Step 4: Copy data into user-space buffer more = copy_to_user( buf, from, count ); • Step 5: Return error if copying unfinished if ( more ) return –EFAULT; Algorithm (conclusion) • Step 6: Advance file’s current position *pos += count; • Step 7: Return number of bytes copied return count; Driver needs ‘llseek()’ • ‘fileview.cpp’ checks for size of device-file • Standard C library function is ‘lseek()’: filesize = lseek( fd, 0, SEEK_END ); ‘fd’ is the file-descriptor for an opened file 0 is the ‘offset’ to seek to SEEK_END says offset is from end-of-file Default ‘llseek()’ won’t work • No way for kernel to know device’s filesize • So default ‘llseek()’ will give a wrong result • Driver must implement its own ‘llseek()’ • For ‘/dev/physmen’ with 1GB memory: FILESIZE = 0x38000000 • But more generally: FILESIZE = virt_to_phys( high_memory ); Sample ‘llseek()’ in LDD text • ‘llseek()’ must handle three cases: #define SEEK_SET #define SEEK_CUR #define SEEK_END 0 1 2 (See author’s example-code on page 163) Prototype for ‘llseek()’ • Function has three arguments: struct file *filp; loff_t offset; int whence; // pointer to file-structure // file-offset to seek to // where to seek from • Return-value has type ‘loff_t’ (new offset) Algorithm for ‘llseek()’ • Step 1: Use ‘switch’ statement: switch( whence ) { case 0: // for SEEK_SET case 1: // for SEEK_CUR case 2: // for SEEK_END default: // should never happen! } Actions for ‘switch’ cases • case SEEK_SET: newpos = offset; break; • case SEEK_CUR: newpos = file->f_pos + offset; break; • case SEEK_SET: newpos = filesize + offset; break; • default: return -EINVAL; Algotithm (conclusion) • Step 2: Check for illegal ‘newpos’ value: if ( newpos < 0 ) return –EINVAL; • Step 3: Update ‘f_pos’ in file-structure: filp->f_pos = newpos; • Step 4: Return new file-pointer position: return newpos; ‘physmem’ is read-only • If device-file lacks ‘write’ permissions, then kernel won’t call driver’s ‘write()’ function • So omit write-function from device-driver in order to avoid having ‘dead code’ Consider your legacy • Your driver-code may have a future life! • Another programmer could ‘inherit’ it • Or you, yourself, may want to ‘reuse’ it • • • • Keeping ‘dead code’ will cause confusion Omitting commentary will cause frustration Need the author’s name (and contact-info) Need code’s revision-history (when/why)