diff options
Diffstat (limited to 'pa3_device_driver/readme.md')
-rw-r--r-- | pa3_device_driver/readme.md | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/pa3_device_driver/readme.md b/pa3_device_driver/readme.md new file mode 100644 index 0000000..c96b2a6 --- /dev/null +++ b/pa3_device_driver/readme.md @@ -0,0 +1,98 @@ +## PA3 - Device Driver +### introduction +this assignment will explore file i/o in the kernel and how to implement a basic device driver +using a loadable kernel module (lkm) + +### devices in linux +device i/o is modeled using files. reading from and writing to a file will invoke the associated +device driver to do the actual reading and writing. all device drivers have a major and minor +number where the major number is unique to every driver, and the minor number differentiates all +the devices belonging to that driver. eg, a typical system will have multiple hard disks, or at +least partitions on a single disk. a single major number number is used to specify the hard disk +device driver, but each partition has a different minor number. +```shell +$ file /dev/sda* +/dev/sda: block special (8/0) +/dev/sda1: block special (8/1) +/dev/sda2: block special (8/2) +/dev/sda3: block special (8/3) +``` + +there are two kinds of device drivers: +* **character driver** reads and writes to/from the device character by character. this is the +most basic type of device driver and is simplest to implement +* **block device driver** reads and writes chunks (blocks) of data with a single read/write +operation. these types of drivers are more complex, but usually more efficient in use of system +resources. network interfaces and disk controller generally prefer to use a block driver. + +### creating device file +to access device driver, a correspondingfile in the `/dev` directory is needed. create it with +`mknod -m <permission> <path> <type> <major> <minor>`, for example +`mknod -m 777 /dev/simple_char_device c 511 0` creates a file where the permission is set so all +users can read, write, and execute the file, `c` specifies the type of driver as a character +driver, 511 is the major number, and 0 is the minor number. + +### driver overview +a userspace program will be used to issue calles `open()`, `read()`, `write()`, `seek()`, or +`close()`. these calls will access the file created in `/dev`, which is associated with driver. +for example. the driver by way of its major and minor numbers indicates to the kernel that you +are trying to perform file operations on a device. the kernel will invoke corresponding file +operations in on driver file. driver then executes it's implementation of these file operations +against the physical device. (our device driver will only read and write to memory regions) + + +### implementation +along with header files necessary for module programming, `linux/fs.h` and `linux/uaccess.h` files +will also need to be included. delcare `init()` and `exit()` functions with `module_init()` and +`module_exit()` to bind these functions. in your init function, register your character driver +using `register_chrdev()`. in exit function, unregister the driver using `unregister_chrdev()`. +`register_chrdev()` takes three parameters; major number, unique name, and pointer to file +operations struct. we'll use dynamically allocated kernel buffer (`device_buffer`) with a fixed +size of 900 bytes to store the data written to device. there are two core functions to manage +memory in the kernel defined in `linux/slab.h`: `void* kmalloc(size, flags)` and +`void kfree(kptr)`. use `GFP_KERNEL` as the flags argument above. + +### operations +to perform operations on device driver, a `file_operations` struct is needed. system defined struct +is found in `/lib/modules/.../build/include/linux/fs.h`. create a similar structure with the same +`struct file_operations` type, but different name. define only the open, close, seek, read, and +write operations. **these are the five functions you need to implement**, and set the function +pointer in `file_operations` struct to point to your implementations. note, there is no close +function here. use `release()` instead. specifications are found below + +**open** -- function takes two parameters; a pointer to an inode struct which represents the +physical file on the disk, and a pointer to a file struct which represents the state of a file, +and returns an integer indicating success or failure. this function does not need to do anything +other than log the number of times the device has been opened + +**release** -- function takes the same two arguments as `open()` an**release** - function takes the same two arguments as `open()` and again returns an integer indicating success or failure. use +`printk()` to output the number of times the device has been closed + +**read** - function expects four parameters: a file pointer, a pointer to a userspace buffer, the +size of that buffer, and a pointer to the current position. use the function `copy_to_user()` to +copy data from the `device_buffer` starting at the current position to the userspace buffer. if +successful, update the current position and return the number of bytes read and print the value + +**write** -- function is similar to read. copy data stored in the userspace buffer into the +`device_buffer` using `copy_from_user()`. the write starts from the current position and if +successful, the position should be updated. print the number of bytes written. finally, return +the number of bytes written to the caller. if the user sets the current position back to the +beginning of the file (using seek), this operation may overwrite existing data + +**seek** -- function takes three parameters: a file pointer, an offset, and the whence value. +whence describes how to interpret the offset (which can be negative). if the value of whence is +0 (`SEEK_SET`), the position is set to the value of the offset. if the value of whence is 1 +(`SEEK_CUR`), the current position is incremented (or decremented if negative offset) by offset +bytes. lastly if the value of whence is 2 (`SEEK_END`), the position is set to the offset bytes +from the end of the file + +if a user attempts to seek before the beginning or beyond the end of the `device_buffer`, the seek +should set the current position to the beginning or end of the file respectively. in other words, +a seek should never fail. if a user attempts to read or write where the number of bytes would pass +the end of the `device_buffer`, driver should only read or write until the end of the buffer and +then return the number of bytes actually read or written. + +### install & test module +verify it is installed by checking kernel log for `printk()` outputs, the `lsmod` command, or by +looking in `/proc/devices`. try to write into and read from device using commands like echo, cat, +head, tail, etc. |