The ‘ioctl’ driver-function On implementing a way to query and modify our UART’s baudrate via the ‘device-file’ abstraction
‘struct file_operations’ For a Linux device-driver’s ‘module_init()’ function, there are two main actions: –Initializing the driver’s global data-structures (this includes verifying the device’s presence) –Registering the driver’s service-functions with the kernel (i.e., the ‘file_operations’ structure) The driver’s ‘module_exit()’ function then has the duty to ‘unregister’ those services
Driver services For character-mode device-drivers (like ‘dram.c’, ‘cmos.c’ and ‘vram.c’), we have implemented some (or all) of the following service-functions (i.e., driver ‘methods’): – read() – llseek() – write() – mmap()
The ‘file’ paradigm The UNIX approach to device-control is to create objects that represent i/o-devices, but which behave like ‘files’ do, insofar as the application programmer is concerned So ‘read()’, ‘lseek()’, ‘write()’ and ‘mmap() use the same function-call syntax – and in most respects the same semantics – for both ordinary ‘files’ and ‘device-files’
An imperfect paradigm But often there are a few ways in which the file-object paradigm doesn’t quite fit with important features of an i/o device In these cases, device-drivers can provide a ‘workaround’ that allows applications to perform device-actions that deviate from customary ‘read/write/seek’ file-like actions This ‘workaround’ mechanism is ‘ioctl()’
The serial UART Our PC’s serial UART device offers us an easy example of some desirable behavior that’s outside the traditional ‘file’ paradigm In order to use our ‘dev/vram’ device-file for communications with computers that we can’t control, we may need to adjust our UART’s communication parameters How can a program change ‘baudrate’?
Our ‘baudrate.c’ module These techniques are demonstrated in this device-driver module’s ‘ioctl()’ function Two IOCTL services are implemented: #define GET_BAUDRATE0 #define SET_BAUDRATE1 Applications can open the device-file and invoke an ‘ioctl’ system-call; for example: int fd = open( “/dev/uart”, O_RDWR ); ioctl( fd, GET_BAUDRATE, &baudrate );
Recall role of a device-driver user application standard “runtime” libraries call ret user spacekernel space Operating System kernel syscall sysret device-driver module call ret hardware device out in operating parameters RAM A device-driver is a software module that controls a hardware device in response to OS kernel requests relayed, often, from an application
UART’s baudrate-control MSB LSB i/o port 0x03F8 i/o port 0x03F9 i/o port 0x03FB DLABDLAB LINE CONTROL REGISTER DIVISOR LATCH (bits 7..0) DIVISOR LATCH (bits 15..8) BREAKBREAK data bits stop bits parity controls
Algorithm to ‘get’ baudrate Input (and save) the LINE_CONTROL Set DLAB-bit in LINE_CONTROL to 1 Input (and save) the DIVISOR_LATCH Restore LINE_CONTROL to former value Compute ‘baudrate’ from divisor_latch: baudrate / divisor_latch Return this current ‘baudrate’ value
Algorithm to ‘set’ baudrate Receive ‘baudrate’ as function-argument Verify that argument is within valid range Compute ‘divisor_latch’ from ‘baudrate’: divisor_latch / baudrate Input (and save) the LINE_CONTROL Set DLAB-bit in LINE_CONTROL to 1 Output ‘divisor_latch’ to UART register Restore LINE_CONTROL to former value
Demo-program: ‘setbaud.cpp’ This application-program lets a user query or modify the UART’s current baudrate in a convenient manner – without requiring any ‘special’ privileges (don’t need ‘iopl3’) To see the current baudrate: $./setbaud To change the current baudrate: $./setbaud 2400
In-class exercise Can you modify our ‘baudrate.c’ driver so it implements an additional ‘ioctl’ service: # define GET_LINECTRL2 Then add an extra ‘ioctl’ system-call in our ‘setbaud.cpp’ application so that it displays the UART’s current data-format (as well as baudrate) using format similar to 8-N-1 HINT: look at driver’s ‘get_info()’ function