One ioctl not being executed in kernel module - c

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>.

Related

How to get struct i2c_client *client structure inside kernel thread

I am now developing a kernel module to handle two digits 7-segment led indicator via I2C device on RaspberryPi4 with RaspberryPiOS.
This module uses kernel thread to handle 7-segment led display, change digit position.
Sending command to i2c device requires i2c_client* structure, it can be obtained via formal argument of probe function.
Kernel thread does not have formal argument of i2c_client structure.
My solution is to store a pointer of i2c_client* structure into a global variable and use this pointer inside kernel thread, and call i2c function with this pointer.
My kernel module works well, at least now...
Do you have better solution to use i2c function in kernel thread? or exists better solution?
My entire code is below...
//i2c_7seg2_udev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/kthread.h>
MODULE_LICENSE("GPL v2");
#define DEVICE_NAME "i2c_7seg2_udev"
#define MINOR_BASE 0
#define MINOR_NUM 1
static unsigned int major_num;
struct cdev i2c_7seg2_udev_cdev;
void init_i2c_gpio(void);
void turn_off_7seg(void);
void turn_off_7seg(void);
void set_7seg(unsigned int number);
void change_7seg_keta(void);
#define LOOP_SLEEP_US (5000)
static struct task_struct *kthread;
static int dynamic_7seg_kthread(void *data);
static void init_kthread(void)
;
static int dynamic_7seg_kthread(void *data)
{
while(!kthread_should_stop()){
change_7seg_keta();
usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
}
return 0;
}
static void init_kthread(void)
{
kthread = kthread_create(dynamic_7seg_kthread, NULL, "dynamic_7seg_kthread");
wake_up_process(kthread);
}
static int init_regist_device(void);
static struct i2c_client *i2c_client_data;
#define DRIVER_NAME "i2c_7seg2_udev"
static struct i2c_device_id i2c_7seg2_udev_device_idtable[] = {
{"I2C_7SEG2_UDEV", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, i2c_7seg2_udev_device_idtable);
static int i2c_7seg2_udev_device_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
if(init_regist_device() < 0){
printk(KERN_DEBUG "i2c_7seg2_udev: initialize failed.\n");
return -1;
}
printk(KERN_DEBUG "i2c_7seg2_udev_device connected.\n");
printk(KERN_DEBUG "id.name = %s, id.driver_data = %ld", id->name, id->driver_data);
printk(KERN_DEBUG "device address is: 0x%02X\n", client->addr);
i2c_client_data = client;
init_i2c_gpio();
set_7seg(12);
init_kthread();
return 0;
}
static int i2c_7seg2_udev_device_remove(struct i2c_client *client)
{
dev_t dev;
turn_off_7seg();
printk(KERN_DEBUG "i2c_7seg2_udev: wait for thread to be terminated.\n");
kthread_stop(kthread);
printk(KERN_DEBUG "i2c_7seg2_udev: thread terminated.\n");
dev = MKDEV(major_num, MINOR_BASE);
cdev_del(&i2c_7seg2_udev_cdev);
unregister_chrdev_region(dev, MINOR_NUM);
printk(KERN_DEBUG "i2c_7seg2_udev_device disconnected.\n");
return 0;
}
static struct i2c_driver i2c_7seg2_udev_device_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.id_table = i2c_7seg2_udev_device_idtable,
.probe = i2c_7seg2_udev_device_probe,
.remove = i2c_7seg2_udev_device_remove,
};
#define MAX_7SEG_KETA (2)
//NOTE: "KETA" means digit position, in Japanese.
static const unsigned int seg7_pattern[] =
//--gfedcba
{0b00111111, //0
0b00000110, //1
0b01011011, //2
0b01001111, //3
0b01100110, //4
0b01101101, //5
0b01111100, //6
0b00100111, //7
0b01111111, //8
0b01100111 //9
};
unsigned char value_7seg[MAX_7SEG_KETA];
DEFINE_MUTEX(__mutex_value_7seg);
void set_7seg(unsigned int number){
unsigned int i;
printk(KERN_DEBUG "i2c_7seg2_udev: value %d .\n", number);
for( i = 0; i < MAX_7SEG_KETA; i++){
value_7seg[MAX_7SEG_KETA - i - 1] = (number % 10) + '0';
number = number / 10;
}
}
void change_7seg_keta(void)
{
static unsigned int keta = 0;
unsigned char keta_shift;
unsigned int number;
keta_shift = 0x01 << keta;
i2c_smbus_write_byte_data(i2c_client_data, 0x03, ~keta_shift); //P1に出力
number = value_7seg[MAX_7SEG_KETA - keta - 1] - '0';
i2c_smbus_write_byte_data(i2c_client_data, 0x02, ~seg7_pattern[number]); //P0に出力
keta ++;
if( keta >= MAX_7SEG_KETA ){
keta = 0;
}
}
void turn_off_7seg(void){
i2c_smbus_write_byte_data(i2c_client_data, 0x02, 0x7F); //P0に出力
}
void init_i2c_gpio(void){
i2c_smbus_write_byte_data(i2c_client_data, 0x06, 0x00);
i2c_smbus_write_byte_data(i2c_client_data, 0x07, 0x00);
}
static int i2c_7seg2_udev_open(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "i2c_7seg2_udev: opened.\n");
return 0;
}
static int i2c_7seg2_udev_close(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "i2c_7seg2_udev: closed.\n");
return 0;
}
static ssize_t i2c_7seg2_udev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char read_datas[MAX_7SEG_KETA + 2];
unsigned long bytes_left;
const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
unsigned int i;
for(i = 0; i < MAX_7SEG_KETA; i++ ){
read_datas[i] = value_7seg[i];
}
read_datas[i] = '\n';
read_datas[i + 1] = '\0';
bytes_left = copy_to_user(buf, read_datas, bytes_to_send);
return (bytes_to_send - bytes_left);
}
static ssize_t i2c_7seg2_udev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char write_values[MAX_7SEG_KETA + 2];
unsigned long bytes_left;
const unsigned long bytes_to_send = MAX_7SEG_KETA + 2;
int i;
printk(KERN_DEBUG "i2c_7seg2_udev: write.");
printk(KERN_DEBUG "i2c_7seg2_udev: write.");
bytes_left = copy_from_user(write_values, buf, bytes_to_send);
mutex_lock(&__mutex_value_7seg);
for(i = 0; i < MAX_7SEG_KETA; i++){
if( (write_values[i] >= '0') && (write_values[i] <= '9') ){
value_7seg[i] = write_values[i];
}
}
mutex_unlock(&__mutex_value_7seg);
return (bytes_to_send - bytes_left);
}
struct file_operations s_i2c_7seg2_udev_fops = {
.open = i2c_7seg2_udev_open,
.release = i2c_7seg2_udev_close,
.read = i2c_7seg2_udev_read,
.write = i2c_7seg2_udev_write,
};
int init_regist_device(void)
{
int device_num = 0;
int devnum_err = 0;
int cdev_err = 0;
dev_t dev;
devnum_err = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DEVICE_NAME);
if (devnum_err != 0) {
printk(KERN_ERR "devnum_err = %d\n", devnum_err);
return -1;
}
if (device_num != 0) {
printk(KERN_ERR "i2c_7seg2_udev: error_init_regist_device , %d\n", device_num);
return -1;
}
major_num = MAJOR(dev);
cdev_init(&i2c_7seg2_udev_cdev, &s_i2c_7seg2_udev_fops);
i2c_7seg2_udev_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&i2c_7seg2_udev_cdev, dev, MINOR_NUM);
if (cdev_err != 0) {
printk(KERN_ERR "cdev_add = %d\n", cdev_err);
unregister_chrdev_region(dev, MINOR_NUM);
return -1;
}
printk(KERN_DEBUG "i2c_7seg2_udev: device registerd.\n");
return 0;
}
static int i2c_7seg2_udev_device_init(void)
{
printk(KERN_DEBUG "i2c_7seg2_udev device driver loaded.\n");
i2c_add_driver(&i2c_7seg2_udev_device_driver);
return 0;
}
static void i2c_7seg2_udev_device_exit(void)
{
printk(KERN_DEBUG "i2c_7seg2_udev device driver unloading.\n");
i2c_del_driver(&i2c_7seg2_udev_device_driver);
}
module_init(i2c_7seg2_udev_device_init);
module_exit(i2c_7seg2_udev_device_exit);
The second argument of the function kthread_create() is void *data.
Right now you are passing NULL, you can pass your pointer to the struct i2c_client instead.
Then you can do:
static int dynamic_7seg_kthread(void *data) {
struct i2c_client *client = data;
while (!kthread_should_stop()) {
change_7seg_keta(client);
usleep_range(LOOP_SLEEP_US, LOOP_SLEEP_US * 2);
}
return 0;
}
Thus eliminating global variable from the code.

