Character Driver
Device drivers are building blocks of a OS. In Linux there are three types of device driver i.e.
-
Character Driver
-
Block Driver
-
Pipes Driver
Device driver are used to make hardware do a specific job with some defined set of rules and they hide the working procedure of hardware from the outside world. Device drivers as implemented as loadable kernel module means you can add a drive at any instance of time.
Character drivers are used to access streams of byte at one by one. Some example of char driver are text console, serial port, USB keyboard etc. Before working with driver, driver must be registered with kernel so that driver can be inserted as lkm module. Driver communicate using node in file-system.
For using the char device, there should be one entry point which will start the driver and there should be one exit point which will be used to remove the driver from the list of installed modules. For this there are two macro’s defined in kernel for char driver. They are module_init() and module_exit(). They take the __init and __exit functions as there arguments. After this the lkm is inserted using the insmod command and lkm is removed using the rmmod command.
To access the device using node,system provides the Major and Minor numbers. To get the Major and Minor numbers, the driver for lkm is registered with kernel using alloc_chrdev_region(if no major number is allocated) and register_chrdev_region(if major number for the device driver is already obtained). It takes the dev_t type variable, no of device, module name and starting minor number as argument and fills the dev_t type variable with the major number allocated to the device. dev_t is a 32 bit variable who’s upper 12 bit represents the major number and lower 20 bits represents the minor number. To get major and minor number separately kernel symbol table provides the two macro’s i.e. MAJOR() and MINOR() which takes the dev_t as argument. The major number is number provided to the device driver and the minor number is used to specify the specific device using the the driver. Device can be freed using the unregistered_chrdev_region which take the dev_t type as argument.
Char device memory is accessed using the scull. Scull stands for simply character utility for loading localities. Scull is implemented as a link list. Scull is represents as
struct scullqset
{
void **;
struct scullqset *next
};
struct sculldev
{
struct scullqset *qset;
//data
};
Sculldev contains the attribute of the devices like quantum size, qset size, device size, date size and some flag etc where as scullqset contains the data present in device. It has a void ** which is used to save data.
Scull device is added to the modules using the cdev_init and cdev_add. cdev_init initialize the file operation that are gonna work on the device and cdev_add adds them into the module. cdev_add creates the node in file-system for the device. Every device, who uses driver is registered and added to kernel modules before any other operations. Modules are removed using cdev_del.
Every device provides some basic operations like open, close, read, write etc… these function are mapped to the standard functions in file_operation structure and are already added at the time cdev_add.
When a device is opened, a special micro is used called CONTAINER_OF which creates a mapping for the outside memory device in form of scull. The obtained scull pointer is stored into private_data variable of the file_operation structure. After that the mode of open is checked, if opened in write-only mode, then previous mapping of the device is removed and cleaned.
In write operation, sculldev is loaded from the file stream’s private_data. memory is allocated for the amount of data to be written. The write function uses the micro provided by the kernel to copy data from the user application using copy_from_user function. This function copies the data from user space to kernel space and put it into the device memory using scull. copy_from_user return the numbers of bytes not copied. After successful copy, the device attributes is updated.
Read operation is also similar like write operation with difference is it uses copy_to_user function for reading the data from scull memory to user application.
llseek operation is used to set the offset from where read or write operation is to be performed by updating the f_pos value.