EmbLogic's Blog

CHARACTER DEVICE DRIVER(ARTICLE)

ARTICLE
ON
CHARACTER DRIVER

I have been working on character device driver since last two to three weeks and here I am
giving you the concept and idea I am using in character driver till now.

Let us start with the basic idea about character driver before moving on what is happening in architecture which help me to create my device driver.
DEVICE DRIVER -> These are loadable kernel modules that can be inserted in kernel at the run time and they provide a well defined interface for hardware to respond to a specific application code that want to communicate with the hardware . user at user level gives some specific code through application and the role of these device driver is to make these command work for hardware.

CHARACTER DRIVER -> Character driver is driver that access the device character by character i.e a stream of bytes as in files.

MODULE -> It is piece of code that is used for communication b/w hardware and software

Device driver concept divided into two basic part.
1. user level -> where your application reside
2. kernel level -> where your modules and drivers reside
now question arise how link is created b/w user level and kernel level.
NODE -> node created the link b/w user level and kernel level
USER SPACE(APPLICATION)
|

KERNEL SPACE(MODULES AND DRIVERS)

|
HARDWARE

There are few structures which were defind in kernel and user space through which we can defined functionality of our device driver
KERNEL DEFINED STRUCTURE :
1. STRUCT FILE
2. STRUCT Inode
3. Cdev
USER DEFINED STRUCTURE:
1. Struct Sculldev
2. Struct Scull_qset
3. Struct file operations

Scull -> Scull is a memory space which is allocated in machine memory to your device. in scull we have two vital structure know as struct Sculldev and struct Scull_qset. The former one is the starting point of scull as well as contains data about all the scullqset present in your scull in a way it contains metadata. its member are as fallow-
1. *scull_qset
2. Scull qset_size
3. data_size
4. quantum_size
5. device_size.
6. struct cdev.
struct Sculldev //user defined structure Sculldev
{
struct Scull_qset *scull_qset; //pointing to 1st quantum set(Scull_qset structure)
int quantum_size; //current quantum size
int qset_size; //current array size
int data_size; //amount of data stored here
int device_size;
int open_count;
struct cdev var_dev; //cdev structure defined in cdev header file
}*sculldev; //used for pointing Sculldev

Scull_qset is a structure which store data or information. it mainly has two member:
1. *next -> pointer to next scull_qset
2. **data -> double pointer to your data, means first it will point to an array of pointer known as qset , contains quantum where actually our data is kept.
struct Scull_qset
{
struct Scull_qset *next;
void **data;
};

Struct file operations -> It generally defines the behavior of our device. It is used here to map the user level system calls to programmer defined system call.
Some defined system call:
1. OPEN
2. CLOSE(RELEASE)
3. WRITE
4. READ
5. LLSEEK
struct file_operations fops=
{
open:scull_open,
read:scull_read,
write:scull_write,
llseek:scull_llseek,
release:scull_release

};

Now comes kernel level structures into picture.
STRUCT INODE -> as we all know that in linux everything is file either it is a text file, some kind of code or any device with each and every file there is an associated inode structure , here we will mainly concentrate and concerned about which is particularly represents our device. “inode” is the only way through which we can or its better to say our application can interact with our device.
main member are:
1. i_cev ->which points to your struct cdev. pointer to file operations.
2.
Struct file -> Members are:
1. *f_pos :- which takes the account of the cursor in your file. pointer to file operations
2. *private data :- which will help us to do mapping from your user level to kernal level as its stores the address of your SCULL so we can access all the data which we have stored in quantum part of scullqset from user level.

Struct Cdev—> It mainly has three member:
1. owner :- which specifies about its owner name
2. pointer to file operation
3. dev which contains major and minor no.

