Home » Posts tagged 'character device driver in Linux'
Tag Archives: character device driver in Linux
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);
}