TheIntRendz

Home » Tech Talk (Page 11)

Category Archives: Tech Talk

Character Device driver code

Character Device Driver in Linux

User level program for the character device driver      

Appendix A Header File Information (cva.h)

ifndef CVA_H_

#define CVA_H_

#define SUCCESS 0

/* The maximum length of the message for the device */

#define BUF_LEN 1024

/* The name of the device file */

#define DEVICE_NAME “cva”

/* The major device number. We can’t rely on dynamic

 * registration any more, because ioctls need to know

 * it. */

#define MAJOR_NUM 100

#define CVA_WRITE 0

#define CVA_READ 1

ssize_t cva_write(struct file *file,const char *buffer,size_t length,loff_t *offset);

ssize_t cva_read(struct file *file,char *buffer,size_t length,loff_t *offset);

int cva_open(struct inode *inode, struct file *file);

int cva_release(struct inode *, struct file *);

int cva_flush(struct file *);

struct file_operations cva_fops = {

                                        owner:         THIS_MODULE,

                                        write: cva_write,

                                        read:  cva_read,

                                        open: cva_open,

                                        release: cva_release,

                                        flush:   cva_flush,

                                        };

struct cva_buf

{

          int buf_size;

          char buffer [BUF_LEN];

          struct cva_buf *link;

};

struct cva_buf *qhead;

struct cva_buf *qtail;

#endif //CVA_H_

Appendix B The device driver code ( cva.c )

/* For character devices */

#include “cva.h”

/* Module information */

MODULE_AUTHOR(“Anshuman Biswal”);

MODULE_LICENSE(“GPL”);

/* How far did the process reading the message get?

* Useful if the message is larger than the size of the

* buffer we get to fill in device_read. */

char *Message_Ptr;

/* Is the device open right now? Used to prevent

* concurent access into the same device,

* write_busy and read_busy are flags used to prevent multiple writers or readers,  */

int Device_Open = 0;

int write_busy = 0;

int read_busy = 0;

/* The message the device will give when asked */

char Message[BUF_LEN];

/* This function is called whenever a process attempts

* to open the device file */

int cva_open(struct inode *inode,

struct file *file);

{

printk (“cva_open(%p)\n”, file);

/* 1 */

switch ((int)iminor(inode))

{

/* 2 */

case CVA_WRITE:

if (write_busy)

return -EBUSY;

else

write_busy = 1;

return 0;

/* 3 */

case CVA_READ:

if (read_busy)

return -EBUSY;

else

read_busy = 1;

return 0;

default:

return -ENXIO;

}

return SUCCESS;

}

/* This function is called whenever a process which

* has already opened the device file attempts to

* read from it. */

ssize_t cva_read(

struct file *file,

char *buffer, /* The buffer to fill with the data */

size_t length,     /* The length of the buffer */

loff_t *offset) /* offset to the file */

{

int i, len; struct cva_buf *ptr;

printk( “cva_read”);

printk(“cva_read(%p,%p,%d)\n”, file, buffer,(int) length);

/* 1 */

if ((int)iminor(file->f_path.dentry->d_inode)!=CVA_READ)

return -EINVAL;

/* 2 */

if (qhead==0)

return -ENODATA;

/* 3 */

ptr = qhead;

qhead = qhead->link;

/* 4 */

len = (int)length<ptr->buf_size?(int)length:ptr->buf_size;

for (i = 0; i<(int)length && i<ptr->buf_size; ++i)

{

put_user(ptr->buffer[i], buffer+i);

printk(“reading %c”,ptr->buffer[i]);

}

printk( “\n”);

/* 5 */

kfree(ptr);

return i;

//return bytes_read;

}

/* This function is called when somebody tries to

* write into our device file. */

ssize_t cva_write(struct file *file,

const char *buffer,

size_t length,

loff_t *offset)

