Kernel Module 2 Char Device - FrankBau/meta-marsboard-bsp GitHub Wiki
This example extends Kernel Module 1 - Hello World by exposing a device /dev/cse3
to user mode which can be used to communicate to my_module
.
The type of device used is called a character device, because its form of communication are two streams of (ASCII) characters, one for input, and one for output.
One of the oldest and most common character devices is a serial console like /dev/tty
or /dev/ttyUSB0
the serial link that is used to connect a MarS Board to a host PC.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
static char command[256];
static char response[256];
int have_command = 0;
int my_read( struct file *filep, char *buffer, size_t count, loff_t *offp )
{
int ret;
if( 0==have_command )
return 0; // we already have sent a response and wait for a "write command" operation
if( count > strlen(response) )
count = strlen(response);
ret = copy_to_user( buffer, response, strlen(response) );
if( ret != 0 )
return -EINVAL;
have_command = 0;
return count;
}
int my_write( struct file *filep, const char *buffer, size_t count, loff_t *offp )
{
int ret;
if( count > sizeof(command)-1 )
count = sizeof(command)-1;
ret = copy_from_user( command, buffer, count );
if( ret != 0 )
return -EINVAL;
command[count] = '\0';
// parse command here and execute it
switch( command[0] )
{
case 's':
// TODO: set led
strcpy( response, "OKd\n" );
break;
case 'c':
// TODO: clear led
strcpy( response, "OK\n" );
break;
default:
strcpy( response, "ERROR: unknown command\n" );
}
have_command = 1;
return count;
}
static struct file_operations my_fops =
{
.read = my_read,
.write = my_write,
};
static int my_major_number;
static struct class* my_device_class;
static struct device* my_device;
static int __init my_init(void)
{
printk(KERN_INFO "my module: init\n");
my_major_number = register_chrdev(0, "cse3", &my_fops);
if( my_major_number < 0 )
{
printk( KERN_ERR "register_chrdev failed, error %d\n", my_major_number );
return my_major_number;
}
my_device_class = class_create( THIS_MODULE, "cse3class" );
if( IS_ERR(my_device_class) )
{
printk( KERN_ERR "class_create failed, error %ld\n", PTR_ERR(my_device_class) );
unregister_chrdev( my_major_number, "cse3" );
return PTR_ERR(my_device_class);
}
my_device = device_create( my_device_class, NULL, MKDEV(my_major_number, 0), NULL, "cse3" );
if (IS_ERR(my_device))
{
printk( KERN_ERR "device_create failed, error %ld\n", PTR_ERR(my_device) );
class_destroy( my_device_cdlass );
unregister_chrdev( my_major_number, "cse3" );
return PTR_ERR(my_device);
}
return 0;
}
static void __exit my_exit(void)
{
device_destroy( my_device_class, MKDEV(my_major_number,0) );
class_destroy( my_device_class );
unregister_chrdev( my_major_number, "cse3" );
printk(KERN_INFO "my module: exit\n" );
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("F.B.");
MODULE_DESCRIPTION("my char device driver");
Test the kernel module by building it (as above) and transferring it to the target, say /home/root/my_module.ko
Login as root on the target and enter:
root@marsboard:~# insmod my_module.ko
my module: init
root@marsboard:~# lsmod
Module Size Used by
my_module 1807 0
...
root@marsboard:~# ls -l /dev/cse3
crw------- 1 root root 246, 0 May 16 01:23 /dev/cse3
root@marsboard:~# echo "s" > /dev/cse3
root@marsboard:~# cat /dev/cse3
OK
root@marsboard:~# echo "x" > /dev/cse3
root@marsboard:~# cat /dev/cse3
ERROR: unknown command
root@marsboard:~# rmmod my_module
my module: exit
This simple module has some limitations:
-
open
andrelease
functions are not implemented, so the device can be openend serveral times or by several processes. This might be confusing. Hint: implement both functions such that the device can be opened at most once at a time. -
my_read
returns 0 which means "end of file". This is good forcat
, becausecat
reads as much as it can. One might prefer a different implementation ofmy_read
when the response is read by a user mode app. - There is no protection against race conditions like if
my_read
is called duringmy_write
. So you might want to use a mutex for protection. - Often, kernel modules implement
ioctl
calls to set/get binary data like a C struct
Kernel Module 3 - GPIO shows how actually to toggle a GPIO pin whenever a read is executed.