About 4 months I have worked upon something called Device Driver (Character Driver, to be specific).Before start working, First of all I needed to know what really a device driver is? As we all know it is the kernel what is directly going to interact with the Hardware and if our application need to access the hardware we need a driver(Specific one for a class of devices)for that in kernel space. As the name suggest a Device Driver is a LKM(Loadable Kernel Module) which drives(runs) our device. The good thing about a Moduler kernel is a Module can be inserted and removed from the runing kernel(using kernel utilities insmod and rmmod respectively)There are three types of devices i.e Character devices like printers, Block devices like Mass storage devices and Network devices like USb modem etc. Now a Character driver is a driver developed to handel character devices.
Now lets explain the basic arch. of a character driver. Memory of our system is devided in to two parts User space and kernel space. Our applications runs in the user space and our driver runs in kernel space. An application cant see anything in kernel space.Driver is just like a black box for the application developer. So if the application need to access the kernel space, it need a fifo in the VFS(Virtual File System) layer. We create this for the specific device using mknod. In kernel space a driver is identified by its unique 12bit major number and a device is identified by its uniqe 20bit minor number. This 32bit unique number is called dev id(device id). This 32bit no is obtained for a device using a kernel MACRO i.e alloc_chrdev_region(). This registration is to be done right after inserting our module in to the kernel and it should also be unregistered befor removing the module.
There are four most important structures, knowing about those was very much needed. First one is struct cdev. This structure is the representation of a character device in the kernel. This one have a lot of hardware specific information of the device. its most important members are dev(for dev id),owner(owner of the device, the module itself) and *fops (pointer to the struct file_operations). Second one in struct file_operations which is a collection of functiotructuren pointers, each of the function is routine for performing a specific operation over the device like read, write ,open, close etc. Another one is struct inode(for the device file). We all know everything in linux is treated as a file, some are regular files and some are special files. A character device is treated as a special file in our system. Attributes of a file are stored in the structure struct inode and struct inode i am talking about have attribues of that special file for the character device. Fourth one is struct file. struct file is a structure which containes the attributes of a stream. This stream is a stream created between our app and node in vfs, when we give an open call from app.
I have allocated some memory in the kernel space of my RAM in form of SCULL(Simple Character Utility for Loading Localities), which i am treating as a device. This memory is variable as i will be performing write and read, it will be allocated and deallocated accordingly. Starting node of this memory is ScullDev which will be loaded with the lot of info about the device like device_size, data_size, quantum_size, qset_size, write_count and the most important an object of struct cdev. We need to initialize and add this cdev object as our device in the kernel using two MACROS cdev_init() and cdev_add(). cdev_init will initialize our object for the set of operations and cdev_add will add this device in to the device table.
After all this, my device is ready to be worked upon i.e open,write,read,close. For this it was required to map my driver routines to the routines called by system as when a call is given from app my routines should run in the driver. This mapping is to be done in struct file_operations by giving the addresses of my_routines to corresponding function pointers in fops. In open i fetched the address of my ScullDev for the perticular cdev and stored it in private_data field of a system genrated structure struct file pointed by *filep in open for the future use of this address in write and read.
Now i am able to write data to the multiple quantums, multiple scullqset in trunc as well as append mode. I am able to read from the same as well. lseek is also done, and i have tested my driver for multithreaded apps, using various synchronization techniques.