A simple character device driver - c

I wrote a simple character device driver & wanted to cross-compile it for craneboard (ARM architecture). My file name is gDev.c. I copied the file to kernel/drivers/char directory in craneboard source. I modified the Kconfig file in that same directory & added the following lines to it.
config TEST_GCHARD
tristate "My Character driver"
default m
I added the following line to the Makefile in the same directory.
obj-$(CONFIG_TEST_GCHARD) += gDev.o
I added the following line in the am3517_crane_defconfig in arch/arm/configs directory.
CONFIG_TEST_GCHARD=m
My problem is, when I set it as m in am3517_crane_defconfig, the file is not getting included for compilation. But, if I change it as y, it is getting compiled. But, I need it to be a module which I must insmod after board boots up. Please guide me whether I'm missing any steps. Thanks.

here goes the driver in megharajchard.c
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h> /*this is the file structure, file open read close */
#include<linux/cdev.h> /* this is for character device, makes cdev avilable*/
#include<linux/semaphore.h> /* this is for the semaphore*/
#include<linux/uaccess.h> /*this is for copy_user vice vers*/
int chardev_init(void);
void chardev_exit(void);
static int device_open(struct inode *, struct file *);
static int device_close(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static loff_t device_lseek(struct file *file, loff_t offset, int orig);
/*new code*/
#define BUFFER_SIZE 1024
static char device_buffer[BUFFER_SIZE];
struct semaphore sem;
struct cdev *mcdev; /*this is the name of my char driver that i will be registering*/
int major_number; /* will store the major number extracted by dev_t*/
int ret; /*used to return values*/
dev_t dev_num; /*will hold the major number that the kernel gives*/
#define DEVICENAME "megharajchard"
/*inode reffers to the actual file on disk*/
static int device_open(struct inode *inode, struct file *filp) {
if(down_interruptible(&sem) != 0) {
printk(KERN_ALERT "megharajchard : the device has been opened by some other device, unable to open lock\n");
return -1;
}
//buff_rptr = buff_wptr = device_buffer;
printk(KERN_INFO "megharajchard : device opened succesfully\n");
return 0;
}
static ssize_t device_read(struct file *fp, char *buff, size_t length, loff_t *ppos) {
int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
int bytes_to_read; /* gives the number of bytes to read*/
int bytes_read;/*number of bytes actually read*/
maxbytes = BUFFER_SIZE - *ppos;
if(maxbytes > length)
bytes_to_read = length;
else
bytes_to_read = maxbytes;
if(bytes_to_read == 0)
printk(KERN_INFO "megharajchard : Reached the end of the device\n");
bytes_read = bytes_to_read - copy_to_user(buff, device_buffer + *ppos, bytes_to_read);
printk(KERN_INFO "megharajchard : device has been read %d\n",bytes_read);
*ppos += bytes_read;
printk(KERN_INFO "megharajchard : device has been read\n");
return bytes_read;
}
static ssize_t device_write(struct file *fp, const char *buff, size_t length, loff_t *ppos) {
int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
int bytes_to_write; /* gives the number of bytes to write*/
int bytes_writen;/*number of bytes actually writen*/
maxbytes = BUFFER_SIZE - *ppos;
if(maxbytes > length)
bytes_to_write = length;
else
bytes_to_write = maxbytes;
bytes_writen = bytes_to_write - copy_from_user(device_buffer + *ppos, buff, bytes_to_write);
printk(KERN_INFO "megharajchard : device has been written %d\n",bytes_writen);
*ppos += bytes_writen;
printk(KERN_INFO "megharajchard : device has been written\n");
return bytes_writen;
}
static loff_t device_lseek(struct file *file, loff_t offset, int orig) {
loff_t new_pos = 0;
printk(KERN_INFO "megharajchard : lseek function in work\n");
switch(orig) {
case 0 : /*seek set*/
new_pos = offset;
break;
case 1 : /*seek cur*/
new_pos = file->f_pos + offset;
break;
case 2 : /*seek end*/
new_pos = BUFFER_SIZE - offset;
break;
}
if(new_pos > BUFFER_SIZE)
new_pos = BUFFER_SIZE;
if(new_pos < 0)
new_pos = 0;
file->f_pos = new_pos;
return new_pos;
}
static int device_close(struct inode *inode, struct file *filp) {
up(&sem);
printk(KERN_INFO "megharajchard : device has been closed\n");
return ret;
}
struct file_operations fops = { /* these are the file operations provided by our driver */
.owner = THIS_MODULE, /*prevents unloading when operations are in use*/
.open = device_open, /*to open the device*/
.write = device_write, /*to write to the device*/
.read = device_read, /*to read the device*/
.release = device_close, /*to close the device*/
.llseek = device_lseek
};
int chardev_init(void)
{
/* we will get the major number dynamically this is recommended please read ldd3*/
ret = alloc_chrdev_region(&dev_num,0,1,DEVICENAME);
if(ret < 0) {
printk(KERN_ALERT " megharajchard : failed to allocate major number\n");
return ret;
}
else
printk(KERN_INFO " megharajchard : mjor number allocated succesful\n");
major_number = MAJOR(dev_num);
printk(KERN_INFO "megharajchard : major number of our device is %d\n",major_number);
printk(KERN_INFO "megharajchard : to use mknod /dev/%s c %d 0\n",DEVICENAME,major_number);
mcdev = cdev_alloc(); /*create, allocate and initialize our cdev structure*/
mcdev->ops = &fops; /*fops stand for our file operations*/
mcdev->owner = THIS_MODULE;
/*we have created and initialized our cdev structure now we need to add it to the kernel*/
ret = cdev_add(mcdev,dev_num,1);
if(ret < 0) {
printk(KERN_ALERT "megharajchard : device adding to the kerknel failed\n");
return ret;
}
else
printk(KERN_INFO "megharajchard : device additin to the kernel succesful\n");
sema_init(&sem,1); /* initial value to one*/
return 0;
}
void chardev_exit(void)
{
cdev_del(mcdev); /*removing the structure that we added previously*/
printk(KERN_INFO " megharajchard : removed the mcdev from kernel\n");
unregister_chrdev_region(dev_num,1);
printk(KERN_INFO "megharajchard : unregistered the device numbers\n");
printk(KERN_ALERT " megharajchard : character driver is exiting\n");
}
MODULE_AUTHOR("A G MEGHARAJ(agmegharaj#gmail.com)");
MODULE_DESCRIPTION("A BASIC CHAR DRIVER");
//MODULE_LICENCE("GPL");
module_init(chardev_init);
module_exit(chardev_exit);
Make file for the same.
obj-m := megharajchard.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
load script
#!/bin/sh
sudo insmod megharajchard.ko
sudo mknod /dev/megharajchard c 251 0
sudo chmod 777 /dev/megharajchard
unload script
#!/bin/sh
sudo rmmod megharajchard
sudo rm /dev/megharajchard
A C application to test the functionalities of the driver.
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<malloc.h>
#define DEVICE "/dev/megharajchard"
//#define DEVICE "megharaj.txt"
int debug = 1, fd = 0;
int write_device() {
int write_length = 0;
ssize_t ret;
char *data = (char *)malloc(1024 * sizeof(char));
printf("please enter the data to write into device\n");
scanf(" %[^\n]",data); /* a space added after"so that it reads white space, %[^\n] is addeed so that it takes input until new line*/
write_length = strlen(data);
if(debug)printf("the length of dat written = %d\n",write_length);
ret = write(fd, data, write_length);
if(ret == -1)
printf("writting failed\n");
else
printf("writting success\n");
if(debug)fflush(stdout);/*not to miss any log*/
free(data);
return 0;
}
int read_device() {
int read_length = 0;
ssize_t ret;
char *data = (char *)malloc(1024 * sizeof(char));
printf("enter the length of the buffer to read\n");
scanf("%d",&read_length);
if(debug)printf("the read length selected is %d\n",read_length);
memset(data,0,sizeof(data));
data[0] = '0\';
ret = read(fd,data,read_length);
printf("DEVICE_READ : %s\n",data);
if(ret == -1)
printf("reading failed\n");
else
printf("reading success\n");
if(debug)fflush(stdout);/*not to miss any log*/
free(data);
return 0;
}
int lseek_device() {
int lseek_offset = 0,seek_value = 0;
int counter = 0; /* to check if function called multiple times or loop*/
counter++;
printf("counter value = %d\n",counter);
printf("enter the seek offset\n");
scanf("%d",&lseek_offset);
if(debug) printf("seek_offset selected is %d\n",lseek_offset);
printf("1 for SEEK_SET, 2 for SEEK_CUR and 3 for SEEK_END\n");
scanf("%d", &seek_value);
printf("seek value = %d\n", seek_value);
switch(seek_value) {
case 1: lseek(fd,lseek_offset,SEEK_SET);
return 0;
break;
case 2: lseek(fd,lseek_offset,SEEK_CUR);
return 0;
break;
case 3: lseek(fd,lseek_offset,SEEK_END);
return 0;
break;
default : printf("unknown option selected, please enter right one\n");
break;
}
/*if(seek_value == 1) {
printf("seek value = %d\n", seek_value);
lseek(fd,lseek_offset,SEEK_SET);
return 0;
}
if(seek_value == 2) {
lseek(fd,lseek_offset,SEEK_CUR);
return 0;
}
if(seek_value == 3) {
lseek(fd,lseek_offset,SEEK_END);
return 0;
}*/
if(debug)fflush(stdout);/*not to miss any log*/
return 0;
}
int lseek_write() {
lseek_device();
write_device();
return 0;
}
int lseek_read() {
lseek_device();
read_device();
return 0;
}
int main()
{
int value = 0;
if(access(DEVICE, F_OK) == -1) {
printf("module %s not loaded\n",DEVICE);
return 0;
}
else
printf("module %s loaded, will be used\n",DEVICE);
while(1) {
printf("\t\tplease enter 1 to write\n \
2 to read\n \
3 to lseek and write\
4 to lseek and read\n");
scanf("%d",&value);
switch(value) {
case 1 :printf("write option selected\n");
fd = open(DEVICE, O_RDWR);
write_device();
close(fd); /*closing the device*/
break;
case 2 :printf("read option selected\n");
/* dont know why but i am suppoesed to open it for writing and close it, i cant keep open and read.
its not working, need to sort out why is that so */
fd = open(DEVICE, O_RDWR);
read_device();
close(fd); /*closing the device*/
break;
case 3 :printf("lseek option selected\n");
fd = open(DEVICE, O_RDWR);
lseek_write();
close(fd); /*closing the device*/
break;
case 4 :printf("lseek option selected\n");
fd = open(DEVICE, O_RDWR);
lseek_read();
close(fd); /*closing the device*/
break;
default : printf("unknown option selected, please enter right one\n");
break;
}
}
return 0;
}

This is because you have to build the modules in a separate process:
make modules
Also, you can install them with:
make modules_install
If this is not for your own system, but for another one, like an embedded one, you should "install" them in a specific directory which you will then copy on the target, using INSTALL_MOD_PATH:
make INSTALL_MOD_PATH=/tmp/modules_for_target modules_install

Related

One ioctl not being executed in kernel module

I wrote a kernel module demonstrating on how ioctl works.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
int base_minor = 0;
char *device_name = "msg";
int count = 1;
dev_t devicenumber;
static struct class *class = NULL;
static struct device *device = NULL;
static struct cdev mycdev;
#define MAX_SIZE 1024
char kernel_buffer[MAX_SIZE];
int buffer_index;
MODULE_LICENSE("GPL");
static int device_open(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
file->f_pos = 0;
buffer_index = 0;
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return 0;
}
static ssize_t device_read(struct file *file, char __user *user_buffer,
size_t read_count, loff_t *offset)
{
int bytes_read;
int available_space;
int bytes_to_read;
pr_info("%s read offset:%lld\n", __func__, *offset);
available_space = MAX_SIZE - *(offset);
if (read_count < available_space)
bytes_to_read = read_count;
else
bytes_to_read = available_space;
pr_info("bytes_to_read:%d\n", bytes_to_read);
if (bytes_to_read == 0) {
pr_err("%s: No available space in the buffer for reading\n",
__func__);
return -ENOSPC;
}
if (buffer_index > *offset)
bytes_to_read = buffer_index - *offset;
else
return 0;
bytes_read = bytes_to_read - copy_to_user(user_buffer, kernel_buffer+*offset, bytes_to_read);
pr_info("%s: Copy to user returned:%d\n", __func__, bytes_to_read);
//update file offset
*offset += bytes_read;
return bytes_read;
}
static ssize_t device_write(struct file *file, const char __user *user_buffer,
size_t write_count, loff_t *offset)
{
int bytes_written;
int available_space;
int bytes_to_write;
pr_info("%s write offset:%lld\n", __func__, *offset);
available_space = MAX_SIZE - *(offset);
if (write_count < available_space)
bytes_to_write = write_count;
else
bytes_to_write = available_space;
if (bytes_to_write == 0) {
pr_err("%s: No available space in the buffer for writing\n",
__func__);
return -ENOSPC;
}
bytes_written = bytes_to_write - copy_from_user(kernel_buffer+*offset, user_buffer, bytes_to_write);
pr_info("%s: Bytes written:%d\n", __func__, bytes_written);
pr_info("%s: kernel_buffer:%s\n", __func__, kernel_buffer);
//update file offset
*offset += bytes_written;
buffer_index += bytes_written;
return bytes_written;
}
static loff_t device_lseek(struct file *file, loff_t offset, int orig)
{
loff_t new_pos = 0;
switch(orig) {
case 0 : /*seek set*/
new_pos = offset;
break;
case 1 : /*seek cur*/
new_pos = file->f_pos + offset;
break;
case 2 : /*seek end*/
new_pos = MAX_SIZE - offset;
break;
}
if(new_pos > MAX_SIZE)
new_pos = MAX_SIZE;
if(new_pos < 0)
new_pos = 0;
file->f_pos = new_pos;
return new_pos;
}
long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned char ch;
pr_info("%s: Cmd:%u\t Arg:%lu\n", __func__, cmd, arg);
switch(cmd)
{
//Get Length of buffer
case 0x01:
pr_info("Get Buffer Length\n");
put_user(MAX_SIZE, (unsigned int *)arg);
break;
//clear buffer
case 0x02:
pr_info("Clear buffer\n");
memset(kernel_buffer, 0, sizeof(kernel_buffer));
break;
//fill character
case 0x03:
get_user(ch, (unsigned char *)arg);
pr_info("Fill Character:%c\n", ch);
memset(kernel_buffer, ch, sizeof(kernel_buffer));
buffer_index = sizeof(kernel_buffer);
break;
default:
pr_info("Unknown Command:%u\n", cmd);
return -EINVAL;
}
return 0;
}
struct file_operations device_fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release,
.llseek = device_lseek,
.unlocked_ioctl = device_ioctl
};
static int test_hello_init(void)
{
class = class_create(THIS_MODULE, "myclass");
if (!alloc_chrdev_region(&devicenumber, base_minor, count, device_name)) {
printk("Device number registered\n");
printk("Major number received:%d\n", MAJOR(devicenumber));
device = device_create(class, NULL, devicenumber, NULL, device_name);
cdev_init(&mycdev, &device_fops);
mycdev.owner = THIS_MODULE;
cdev_add(&mycdev, devicenumber, count);
}
else
printk("Device number registration Failed\n");
return 0;
}
static void test_hello_exit(void)
{
device_destroy(class, devicenumber);
class_destroy(class);
cdev_del(&mycdev);
unregister_chrdev_region(devicenumber, count);
}
module_init(test_hello_init);
module_exit(test_hello_exit);
Then i wrote a user space code
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
int main(int argc, char *argv[])
{
char buffer[1024];
int fd;
unsigned int length;
unsigned char ch = 'A';
int i = 0;
fd = open("/dev/msg", O_RDWR);
if (fd < 0) {
perror("fd failed");
exit(2);
}
//Get Length - 0x01
ioctl(fd, 0x01, &length);
printf("Length:%u\n", length);
ioctl(fd, 0x02);
//Set Character - 0x03
ioctl(fd, 0x03, &ch);
perror("ioctl");
lseek(fd, 0, SEEK_SET);
perror("lseek");
length = read(fd, buffer, 1024);
perror("Read");
printf("length:%d\n", length);
buffer[1023] = '\0';
printf("Buffer:%s\n", buffer);
close(fd);
}
ioctl commands 1, 3 work but not 2. Can you please provide what's the mistake in the code
You should review the requirements for ioctl on the man page:
DESCRIPTION
The ioctl() system call manipulates the underlying device parameters of
special files. In particular, many operating characteristics of char‐
acter special files (e.g., terminals) may be controlled with ioctl()
requests. The argument fd must be an open file descriptor.
The second argument is a device-dependent request code. The third
argument is an untyped pointer to memory. It's traditionally char
*argp (from the days before void * was valid C), and will be so named
for this discussion.
An ioctl() request has encoded in it whether the argument is an in
parameter or out parameter, and the size of the argument argp in bytes.
Macros and defines used in specifying an ioctl() request are located in
the file <sys/ioctl.h>.

