Lecture 3 Module Programming and Device Driver (Homework#1 included) Kyu Ho Park Sept. 13, 2016 Lecture 3 Module Programming and Device Driver (Homework#1 included)
Monolithic Kernel and Micro kernel All functions are provided by a single kernel space ->Linux Micro kernel: Kernel functions are minimized and the other functions are implemented in user space Mach, VxWorks , Window NT.
Monolithic Kernel/Microkernel
Why module programming? ‘modules’: relocatable objects that can be loaded or removed dynamically into or from the kernel. Most of the system functions such as file systems, device drivers, communication protocol can be implemented by modules in Linux!!! To add or modify some functions of the Kernel, no need to recompile the whole kernel.
A simple module program Ref: The Linux Kernel Module Programming Guide, Peter Jay Salzman et al.
Module ‘Makefile’ Line1: module name to be created. Line3: uname –r -> current kernel version, kernel directory for this module. Line4: current module programming directory
insmod-dmesg-rmmod #insmod simpleModule.ko #dmesg #rmmod simpleModule.ko #dmesg
Module utility insmod, rmmod lsmod - program to show the status of modules depmod - program to generate modules.dep and map files. modprobe – program to add and remove modules from the kernel
Kernel 2.4 and 2.6 2.4_header_file 2.6_header_file #include MODULE #include <linux/init.h> #include <linux/module.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kernel.h> 2.4_module_functions 2.6_module_functions int init_module(void); module_init(xxx_init); void cleanup_module(void); module_exit(xxx_exit);
Passing parameters charp == char *
Module_param( ) module_param(para_name, para_type, attribute) short, ushort, int, uint, long, ulong, charp, bool, invbool, intarray
printk( ) Typical usage: printk(KERN_INFO “message\n”); loglevel Remark: the execution time of printk( ) is very long!!!
What is the Device Driver?
Device Driver What is the Device Driver? Software Hardware
(keyboard, mouse, HDD, LanCard, ….) User Program System Call Interface Hardware Devices (keyboard, mouse, HDD, LanCard, ….) Virtual File System Protocol Stack Buffer Character Device Driver Network Block User Level Hardware Level Kernel Level
Device files -File Types in Linux: Regular Files, Directory, Character Device Files, Block Device Files, Link, Pipe, Socket -Device Files(Special Files) Character Devices Block Devices
struct file_operations The operations users can do on Linux file system are defined at the <linux/fs.h>
Device Driver Device driver is a program to access devices through functions defined in file_operations.
Major and Minor Number Major number Minor number Each device driver is identified by a unique major number. This number is the index into an array that contains the information about the driver (the device_struct) Minor number This uniquely identifies a particular instance of a device. For example, a system may have multiple IDE hard disks each would have a major number of 3, but a different minor number.
ls –al (-,d,c,l,p,s,b) Major Number Minor Number
ls -al - (regular file)
Procedure to make a device driver vi simpleDevice.c make insmod make a device_node(mknod) application program by read(), write() ,etc. rmmod
Device registration: register_chrdev, unregister_chrdev and register_chrdev_region,alloc_chrdev_region, unregister_chedev_region -The old way to register a char device driver: int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); int unregister_chrdev(unsigned int major, const char *name);
register_chrdev( ), unregister_chrdev_region( )
<linux/fs.h> -int register_chrdev_region(dev_t first, unsigned int count, char *name); /* If the device major number is known */ -int alloc_chrdev_region(dev_t *dev, unsigned int minor, unsigned int count, char *name)/*Kernel allocate the major number dynamically*/ -void unregister_chrdev_region(dev_t first, unsigned int count);
cdev(Linuc 2.6 Provide cdev struct) struct cdev { struct kobjects kobj; /*objects in module is represented by kobjects */ struct module *owner; struct file_operations *ops; dev_t dev; unsigned int count; };
Functions for character devices #include <linux/cdev.h> struct cdev *cdev_alloc(void); void cdev_init(struct cdev *p, struct file_operations *fop); int cdev_add(struct cdev *p, dev_t first, unsigned count); void cdev_del(struct cdev *p);
<linux/cdev.h> To obtain a standalone cdev structure at runtime; struct cdev *my_cdev= cdev_alloc( ); dummy_cdev->ops=my_fops; - To embed the cdev structure within a device specific structure; void cdev_init(struct cdev *cdev, struct file_operations *fops); -Finally to inform the kernel; int cdev_add(struct cdev *dev, dev_t first_dev_num,unsigned int count);
fops -Initialization of file operations struct file_operations fops = { .open = dummy_open, .release = dummy_close, .read = dummy_read, .write = dummy_write, };
dummy_driver.c
#dmesg after running an application program
Device Driver Internel Basically, it is usually generated by C code. In other word, it can be simple as much as ‘hello world’ in C programming. You can freely imagine your device driver structure. Even though it is not linked with real HW, it works. Closely related with kernel. (use of system calls)
Device Drivers Character Device Block Device Network Device Bypass Buffer Cache Serialized Data User receives Row Data of the device Ex. Terminal, video card, sound card… Block Device Through Buffer Cache Random Accessible Ex. HDD, CDROM … Network Device Ex. Ethernet
How to access the Device Driver How can we access to the device driver? Through Device File (special file) Char device and Block device have this file. ‘mknod /dev/dummy c 250 0’ – creation of the device file. Real Usage fd = open("/ dev/dummy ", O_RDWR); write(fd,buf,1); read(fd,buf,1); close(fd);
Implementation of Device Driver Specification of Dummy Device Driver No linkage with Real HW. Using printk, only printout the name of the device driver operation called by a user. Implementations Module init and cleanup 4 Operations in Dummy Device Driver dummy_read : read system call dummy_write : write system call dummy_open : open system call dummy_release : close system call
Implementation of Device Driver How can we implement it? Initialization (in init_module function) Mapping a device file with the device driver Registration of a Char Device : offered by OS Needs Major Number, Name, and File_operation Structure of the device driver. int register_chrdev( unsigned int major, const * name, struct file_operations * fops); struct file_operations dummy_fops = { .open = dummy_open, // open .read = dummy_read, // read .write = dummy_write, // write .release = dummy_release, // release }; Mapping system calls with the internal functions of the device drivers
How can we implement it? dummy_driver.c #include <linux/module.h> #include <linux/string.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/semaphore.h> #include <asm/uaccess.h> #define DUMMY_MAJOR_NUMBER 250 static struct file_operations dummy_fops= { .open=dummy_open, .read=dummy_read, .write=dummy_write, .release=dummy_release, }; char devicename[20]; char value=' '; static struct cdev my_cdev; static int __init dummy_init(void) { dev_t dev=MKDEV(DUMMY_MAJOR_NUMBER,0); printk("Dummy Driver: init module\n"); strcpy(devicename, "Dummy_Driver"); register_chrdev(DUMMY_MAJOR_NUMBER, devicename, &dummy_fops); cdev_init(&my_cdev, &dummy_fops); cdev_add(&my_cdev,dev,128); return 0; } static void __exit dummy_exit(void) printk("Dummy Driver : Clean Up Module\n"); cdev_del(&my_cdev); unregister_chrdev_region(MKDEV(DUMMY_MAJOR_NUMBER,0),128);
Continued from the previous page ssize_t dummy_read(struct file *file, char *buffer, size_t length, loff_t *offset) { printk("Dummy Driver : Here is Read Call[%x]\n", value); if (copy_to_user(buffer, &value, sizeof(char))) return -EFAULT; return 0; } ssize_t dummy_write(struct file *file, const char *buffer, size_t length, loff_t *offset) char vaule; if( copy_from_user(&value, buffer, sizeof(char))) printk("Dummy Driver: Here is Write Call [%x]\n", value); int dummy_open(struct inode *inode, struct file *file) printk("Dummy Driver : Open Call\n"); int dummy_release(struct inode *inode, struct file *file) printk("Dummy Driver : Release Call\n"); module_init(dummy_init); module_exit(dummy_exit); MODULE_AUTHOR("KHPARK"); MODULE_DESCRIPTION("Dummy_Driver"); MODULE_LICENSE("GPL");
How to build? Makefile
Module Instructions mknod /dev/DUMMY_DEVICE c 250 0 insmod dummy_driver.ko dmesg rmmod dummy_driver.ko
test.c #dmesg //after the execution of test program. Test: #include <stdio.h> #include <fcntl.h> int main(void) { char buf[20]; int fd = open("/dev/DUMMY_DEVICE", O_RDWR); if(fd <= 0) return -1; buf[0] = 0x07; write(fd, buf, 1); read(fd, buf, 1); printf("data from the device : %x\n", buf[0]); close(fd); return 0; } #dmesg //after the execution of test program.
Problem1 1. Make your device driver with a Stack and its application program to prove the correctness. Stack structure(assume the size of the stack is 256) In (write) & Creation of Entry for the stack Out (read) & Deletion of Entry sp :stack pointer APP1 APP2 Check the written data sequence and the read data sequence. And explain why?
Problem2 2. make your own ‘clean function’ that makes the stack empty. remove all the entries in the stack sp clean operation APP1 APP3 APP2
Environment & Policies Same as the Project1 Policies Due : Sept. 23, 11:59 PM Submission to : Joo Kyung Ro ( eu8198@kaist.ac.kr )
Appendix Queue Example
Appendix Queue Example #define QUEUE_SIZE 1024 typedef { int head, tail; int item[QUEUE_SIZE]; } CQ_t; static inline void InitCQ(CQ_t *q) { q->head = q->tail = 0; } static inline int IsFull(CQ_t *q) { return ( (q->head+1)%QUEUE_SIZE == q->tail ); static inline int IsEmpty(CQ_t *q) { return (q->head == q->tail); int Queue(CQ_t *q, int value) { if (q==NULL) return -1; if (IsFull(q)) return -2; q->item[q->head] = value; q->head = (q->head + 1)%QUEUE_SIZE; return 0; } int Dequeue(CQ_t *q, int *value) if (q==NULL || value==NULL) return -1; if (IsEmpty(q)) return -2; *value = q->item[q->tail]; q->tail = (q->tail + 1)%QUEUE_SIZE;