Using multithreads to copy a single file at the same time, outputting the same file down to the MD5 check sum

The problem I'm running into is that when the file tries to copy, it only copies part of the file and the other part is a bunch of unreadable characters. This is for an academic assignment that wants us to use barrier synchronization so we need to use open, write, and read.
I've reworked the thread function many times but if it's the problem I can change it again, I haven't changed the for loop in main at all so even that might be the problem but I don't know what it could be. Lastly, I don't really know what to do with the barrier; my professor was very vague and I can't really ask him questions, maybe the barrier is the part that I'm truly missing.
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
typedef struct args {
int fd;
int copy;
long int start;
long int size;
}threadarg;
int barrier = 0;
int main(int argc, char *argv[])
{
void usage(char *progname);
int chkdst(char **argv);
void die(char *why);
long int filesize(char *srcpath);
void buildpath(char *src, char *dst, char **dstpath);
int isvalid(char *path, char *dst);
void *dowork(void *arg);
if (argc < 4) usage("a8");
int workers, i;
char *check;
workers = strtol(argv[3], &check, 10);
if (!check) usage("a8");
else if (!chkdst(&argv[2])) die ("DST not valid!");
long int size = filesize(argv[1]);
if (size == -1) die ("Could not find file size");
char *dstpath; buildpath(argv[1], argv[2], &dstpath);
if (!isvalid(argv[1], dstpath)) die ("scr not valid!");
long int chunksize = size / workers;
long int remainder = size % workers;
int fd = open(argv[1], O_RDONLY);
int copy = open(dstpath, O_CREAT | O_RDWR, 0644);
if (fd < 0 || copy < 0) die("Fail to access or create files");
barrier = workers;
threadarg threadargs[workers];
pthread_t threads[workers];
for (i = 0; i < workers; i++)
{
threadargs[i].fd = fd;
threadargs[i].copy = copy;
threadargs[i].start = i * chunksize;
if (i == workers - 1)
threadargs[i].size = chunksize + remainder;
else
threadargs[i].size = chunksize;
if (pthread_create(&threads[i], NULL, dowork, (void *) &threadargs[i]))
die("Thread Creation Failure");
}
for (i = 0; i < workers; i++)
pthread_join(threads[i], NULL);
}
void usage(char *progname)
{
fprintf(stderr, "./%s srcpath dstpath workercount\n", progname);
exit(0);
}
void die(char *why)
{
fprintf(stderr, "Program Killed...\nReason: %s\n", why);
exit(1);
}
long int filesize(char *srcpath)
{
struct stat st;
if(stat(srcpath, &st) != 0) return 0;
return st.st_size;
}
/*
void domd5(char *path)
{
}
*/
void *dowork(void *arg)
{
threadarg *args = (threadarg *)arg;
int fd = args->fd,
copy = args->copy, rd;
long int start = args->start,
size = args->size;
char bufs[2048], *remains;
lseek(fd, start, SEEK_SET);
lseek(copy, start, SEEK_SET);
printf("%d thread with offset %ldKB, reached barrier\n", (int) pthread_self(), start);
barrier--;
while (barrier > 0);
long int count = 0, remainder = 0, i;
for (i = 0; i < size; i += 2048)
{
if (i + 2048 > size)
{
remainder = size - count;
remains = malloc(remainder * sizeof(char));
rd = read (fd, remains, sizeof(remains));
if (write(copy, remains, rd) != rd)
die("Error accessing files during copy");
count += remainder;
}
else
{
rd = read(fd, bufs, sizeof(bufs));
if (write(copy, bufs, rd) != rd)
die("Error accessing files during copy");
count += 2048;
}
}
pthread_exit(NULL);
}
/* Takes a single pointer, *argv, and passes it to isdir()
to check if the directory exists. If isdir returns a 1 a
1 is returned from this module. Otherwise, an error message
is printed and a 0 is returned.
Calls isdir().
Called by main().
*/
int chkdst(char **argv)
{
int isdir(char *path);
if (isdir(*argv)) return 1;
return 0;
}
/* Uses the Stat struct to construct a struct, sbuf,
and uses stat() to obtain information from the file and
write it to sbuf. Uses S_ISDIR() on sbuf.st_mode to see
the mode of the file. A 1 is returned if the file is a
directory otherwise a 0 is returned.
Called by isvalid().
*/
int isdir(char *path)
{
struct stat sbuf;
if (stat(path, &sbuf)) return 0;
return S_ISDIR(sbuf.st_mode);
}
/* Uses the Stat struct to construct a struct, sbuf,
and uses stat() to obtain information from the file and
write it to sbuf. Uses S_ISREG on sbuf.st_mode to see if
the file is regular. A 1 is returned if the S_ISREG is true
otherwise a 0 is returned.
Called by isvalid().
*/
int isregular(char *path)
{
struct stat sbuf;
if (stat(path, &sbuf)) return 0;
return S_ISREG(sbuf.st_mode);
}
/* Checks if the source path is a directory first, then if its
a regular file return 0 if it is dir and if it isn't a regular
file, then checks if the destionation path was created or if
the file exist at the destination if either return 0, if none
of these return 1.
Calls isdir() and isregular().
Called by copyfiles().
*/
int isvalid(char *path, char *dst)
{
if (isdir(path))
{
return 0;
}
else if (!isregular(path))
{
return 0;
}
else if (dst == NULL)
{
return 0;
}
else if (isregular(dst))
{
return 0;
}
return 1;
}
/* Builds destination-path using strrchr() function from library,
dstpath is null on error and defined otherwise. The src file has
its original destination removed and replaced with the new one if
it has a original destination on it otherwise it is just added to
the end of the existing name of the file.
Called by copyfiles().
*/
void buildpath(char *src, char *dst, char **dstpath)
{
char *ptr;
int n;
ptr = strrchr(src, '/');
if (ptr) n = strlen(dst) + strlen(ptr) + 2;
else n = strlen(dst) + strlen(src) + 2;
*dstpath = malloc(n);
if (!dstpath) return;
if (ptr)
{
strcpy(*dstpath, dst);
strcat(*dstpath, ptr);
}
else
{
strcpy(*dstpath, dst);
strcat(*dstpath, "/");
strcat(*dstpath, src);
}
}

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 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;

A simple character device driver

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

Resources