Creating message character device - /dev/msg

In this post, let's create a simple character device - /dev/msg, which will store a user message.

Theory

We use copy_to_user and copy_from_user to copy data to/fro in between user space and kernel space.

unsigned long copy_to_user ( void __user *  to, const void *  from, unsigned long  n); -- Copies data from kernel space to user space.

unsigned long copy_from_user (void *to, const void __user *from,unsigned long n); -- Copies data from user space to kernel space.

Both on success returns 0 else returns number of bytes not copied.

Why copy_to_user and copy_from_user is needed instead of memcpy?


  • It verifies that the memory passed is in a region of memory that the process have access to and the address is not pointing to kernel space by using access_ok macro
  • User pages being addressed might not be currently present in memory, if the kernel directly tries to access the pages which are not in memory, it will result in page fault and kernel will panic on page fault. But if there is page fault during copy_from_user kernel does not panic, but returns -EFAULT.
  • It also handles if it encounters an invalid address during copy.

Code:


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
dev_t device_number;
bool dynamic = true;
struct class *my_class;
static struct cdev my_cdev;
#define MAX_SIZE 1024
char buffer[MAX_SIZE];
static int msgdevice_open(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return 0;
}
static int msgdevice_release(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return 0;
}
ssize_t msgdevice_read(struct file *file, char __user *user_buffer,
size_t count, loff_t *offset)
{
int retval = 0;
size_t bytes;
bytes = strlen(buffer) - (*offset); //how many bytes not yet sent?
bytes = bytes > count ? count: bytes;
if (bytes) {
retval = copy_to_user(user_buffer, buffer, strlen(buffer));
if (!retval) {
*offset = bytes;
}
else
return retval;
}
return bytes;
}
ssize_t msgdevice_write(struct file *file, const char __user *user_buffer,
size_t count, loff_t *offset)
{
int retval;
size_t bytes;
bytes = count;
if (bytes > (MAX_SIZE - 1))
bytes = MAX_SIZE - 1;
memset(buffer, 0, sizeof(buffer));
retval = copy_from_user(buffer, user_buffer, bytes);
if (!retval) {
buffer[bytes] = '\0';
pr_info("%s Data written:%s\n", __func__, buffer);
}
else {
pr_info("%s: copy_from_user failed\t retval:%d\n",
__func__, retval);
return retval;
}
return bytes;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.open = msgdevice_open,
.release = msgdevice_release,
.read = msgdevice_read,
.write = msgdevice_write
};
static int msg_device_init(void)
{
int retval;
pr_info("%s: In init\n", __func__);
if (dynamic) {
retval = alloc_chrdev_region(&device_number, 0, 1, "embedded");
}
else {
device_number = MKDEV(180, 0);
retval = register_chrdev_region(device_number, 1, "embedded");
}
if (!retval) {
pr_info("%s: Major Number:%d\t Minor Number:%d\n",
__func__, MAJOR(device_number), MINOR(device_number));
my_class = class_create(THIS_MODULE, "my_driver_class");
cdev_init(&my_cdev, &fops);
retval = cdev_add(&my_cdev, device_number, 1);
if (retval) {
pr_info("%s: Failed in adding cdev to subsystem "
"retval:%d\n", __func__, retval);
}
else {
device_create(my_class, NULL, device_number, NULL, "msg");
}
}
else
pr_err("%s: Failed in allocating device number "
"Error:%d\n", __func__, retval);
return retval;
}
static void msg_device_exit(void)
{
cdev_del(&my_cdev);
device_destroy(my_class, device_number);
class_destroy(my_class);
unregister_chrdev_region(device_number, 5);
pr_info("%s: In exit\n", __func__);
}
module_init(msg_device_init);
module_exit(msg_device_exit);
view raw msg_device.c hosted with ❤ by GitHub

Sample Test Application:



#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buffer[1024];
int fd;
int length;
fd = open("/dev/msg", O_RDWR);
if (fd < 0) {
perror("fd failed");
exit(2);
}
printf("write : %d\n", write(fd, "hello world", sizeof("hello world")));
length = read(fd, buffer, sizeof(buffer));
buffer[length] = '\0';
printf("Read:%s\n", buffer);
memset(buffer, 0, sizeof(buffer));
length = read(fd, buffer, sizeof(buffer));
buffer[length] = '\0';
printf("Read:%s\n", buffer);
close(fd);
}
view raw testapp.c hosted with ❤ by GitHub

Output:

$ ./testapp 
write : 12
Read:hello world
Read:



Comments

Popular posts from this blog

bb.utils.contains yocto

Difference between RDEPENDS and DEPENDS in Yocto

PR, PN and PV Variable in Yocto