Linux kernel module : Is it possible to use an open function inside another open function for my module?

Maybe this question makes no sense, but I was wondering if there was a "recommended practice" on how to open a file descriptor for a device inside an open function of the created module.
In fact, I developped a simple Linux kernel module with its basic functions :
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/input.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gaston");
MODULE_DESCRIPTION("A simple Linux char driver");
MODULE_VERSION("0.1");
ssize_t exer_open(struct inode *pinode, struct file *pfile) {
printk(KERN_INFO "Device has been opened\n");
return 0;
}
ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
return 0;
}
ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {
return 0;
}
ssize_t exer_close(struct inode *pinode, struct file *pfile) {
printk(KERN_INFO "Device successfully closed\n");
return 0;
}
struct file_operations exer_file_operations = {
.owner = THIS_MODULE,
.open = exer_open,
.read = exer_read,
.write = exer_write,
.release = exer_close,
};
int exer_simple_module_init(void) {
printk(KERN_INFO "Initializing the LKM\n");
register_chrdev(240, "Simple Char Drv", &exer_file_operations);
return 0;
}
void exer_simple_module_exit(void) {
unregister_chrdev(240, "Simple Char Drv");
}
module_init(exer_simple_module_init);
module_exit(exer_simple_module_exit);
I compile it and no errors occured.
Now I want to open the file descriptor of my device ( BUTTON ) in order to manipulate it later from user space program, so I made some modifications by adding the BUTTON device path and another open function like this :
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/input.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gaston");
MODULE_DESCRIPTION("A simple Linux char driver");
MODULE_VERSION("0.1");
#define BTN_FILE_PATH "/dev/input/event0"
int file;
char *str = BTN_FILE_PATH;
ssize_t exer_open(struct inode *pinode, struct file *pfile) {
printk(KERN_INFO "Device has been opened\n");
if((file = open(str, O_RDONLY)) < 0) {
printk("simplekey: File can not open");
return(-1);
}
return 0;
}
ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
return 0;
}
ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {
return 0;
}
ssize_t exer_close(struct inode *pinode, struct file *pfile) {
printk(KERN_INFO "Device successfully closed\n");
return 0;
}
struct file_operations exer_file_operations = {
.owner = THIS_MODULE,
.open = exer_open,
.read = exer_read,
.write = exer_write,
.release = exer_close,
};
int exer_simple_module_init(void) {
printk(KERN_INFO "Initializing the LKM\n");
register_chrdev(240, "Simple Char Drv", &exer_file_operations);
return 0;
}
void exer_simple_module_exit(void) {
unregister_chrdev(240, "Simple Char Drv");
}
module_init(exer_simple_module_init);
module_exit(exer_simple_module_exit);
But the problem, when I try to compile the module now errors are printed :
/home/gaston/ledshared/exer_simple_char_drv.c: In function
‘exer_open’: /home/gaston/ledshared/exer_simple_char_drv.c:32:13:
error: implicit declaration of function ‘open’
[-Werror=implicit-function-declaration]
if((file = open(str,O_RDONLY)) < 0) {
How can I fix the problem please ?
open() is a user-space function. The equivalent kernel-space function is filp_open(), but it returns a struct file * instead of a int file descriptor. The returned struct file * could be an error code instead of a valid pointer. Use the IS_ERR(ptr) macro to check for that, and the PTR_ERR(ptr) macro to extract the error code (which will be a negated errno value).
Use of the filp_open function is discouraged, but here are some modifications to your code to use this function:
int exer_open(struct inode *pinode, struct file *pfile) {
struct file *f;
f = filp_open(str, O_RDONLY);
if (IS_ERR(f)) {
printk("simplekey: File can not open");
return(PTR_ERR(f));
}
pfile->private_data = f;
printk(KERN_INFO "Device has been opened\n");
return 0;
}
The close function should look something like this:
int exer_close(struct inode *pinode, struct file *pfile) {
struct file *f = pfile->private_data;
int rc;
rc = filp_close(f, NULL);
if (rc == 0) {
printk(KERN_INFO "Device successfully closed\n");
}
return rc;
}
There is no legitimate way for a module to read from a struct file * directly into a user-space buffer or write from a user-space buffer to a struct file *, so an intermediate buffer in kernel memory is needed, so that kernel_read() or kernel_write() can be used to read or write the file:
ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
struct file *f = pfile->private_data;
enum { MAX_BUF_SIZE = 4096 };
size_t buf_size = 0;
char *buf = NULL;
ssize_t total = 0;
ssize_t rc = 0;
/* Allocate temporary buffer. */
if (length) {
buf_size = min_t(size_t, MAX_BUF_SIZE, length);
buf = kmalloc(buf_size, GFP_KERNEL);
if (buf == NULL) {
return -ENOMEM;
}
}
/* Read file to buffer in chunks. */
do {
size_t amount = min_t(size_t, length, buf_size);
rc = kernel_read(f, buf, amount, offset);
if (rc > 0) {
/* Have read some data from file. */
if (copy_to_user(buffer, buf, rc) != 0) {
/* Bad user memory! */
rc = -EFAULT;
} else {
/* Update totals. */
total += rc;
buffer += rc;
*offset += rc;
length -= rc;
if (rc < amount) {
/* Didn't read the full amount, so terminate early. */
rc = 0;
}
}
}
} while (rc > 0 && length > 0);
/* Free temporary buffer. */
kfree(buf);
if (total > 0) {
return total;
}
return rc;
}
ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {
struct file *f = pfile->private_data;
enum { MAX_BUF_SIZE = 4096 };
size_t buf_size = 0;
char *buf = NULL;
ssize_t total = 0;
ssize_t rc = 0;
/* Allocate temporary buffer. */
if (length) {
buf_size = min_t(size_t, MAX_BUF_SIZE, length);
buf = kmalloc(buf_size, GFP_KERNEL);
if (buf == NULL) {
return -ENOMEM;
}
}
/* Write file from buffer in chunks. */
do {
size_t amount = min_t(size_t, length, buf_size);
if (copy_from_user(buf, buffer, amount) != 0) {
/* Bad user memory! */
rc = -EFAULT;
} else {
rc = kernel_write(f, buf, amount, offset);
if (rc > 0) {
/* Have written some data to file. */
/* Update totals. */
total += rc;
buffer += rc;
*offset += rc;
length -= rc;
if (rc < amount) {
/* Didn't write the full amount, so terminate early. */
rc = 0;
}
}
}
} while (rc > 0 && length > 0);
/* Free temporary buffer. */
kfree(buf);
if (total > 0) {
return total;
}
return rc;
}

