/*
* chardev.c - Create an input/output character device
*/
#include <linux/kernel.h> /*We're doing kernel work*/
#include <linux/module.h> /*Specifically, a module*/
#include <linux/fs.h>
#include <asm/uaccess.h> /*for get_user and put_user*/
#include "chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80
/*
* Is the device open right now? Usedtoprevent
* concurent access into the same device
*/
static int Device_Open=0;
/*
* The message the device will give when asked
*/
static char Message[BUF_LEN];
/*
* 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.
*/
static char *Message_Ptr;
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file){
#ifdefDEBUG
printk("device_open(%p)/n",file);
#endif
/*
* We don't want to talk to two processes at the same time
*/
if(Device_Open)
return -EBUSY;
Device_Open++;
/*
* Initialize the message
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file){
#ifdefDEBUG
printk("device_release(%p,%p)/n",inode,file);
#endif
/*
* We're now ready for our next caller
*/
Device_Open--;
module_put(THIS_MODULE);
return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(struct file *file, /*seeinclude/linux/fs.h */
char__user *buffer, /*buffer to be filled with data*/
size_t length, /*length of the buffer */
loff_t *offset)
{
/*
* Number of bytes actually written to the buffer
*/
int bytes_read=0;
#ifdefDEBUG
printk("device_read(%p,%p,%d)/n",file,buffer,length);
#endif
/*
* If we're at the end of the message, return 0
* (which signifies end of file)
*/
if(*Message_Ptr==0)
return 0;
/*
* Actually put the data into the buffer
*/
while (length && *Message_Ptr){
/*
* Because the buffer is in the user data segment,
* not the kernel data segment, assignmentwouldn't
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment.
*/
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}
#ifdefDEBUG
printk("Read%dbytes,%dleft/n",bytes_read,length);
#endif
/*
* Read functions are supposed to return the number
* of bytes actually insertedintothebuffer
*/
return bytes_read;
}
/*
* This function is called when somebody tries to
* write into our device file.
*/
staticssize_t device_write(struct file *file,
const char__user *buffer, size_t length, loff_t *offset)
{
int i;
#ifdefDEBUG
printk("device_write(%p,%s,%d)",file,buffer,length);
#endif
for(i=0;i<length && i<BUF_LEN;i++)
get_user(Message[i],buffer+i);
Message_Ptr=Message;
/*
* Again, return the number of input characters used
*/
return i;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the outputofthisfunction.
*
*/
int device_ioctl(struct inode *inode, /*see include/linux/fs.h*/
struct file *file, /*ditto*/
unsigned int ioctl_num, /*number and param for ioctl*/
unsigned long ioctl_param)
{
int i;
char *temp;
char ch;
/*
* Switch according to the ioctl called
*/
switch(ioctl_num){
case IOCTL_SET_MSG:
/*
* Receive a pointer to a message(in user space) and set that
* to be the device's message. Get the parameter givento
* ioctl by the process.
*/
temp=(char*)ioctl_param;
/*
* Find the length of the message
*/
get_user(ch,temp);
for(i=0; ch && i<BUF_LEN; i++,temp++)
get_user(ch,temp);
device_write(file,(char *)ioctl_param,i,0);
break;
case IOCTL_GET_MSG:
/*
* Give the current message to the calling process -
* the parameter we got is a pointer, fill it.
*/
i = device_read(file, (char *)ioctl_param, 99, 0);
/*
* Put a zero at the end of the buffer, so it will be
* properly terminated
*/
put_user('/0',(char*)ioctl_param+i);
break;
case IOCTL_GET_NTH_BYTE:
/*
* This ioctl is both input (ioctl_param) and
* output (the return value of this function)
*/
return Message[ioctl_param];
break;
}
return SUCCESS;
}
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structureiskeptin
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operationsFops={
.read=device_read,
.write=device_write,
.ioctl=device_ioctl,
.open= device_open,
.release=device_release, /*a.k.a.close*/
};
int init_module()
{
int ret_val;
/*
* Register the character device (atleast try)
*/
ret_val=register_chrdev(MAJOR_NUM,DEVICE_NAME,&Fops);
/*
* Negative values signify an error
*/
if(ret_val<0){
printk("%sfailedwith%d/n",
"Sorry,registeringthecharacterdevice",ret_val);
return ret_val;
}
printk("%sThe major device number is%d./n", "Registerationisasuccess",MAJOR_NUM);
printk("If you want to talk to the device driver,/n");
printk("you'llhavetocreateadevicefile. /n");
printk("We suggest you use:/n");
printk("mknod%s c%d0/n",DEVICE_FILE_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;
}
void cleanup_module()
{
int ret;
/*
* Unregister the device
*/
ret=unregister_chrdev(MAJOR_NUM,DEVICE_NAME);
/*
* If there's an error, report it
*/
if(ret<0)
printk("Error in module_unregister_chrdev:%d/n",ret);
}
/*
* chardev.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (inchardev.c)and the process calling ioctl(ioctl.c)
*/
#ifndef CHARDEV_H
#define CHARDEV_H
#include <linux/ioctl.h>
/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAJOR_NUM 100
/*
* Set the message of the device driver
*/
#define IOCTL_SET_MSG_IOR(MAJOR_NUM,0,char*)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAJOR_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
/*
* Get the message of the device driver
*/
#define IOCTL_GET_MSG_IOR(MAJOR_NUM,1,char*)
/*
* This IOCTL is used for output, to get the message
* of the device driver. However, we still need the
* buffer to place the message in to be input,
* as it is allocated by the process.
*/
/*
* Get the n'th byte of the message
*/
#define IOCTL_GET_NTH_BYTE_IOWR(MAJOR_NUM,2,int)
/*
* The IOCTL is used for both input and output. It
* receives from the user a number, n, and returns
* Message[n].
*/
/*
* The name of the device file
*/
#define DEVICE_FILE_NAME "char_dev"
#endif
/* ioctl.c */
#include "chardev.h"
#include <fcntl.h> /*open*/
#include <unistd.h> /*exit*/
#include <sys/ioctl.h> /*ioctl*/
ioctl_set_msg(intfile_desc,char*message)
{
int ret_val;
ret_val=ioctl(file_desc,IOCTL_SET_MSG,message);
if(ret_val<0){
printf("ioctl_set_msg failed:%d/n",ret_val);
exit(-1);
}
}
ioctl_get_msg(intfile_desc)
{
int ret_val;
char message[100];
/*
* Warning - this is dangerous because wedon't tell
* the kernel how far it's allowed to write, so it
* might overflow the buffer. Inarealproduction
* program, we would have used two ioctls - onetotell
* the kernel the buffer length and another to give
* it the buffer to fill
*/
ret_val=ioctl(file_desc,IOCTL_GET_MSG,message);
if(ret_val<0){
printf("ioctl_get_msg failed:%d/n",ret_val);
exit(-1);
}
printf("get_msg message:%s/n",message);
}
ioctl_get_nth_byte(intfile_desc)
{
int i;
char c;
printf("get_nth_bytemessage:");
i = 0;
while(c!=0){
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);
if(c<0){
printf("ioctl_get_nth_byte failed at the%d'th byte:/n", i);
exit(-1);
}
putchar(c);
}
putchar('/n');
}
main()
{
int file_desc, ret_val;
char*msg="Message passed by ioctl/n";
file_desc=open(DEVICE_FILE_NAME,0);
if(file_desc<0){
printf("Can't open device file: %s/n",DEVICE_FILE_NAME);
exit(-1);
}
ioctl_get_nth_byte(file_desc);
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc,msg);
close(file_desc);
}