{

/* Again, return the number of input characters used */

int i, len;

struct cva_buf *ptr;

printk( “cva_write”);

/* 1 */

return -EINVAL;

/* 2 */

if ((ptr = kmalloc(sizeof(struct cva_buf), GFP_KERNEL))==0)

return -ENOMEM;

/* 3 */

len = (int)length<BUF_LEN?(int)length:BUF_LEN;

for (i = 0; i<(int)length && i<BUF_LEN; ++i)

{

get_user(ptr->buffer[i],buffer+i);

printk(“read %c”,ptr->buffer[i]);

}

/* 4 */

ptr->link = 0;

if (qhead==0)

qhead = ptr;

else

qtail->link = ptr;

qtail = ptr;

printk( “\n”);

/* 5 */

ptr->buf_size = i;

return i;

}

/* Initialize the module – Register the character device */

int init_module()

{

int ret_val;

/* Register the character device (atleast try) */

ret_val = register_chrdev(MAJOR_NUM,

DEVICE_NAME,

&cva_fops);

/* Negative values signify an error */

if (ret_val < 0) {

printk (“%s failed with %d\n”,

“Sorry, registering the character device “,

ret_val);

return ret_val;

}

write_busy = 0;

read_busy = 0;

printk (“%s The major device number is %d.\n”,

“Registeration is a success”,

MAJOR_NUM);

printk (“If you want to talk to the device driver,\n”);

printk (“you’ll have to create a device file. \n”);

printk (“We suggest you use:\n”);

printk (“mknod %s c %d 0\n”, DEVICE_NAME,

MAJOR_NUM);

//printk (“The device file name is important, because\n”);

//printk (“the ioctl program assumes that’s the\n”);

//printk (“file you’ll use.\n”);

return 0;

}

int cva_release(struct inode *inode, struct file *file)

{

printk(“cva_release”);

switch ((int)iminor(inode))

{

case CVA_WRITE:

write_busy = 0;

break;

case CVA_READ:

read_busy = 0;

break;

}

return 0;

}

int cva_flush(struct file* filp)

{

printk(“cva_flush”);

switch ((int)iminor(filp->f_path.dentry->d_inode))

{

case CVA_WRITE:

write_busy = 0;

break;

case CVA_READ:

read_busy = 0;

break;

}

return 0;

}

/* Cleanup – unregister the appropriate file from /proc */

void cleanup_module()

{

/* Unregister the device */

printk( “<1>Removing \’%s\’ module\n”, DEVICE_NAME );

unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

}

Appendix C MakeFile

ifneq  ($(KERNELRELEASE),)

obj-m :=cva.o

else

KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

rm -r -f .tmp_versions *.mod.c .*.cmd *.o *.symvers

endif

Appendix D User level program ( watchcva.c )

//——————————————————————-

//        watchcva.cpp

//        This program writes to  a device-file (‘/dev/cva0’)

//  and read the written contents from another device file (‘/dev/cva1,).

//——————————————————————-

#include <stdio.h> // for printf(), perror()

#include <fcntl.h> // for open()

#include <stdlib.h>          // for exit()

#include <unistd.h>         // for read(), and write

#include <string.h>

int main( int argc, char **argv )

{

char buf[1024] = {”};

ssize_t i_read, i_write;

int      fd = open( “/dev/cva0”, O_WRONLY|O_APPEND);

if ( fd < 0 ) { perror( “/dev/cva0” ); exit(1); }

int fd1 = open(“/dev/cva1”,O_RDONLY);

if ( fd1 < 0 ) { perror( “/dev/cva1” ); exit(1); }

printf(“\n Write to /dev/foo file \n”);

printf(“\n please enter string: \n”);

fgets(buf,sizeof(buf),stdin);

i_write=write(fd,buf,strlen(buf));

if((int) i_write < 0) {perror(“error writing to /dev/cva0\n”); return -1;}

printf(“\n reading the /dev/cva1 file\n”);

/* Read from the file, one chunk at a time. Continue until read

“comes up short”, that is, reads less than we asked for.

This indicates that we’ve hit the end of the file. */

i_read = read(fd1,&buf,1024);

if((int) i_read < 0) {perror(“error reading from /dev/cva1\n”); return -1;}

printf(“%s”,buf);

close(fd);

close(fd1);

}