Linux kernel read custom function

Trying to make a read function, that not only adds the numbers together. But also counts how many numbers were added.
Needs to be used in such script:
for i in 1 2 4 8 16 32 64; do
echo $i > /dev/summer0
sleep 60
done
But also just using a test case.
Here is the kernel module code:
/* the memory of the device
*/
int total;
/* Global variables declared as staic, so are global within the file.*/
static char *msg_Ptr;
static char msg[BUF_LEN];
static int n;
/* called after the device is opened
*/
int device_open(struct inode *inode, struct file *file)
{
printk("\nsummer device is open\n");
total = 0;
printk(msg);
msg_Ptr = msg;
return 0;
}
/* Read function */
static ssize_t device_read(struct file *filep, char *buffer, size_t length, loff_t *offset)
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we're at the end of the message, return 0 signifying end of file */
if (*msg_Ptr == 0) return 0;
/* Actually put the data into the buffer */
while (length && *msg_Ptr)
{
/* The buffer is in the user data segment, not the kernel segment;
* assignment won't work. We have to use put_use which copies data from
* the kernel data segment to the user data segment. */
__put_user(*(msg_Ptr++), buffer++);
length++;
n++;
}
/* Most read functions return the number of bytes put into the buffer */
return n;
}
/* handling of ioctl events
*/
long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
int number;
switch(ioctl_num)
{
case SUMMER_SET:
__get_user(number, (int*)ioctl_param);
total += number;
break;
case SUMMER_GET:
__put_user(total, (int*)ioctl_param);
break;
}
return 0;
}
/* table of event handlers of the device
*/
struct file_operations fops =
{
read : device_read,
//write : device_write,
open : device_open,
release: device_close,
unlocked_ioctl : device_ioctl
};
As for the testcase
int n;
int main(void)
{
int sum;
char buffer[BUF_LEN];
/* open character device */
int fd = open("/dev/summer0", O_RDWR); // open device
if(fd < 0)
{
printf("no device found\n");
return 0;
}
int nc = read(fd,buffer,4,n);
for(;;)
{
int num;
printf("Number (exit with 0) = ");
while(scanf("%d", &num) != 1)
;
printf("-- %d --\n", num);
if(num <= 0) break;
/* use ioctl to pass a value to the character device */
ioctl(fd, SUMMER_SET, &num);
n++;
}
/* use ioctl to get a value from the character device */
ioctl(fd, SUMMER_GET, &sum);
printf("#char = %d\n", nc);
printf("Result = %d\n", sum);
printf("Buffer: %s\n" , buffer);
close(fd); // close device
return 0;
}
The program is keeping track of the amount of numbers entered during a single test, but it is supposed to keep track of the numbers until I install the kernel module.

