Kernel use the Driver to interact with hardware in an efficient manner. Driver is used as a module to support kernel functioning. Hence driver use kernel space. Each driver have an identical Major number range from 0-255 (8 bit number). However actual bits for major number is 12 bits. Each device attached to a driver have its own minor number. This major & minor number constitute to a dev_t type variable which is unique for every device.
Since Driver are modules ,so they can be dynamically inserted & removed from the kernel ,such driver are known as Loadable Kernel module. Character driver is used to transform data from user process . Character driver is able to transform 1 byte(character) thats why it’s called Character Driver.
Each device is reprensented by a struct sculldev which has following members-
struct scullqset sqset(used for making linked list )
struct cdev (used for storing cdev structure)
int quantum (number of bytes to be stored in each quantum)
int qset (number of qset/linked list)
int size (size of the device)
struct scullqset have two member -
struct scullqset *next (for linking next scullqset)
void ** data (for storing data)
For Module programming necessary header files needed to include are <linux/kernel.h> & <linux/module.h> .Module is initialized by ‘module_init’ macro which specifies the function for the initialization .Similarly ‘module_exit’ macro specifies the exit of module. When a module is inserted using insmod <module name> ,function pointed by module_init() is executed & rmmod <module name> executes the function pointed by module_exit(). Every operation performed in initialization function must be reversed in exit function.
For driver registration we may use one of the following functions:
register_chrdev(unsigned int ,const char* ,const struct file_operations *)
register_chrdev_region (dev_t ,unsigned ,const char * )
alloc_chardev_region (dev_t * ,unsigned ,unsigned ,const char *)
In earlier version of linux module were inserted only by register_chrdev .In such case a driver is mapped with a file operations ,also minor no were not set by the developers. However register_chrdev_region & alloc_chrdev_region were further used for driver registration. alloc_chrdev_region not only provide developer to assign starting minor no (2nd argument ) but also ,number of devices can be register with it (3rd argument). First argument stores the address of dev_t variable assigned by the function. Kernel search for the maximum major no which is free & then assign for the major number. Successful registration of driver returns 0 .
A user defined struct sculldev is used to represent the device memory & its metadata . In structure sculldev we have different members like int quantum,int size,int qset,int devsize ,struct cdev * & struct scullqset * . Structure scullqset is used as a linked list for memory allocation & then data storage for the device .So we allocate memory for this structure in kernel space ,using kmalloc . Memory allocated for structure depend on the size of structure & also the number of devices. For kmalloc <linux/slab.h> must be included. As stated earlier , every operation in initialization must be reversed, hence we free the kernel space allocated for sculldev structure in cleanup function.
cdev_init is used to initialize device & map it with the particular file operations routine . All the attributes for structure sculldev are set before cdev_add. cdev-add mapped the dev_t number (provided by alloc_chrdev_region) to the struct cdev type variable ,which is a member of structure sculldev . Each devices are removed using cdev_del function in cleanup function . For these function <linux/cdev.h> must be included.
When a device is initialized we provide some routine operations mapped to struct file_operations f_ops . For eg., when application uses to open device using open call ,then driver execute the file operation routine mapped for the open .application open the device using a node made by mknod node c <major no> <minor no> & they obtain a file descriptor for this particular node.
While driver interacts with api using struct inode * & struct file * . For each individual node there is unique inode * . For each api opening the node gets its individual file *.When Appplication opens a node it goes to an open routine,defined in module. In open routine struct sculldev pointer for particular devices is saved in the filep->private_data for further operations . This is implemented by container_of . In open routine f_flags in struct file & Accessmode are checked,to see in which mode file is opened.
Release routine is mapped to a function in module which is called when the application uses the close() call.
When a application calls write () function, it calls a write routine of module( in struct file_operations). write routine is given as,
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
where first argument is file pointer ,second one is buffer to be write (given by the application in user space),third argument is number of bytes to be write & last argument tells the current position of struct file ,i.e.,filep->f_pos. first of all strcut sculldev pointer is retrieved from filep->private_data , which was saved earlier in the open routine. After this struct scullqset is given memory using kmalloc.(first scullqset which is given the memory is mapped to the struct sculldev). Then the memory is allocated to void **data & void *data[x]. (void **) data & (void *)data[x] take the memory using kmalloc(sizeof(void *) * qset) & kmalloc(sizeof(void *) * qunatum). Hence data[0...7] will be in sequence. Therefoe they can be accessed later. data[x] (quantum) stores number of bytes given in int quantum of struct sculldev. after qset*quantum bytes next scullqset is needed . Hence we use struct scullqset *next for the next scullqset & the adderss of next scullqset is stored in the scullqset *next (member of the struct scullqset *sqset). Simillarly a linked list is formed with the base address of the list is stored in struct scullqset *sqset (member of struct sculldev). For user space buffer to store in kernel memory ,copy_from_user is used which is declared in <linux/uaccess.h> .
Simillarly when read() is called by application ,module runs read routine,
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
struct sculldev associated with the device is retieved by filep->private_data (as in write routine). as base address of struct scullqset is stored in member of strcut sculldev ,data can be retieved in the same way data was weittrn. copy_to_user is used to copy data from kernel space to user space.