void Sculldev_initialisation(void)
{
int a,lv;
for(lv=0;lv Major no. is a number which is given to each and every device driver we have in our machine
Minor no -> Minor no. is the number which represents your the device associated with that particular device driver.

majorno=MAJOR(dev); //extract major no.from device number
minorno=MINOR(dev); //extract minor no.from device number

This is all about the architecture now lets have look on the operations or working.

STEPS USED IN IMPLIMENTATION OF CHARACTER DRIVER

1. Insmod -> which will load our module, module is a source code which is loaded on demand.
insmod ./modules/amit.ko

2. Registration -> register our device driver and device so that DD will get a major number and device gets its own minor number.
ret=alloc_chrdev_region(&dev,minorno,nod,DRIVER_NAME); //when we are registering one device (for dynamic allocation)

3.Allocation -> Allocate space to Scull in machine memory.
sculldev=kmalloc(sizeof(struct Sculldev)*nod , GFP_KERNEL); //allocate memory to Sculldev

4.Initialize Scull –> cdev_init— create link between struct inode and struct cdev… define owner.. cdev_add make attribute of device available to our kernel.
void Sculldev_initialisation(void)
{
int a,lv;
for(lv=0;lv flash data if present in the memory space which is assigned to scull

7.Open -> define open system call.
int scull_open(struct inode *inodep, struct file *filep)
{

struct Sculldev* pdev;
printk(KERN_ALERT”BEGIN:open SUCCESSFULLY :%s\n”,__func__);

pdev=(struct Sculldev *)container_of(inodep->i_cdev, struct Sculldev, var_dev); // map the memeory allocated to Sculldev to VFS which is going to return pointer(A convenience macro that may be used to obtain a pointer to a structure from a pointer to some other structure contained within it.)

filep->private_data =pdev; //all information stored in pdev pointer conatining address

if((filep->f_flags && O_ACCMODE) == O_WRONLY)
{

printk(KERN_ALERT”FILE OPEN\n”);
scull_trim(pdev);
}

printk(KERN_ALERT”END:open close SUCCESSFULLY: %s\n”,__func__);
return 0;
}

8. Write ->before doing that allocate space to Scullqset then to qset and at last to quantum now we can write data to our quantum, LSEEK if needed.
ssize_t scull_write(struct file *filep,const char __user *buff,size_t count, loff_t *f_pos)
{
struct Sculldev *pdev;
struct Scull_qset *qset;
int noq,nosqs,i,j,ret=0,loop=0; //noq=no.of quantum,nosqs=no. of scull_qset,
int nobpq,noctw,nocsw,flag=0,bpos=0; //nocsw=no. of character successfully write
noq=nosqs=nobpq=noctw=nocsw=0;
pdev=filep->private_data; //fetch the device

printk(KERN_ALERT”BEGIN:write open SUCCESSFULLY: %s\n”,__func__);
if(!pdev)
{
printk(“no private data”);
}

nosqs=count/(quantum_size*qset_size); //finding no. of scull_qset required
if(count % (quantum_size*qset_size))
nosqs++;
pdev->scull_qset=create_scull_qset(nosqs); //creating scull_qset(Sculldev *pdev pointing towards *scull_qset)
if(!pdev->scull_qset)
{
printk(“no scull_qset found\n”);
}
noq=count/quantum_size;
if(count%quantum_size)
noq++;

qset=pdev->scull_qset;
create_qset(qset,nosqs); //creating _qset
create_quantum(qset,noq); //creating _quantum
nobpq=quantum_size;
nocsw=0;
noctw=count;
int offset=filep->f_pos;
printk(KERN_ALERT”offset=%d\n”,(int)filep->f_pos); //data write in quantum

while(nosqs)
{
if(noq>quantum_size)
{
loop=quantum_size;
}
else
{
loop=noq;
}
if(flag==0)
{
if(offset>quantum_size)
{
j=offset/quantum_size;
bpos=offset%quantum_size;
nobpq=quantum_size-bpos;
}
else
{
j=0;
nobpq=quantum_size-offset;
bpos=offset;
}
flag=1;
}
else
{
j=0;
loop=noq;
bpos=0;
}
for(i=j;idata[i]+bpos,buff+nocsw,nobpq); //copy data from user space
printk(KERN_ALERT”quantum=%d->write=%s\n”,i,(char *)qset->data[i]+bpos); //data write in quantum
nocsw=nocsw+(nobpq-ret);
noctw=noctw-(nobpq-ret);
if(noctw>quantum_size)
{
nobpq=quantum_size;
}
else
{
nobpq=noctw;
}
printk(KERN_ALERT”no. of byte successfully write=%d\n”,nocsw);
printk(KERN_ALERT”no. of byte not write=%d\n”,ret);
bpos=0;
}
nosqs–;
qset=qset->next;
noq=noq-quantum_size;
}
printk(KERN_ALERT”no.of qset=%d\n”,loop);

printk(KERN_ALERT”END:write close SUCCESSFULLY : %s\n”,__func__);
return count-ret;
}

9. Read -> read whatever you have written to quantum.
ssize_t scull_read(struct file *filep,char __user *buff,size_t count, loff_t *f_pos)

ret=copy_to_user(buff+nocsr,qset->data[i]+bpos,nobpq); //copy data from user space
10. Release-> de-allocate all the space, unregister your device and DD, remove your module.
int scull_release(struct inode *inodep, struct file *filep)
{
printk(KERN_ALERT”BEGIN:OPEN SUCCESSFULLY release: %s\n”,__func__);
printk(KERN_ALERT”END:close SUCCESSFULLY release: %s\n”,__func__);
return 0;
}

For unregistering :

static void cleanup(void)
{
int lv;
printk(KERN_ALERT “BEGIN: %s\n”,__func__);
printk(KERN_ALERT”\ngood bye\n”);
for(lv=0;lv make
For making node -> mknod node c 249 0

TO remove the driver -> rmmod amit

HERE I AM GIVING YOU THE IMPLIMENTATION PROCEDURE TO CLEARIFY THE ALL STEP:-

Initialization of driver -> Mapping of system calls from application to the driver -> open call -> Mapping of memory on to the device (including trim function) -> Write call -> Read call -> Seek call ->use of multi threaded application.

We have used command “insmod” to call the initialization macro i.e. module_init() which will insert the driver into the list of modules & “rmmod” to call the cleanup macro i.e. module_exit() which will remove the driver from the list.

Driver starts with the registration using alloc_chrdev_region(). This registration (using alloc_chrdev_region()) is done so as to get the major number dynamically for the driver. Correspondingly a minor number is generated for the first device.
We can also use register_chrdev() to register the driver if we want to assign the user defined major number.

After registration we got a 32 bit number stored in “dev” which is of “dev_t” type. First 12 bits will give major number by using macro “MAJOR” & remaining 20 bits will give the minor number by using macro “MINOR”.

After getting the major & minor number, now its time to initialize the SCULL i.e. Simple character utility for loading localities. SCULL is a char driver that acts on the memory area as though it was a device. This SCULL is defined by a structure “Sculldev” contains blocks of memory called scullqsets which are also called as items and each item contains qsets which are nothing but array of pointers pointing to specific locations known as quantums. The scullqset or item can handle data maximum upto the data equal to product of qset_size and quantum size. Further each quantum can store maximum of bytes equal to quantum_size defined by the developer. We can calculate the number of scullqsets and number of quantums required for storing given number of bytes. One thing which is very important that these “items” or we can say that “scullqsets” should be linked like “link list” so as to store the data in quantums of different scullqsets.

After initializing the structure Sculldev, now we have to initialize the structure C_dev which will give the information of our device i.e. Owner of the device, Major & Minor numbers, operations that our device can perform etc. After this its time to map the system calls to the corresponding functions defined in the driver.

As the mapping is done so now from the application end we will call the open system call which will call our defined function in driver. In normal routine open call creates a stream and after handing over of the stream this call terminates. For our driver we have written our own routines. In our open function we have used a macro “container_of” to map the memory allocated for scull from RAM to the device & along with the link of “Structure inode” with “Structure C_dev & SCULL. This mapping will return the starting address of the SCULL which we will store into the “private data” of the “Structure file”. When ever & where ever if we want to access the sculldev we will fetch the private data. After sending the address of Sculldev we will call the trim function.

If we want to write on to the device, we have to check whether the device is having something onto its memory or not. If we found anything in quantums, we first trim the data nothing but flushing out the previously written bytes.

After trimming we will call write from the application. This write system call will call the write function which we have in our driver as we have already mapped all the operations to be performed by the applications in “Structure file_operations”. Writing here means getting the data from the user buffer & writing it on the quantums present in the SCULL i.e. Memory of device. In the starting of write function we will fetch the private data so as to get the address of SCULL. For writing we will use “Copy_from_user()”. Before writing we will calculate the number of items (scullqsets) to be created along with the number of quantums. Now we want to read what we have written earlier in the quantums. For this we will call the read function. This means reading the data from the quantums & passing it to the user buffer which we will receive in our application. For this we will use “Copy_to_user()”.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>