Linux kernel character device driver returns empty string to userspace

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#define DEVICE_NAME "kbdozgur"
#define CLASS_NAME "kbdozgur"
MODULE_AUTHOR("Mehmet Ozgur Bayhan");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interrupt buffering skeleton");
MODULE_VERSION("0.2");
#define BUFFER_SIZE 20
static unsigned char bfr[BUFFER_SIZE];
static int bufferCounter = 0;
static int majorNumber;
static char message[BUFFER_SIZE] = { 0 };
static short size_of_message;
static int numberOpens = 0;
static struct class* kbdozgurcharClass = NULL;
static struct device* kbdozgurcharDevice = NULL;
static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, };
irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
//Read keyboard status
scancode = inb(0x60);
if (scancode == 0x01) {
printk(KERN_INFO "MOB: Inputs are > %s\n", bfr);
bufferCounter = 0;
memset(&bfr[0], 0, sizeof(bfr));
}
else if (scancode == 0x1E) {
bfr[bufferCounter] = 'a';
bufferCounter++;
}
else if (scancode == 0x1F) {
bfr[bufferCounter] = 's';
bufferCounter++;
}
else if (scancode == 0x20) {
bfr[bufferCounter] = 'd';
bufferCounter++;
}
else if (scancode == 0x21) {
bfr[bufferCounter] = 'f';
bufferCounter++;
}
else if (scancode == 0x22) {
bfr[bufferCounter] = 'g';
bufferCounter++;
}
else if (scancode == 0x23) {
bfr[bufferCounter] = 'h';
bufferCounter++;
}
else if (scancode == 0x24) {
bfr[bufferCounter] = 'j';
bufferCounter++;
}
if (bufferCounter >= BUFFER_SIZE) {
bufferCounter = 0;
memset(&bfr[0], 0, sizeof(bfr));
}
return (irq_handler_t) IRQ_HANDLED;
}
static int init_mod(void) {
int result;
/*
*****************************
* Create Character device
*****************************
*/
// Try to dynamically allocate a major number for the device
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber < 0) {
printk(KERN_ALERT "MOB: kbdozgurcharClass failed to register a major number\n");
return majorNumber;
}
printk(KERN_INFO "MOB: registered correctly with major number %d\n", majorNumber);
// Register the device class
kbdozgurcharClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(kbdozgurcharClass)) { // Check for error and clean up if there is
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "MOB: Failed to register device class\n");
return PTR_ERR(kbdozgurcharClass); // Correct way to return an error on a pointer
}
printk(KERN_INFO "MOB: device class registered correctly\n");
// Register the device driver
kbdozgurcharDevice = device_create(kbdozgurcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(kbdozgurcharDevice)) { // Clean up if there is an error
class_destroy(kbdozgurcharClass); // Repeated code but the alternative is goto statements
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "MOB: Failed to create the device\n");
return PTR_ERR(kbdozgurcharDevice);
}
printk(KERN_INFO "MOB: device class created correctly\n"); // Made it! device was initialized
/*
*****************************
* Bind interrupt
*****************************
*/
result = request_irq(1, (irq_handler_t) irq_handler, IRQF_SHARED, "kbdozgur", (void *) (irq_handler));
if (result) printk(KERN_INFO "MOB: can't get shared interrupt for keyboard\n");
printk(KERN_INFO "MOB: kbdozgur loaded.\n");
return result;
}
static void exit_mod(void) {
/*
* ****************************
* Destroy Character Device
* ****************************
*/
device_unregister(kbdozgurcharDevice);
device_destroy(kbdozgurcharClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(kbdozgurcharClass); // unregister the device class
class_destroy(kbdozgurcharClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "MOB: Goodbye from the LKM!\n");
/*
* ****************************
* Free IRQ bind
* ****************************
*/
free_irq(1, (void *) (irq_handler));
printk(KERN_INFO "MOB: kbdozgur unloaded.\n");
}
static int dev_open(struct inode *inodep, struct file *filep) {
numberOpens++;
printk(KERN_INFO "MOB: Device has been opened %d time(s)\n", numberOpens);
return 0;
}
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
// error_count = copy_to_user(buffer, message, size_of_message);
error_count = copy_to_user(buffer, "test", 4);
if (error_count == 0) { // if true then have success
// printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
return (size_of_message = 0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
sprintf(message, "%s(%d letters)", buffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO "MOB: Received %d characters from the user\n", len);
return len;
}
static int dev_release(struct inode *inodep, struct file *filep) {
printk(KERN_INFO "MOB: Device successfully closed\n");
return 0;
}
module_init(init_mod);
module_exit(exit_mod);
I am trying to build a skeleton driver for interrupt buffering and serving to userspace.
However my character device returns empty string to user space. I tried cat in shell and open and read in python. Both returns empty string. By the way it takes character array from user space normally and as i expected.
Related part >>
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
// error_count = copy_to_user(buffer, message, size_of_message);
error_count = copy_to_user(buffer, "test", 4);
if (error_count == 0) { // if true then have success
// printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", size_of_message, message);
printk(KERN_INFO "MOB: Sent %d characters to the user >> %s\n", 4, "test");
return (size_of_message = 0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
First i tried:
error_count = copy_to_user(buffer, message, size_of_message);
Then i tried for check:
error_count = copy_to_user(buffer, "test", 4);
Same story. Both returns empty string. There is no error. Nothing related in dmesg.
I make my trials as root user and the file has that permissions:
crw------- 1 root root 250, 0 Mar 30 14:43 /dev/kbdozgur
So where did i do wrong?
The read should return the number of bytes read. In your case, you are return 0.
size_message = 0
You should do the following
size_t size_requested;
...
if (len >= size_of_message) {
size_requested = size_of_message;
} else {
size_requested = len;
}
if (copy_to_user (buf, message, size_requested)) {
retval = -EFAULT;
return retval
}
return size_requested;

copy_to_user not working - where I am on mistake?

I am trying to get some message from kernel space to userspace, when a condition fails!
Here's my kernel code:
#define MESSAGTOUSER 1
int ret_val;
struct siginfo sinfo;
pid_t id;
struct task_struct *task;
unsigned char msgBuffer[20];
unsigned char buf1[20]= "HI";
static int major_no;
static struct class *safe_class;
static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static int device_open(struct inode *inode, struct file *file);
static int device_write(struct file *file, const char *gdata, size_t len, loff_t *off);
static int device_read(struct file *file, char *buf, size_t len, loff_t *off);
static int device_release(struct inode *inode, struct file *file);
int failureDetection (char* faultMsg) {
strcpy (msgBuffer, faultMsg);
printk(KERN_ALERT"\nMessage from HBM %s\n", msgBuffer);
printk(KERN_ALERT".......... RETURN VALUE ...... : %d", ret_val);
int Reg_Dev(void);
memset (&sinfo, 0, sizeof(struct siginfo));
sinfo.si_signo = SIGUSR1;
sinfo.si_code = SI_USER;
if (id == 0) {
printk("\ncan't find User PID: %d\n", id);
}else {
//task = pid_task(find_vpid(pid), PIDTYPE_PID);
task = find_task_by_vpid(id);
send_sig_info(SIGUSR1, &sinfo, task);
}
return 0;
}
static int device_open(struct inode *inode, struct file *file){
/*sucess*/
return 0;
}
void strPrint(void) {
printk("value of msgBuffer: %s", msgBuffer);
}
static int device_write(struct file *file, const char *gdata, size_t len, loff_t *off){
get_user (id,(int *)gdata);
if(id <0)
printk(KERN_ALERT"Cann't find PID from userspace its : %i", id);
else
printk(KERN_ALERT"Successfully received the PID of userspace %i", id);
return len;
}
static int
device_read(struct file *file, char *buf, size_t len, loff_t *off){
/*success*/
return 0;
}
static int device_release(struct inode *inode, struct file *file){
/*success*/
return 0;
}
static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
switch (cmd) {
case MESSAGTOUSER:
ret_val = copy_to_user((char *)arg, msgBuffer, sizeof(arg));
printk("Msg of Kernel %s", msgBuffer);
break;
default:
break;
}
return 0;
}
static struct file_operations fops = {
.open = device_open,
.write = device_write,
.read = device_read,
.release = device_release,
.unlocked_ioctl = device_ioctl
};
int Reg_Dev(void) {
major_no = register_chrdev(0, "safe_dev", &fops);
safe_class = class_create(THIS_MODULE, "safe_dev");
device_create(safe_class,NULL, MKDEV(major_no, 0), "safe_dev");
printk("\n Device Registered and Created \n");
return 0;
}
void UnReg_dev (void) {
printk("\nUser PID : %d\n", id);
unregister_chrdev(major_no, "safe_dev");
device_destroy(safe_class, MKDEV(major_no,0));
class_unregister(safe_class);
class_destroy(safe_class);
printk("\n Device Un-Registered and Destroyed \n");
}
extern int Reg_Dev(void);
for he userspace i have this code:
#define PORT 9930
#define G_IP "192.168.10.71"
#define BUFLEN 512
#define MESSAGTOUSER 0
unsigned char *str[20];
char b1[BUFLEN], b2[BUFLEN];
struct sockaddr_in me,client;
int s, i, n=sizeof(me);
int fd;
void error_handler(char *s) {
perror(s);
exit(1);
}
void signal_handler (int signum) {
if(signum == SIGUSR1)
{
printf("\n%s\n",str);
if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
error_handler("\nERROR: in Socket\n");
memset((char *) &me, 0, sizeof(me));
me.sin_family = AF_INET;
me.sin_port = PORT;
if (inet_aton(G_IP, &me.sin_addr)==0)
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
printf("Message from Kernel : %s", &str);
//strcpy (str, newStr);
int cntr =0; sprintf(b2, "\nFailure Message: %s\n",str);
printf("\nsending Fault to PMN Group : Tick - %d\n", cntr++);
if(sendto(s, str, sizeof(str),0,(struct sockaddr *) &me,n)==-1)
error_handler("\nERROR: in sendto()\n");
close (s);
// counter ++;
// sendAndReceiveOverUDP();
return;
}
}
int main() {
pid_t u_id;
u_id = getpid();
int i = 1;
fd = open("/dev/safe_dev",O_RDWR);
write(fd, &u_id, 4);
ioctl (fd, MESSAGTOUSER, &str);
printf("\n PID sent to device successfully: %d \n", u_id);
close(fd);
signal(SIGUSR1, signal_handler);
printf("\nMy PID is: %d\n",u_id);
//printf("Subnet 1 working fine.. Tick - %d", tv.tv_sec);
while (1)
sleep(1);
return 0;
}
Now what I am expecting to receive on Userspace:
Message from Kernel: A<->B
Sending Fault o PMN Group : tick - 0
Message from Kernel: B<->B
Sending Fault o PMN Group : tick - 1
....
...
but what is the output:
Message from Kernel:
Sending Fault o PMN Group : tick - 0
Message from Kernel:
Sending Fault o PMN Group : tick - 1
....
...
It seems that copy_to_user is not working, while in simple program just copying a string from kernel to user is working fine, but while i am using in this scenario then its not working, its compiling without any warning,
Some other Details:
failureDetection() is getting a string like A<->B mentioned in output from rest of the programs..
the same message from failureDetection is printing on kernel level but not transferring at the user level.
I have also tried to create an own string in this and tried to transfer that, but it is also not working! suppose msgBuffer = HI, then I should receive HI on to the userspace. but its not happening! can anyone please please make me correct whats wrong with this code? how can i get updates onto the userspace!!??
Sindhu..
The copy_to_user() only happens in response to the ioctl(), which only happens once, very early on in your code. Presumably at that point the kernel buffer msgBuffer is empty, because the failureDetection() function has not yet run at that point. It doesn't matter if failureDetection() runs later and sets msgBuffer then, because your userspace program never calls the ioctl() again so it doesn't see the new contents of msgBuffer.
You also have a bug in your copy_to_user() call - instead of sizeof(args) (which is a constant 4) you should probably use sizeof msgBuffer.
#caf: Thank you so much..
void signal_handler (int signum) {
if(signum == SIGUSR1)
{
fd = open ("/dev/safe_dev",O_RDWR);
ioctl (fd, MESSAGTOUSER, &str);
close (fd);
if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
error_handler("\nERROR: in Socket\n");
memset((char *) &me, 0, sizeof(me));
me.sin_family = AF_INET;
me.sin_port = PORT;
if (inet_aton(G_IP, &me.sin_addr)==0)
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
printf("Failure Detected on Eth Cards as : %s are non reachable.", str);
printf("\nsending Fault to PMN Group : Tick - %d\n", cntr++);
sprintf(b2, "\nFailure Message: %s\n",str);
if(sendto(s, str, sizeof(str),0,(struct sockaddr *) &me,n)==-1)
error_handler("\nERROR: in sendto()\n");
close (s);
return;
}
}
I was just making a stupid mistake.. hehehe.. i was not adding it in between file open and close block.. your suggestion resolved my issue...
Thank you so much for your response..
Rahee..

Resources