I am trying to implement a FIFO using character driver. However while writing to the device it doesn't seem to work. It doesn't seems to end the loop. Any help or link is appreciated. I have taken help from many sources so current code is kind of mess with many things which shouldn't be as they are.
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off) {
int mode;
int ind;
ssize_t count = -ENOMEM;
printk(KERN_ALERT "to be written : %s\n", buff);
mode = iminor(filp->f_dentry->d_inode);
printk(KERN_ALERT "Device minor : %d\n", mode);
if ((mode == 1) || (mode ==3))
return -EINVAL;
if (mode == 0){
count = 0;
ind = 0;
if (buff[ind] == NULL) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position1 : %d\n", writePos1);
while(ind<=len) { //loop untill we have something to writer
if (down_interruptible(&buffer1_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer1 flag didn't work\t %d", buffer1_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
up(&buffer1_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue1[writePos1] = buff[ind];
printk(KERN_ALERT "Write %d %c\n",ind,queue1[writePos1]);
if (writePos1 == 9){
writePos1 = 0;
}
else
writePos1++;
count++;
}
up(&flag1);
}
up(&buffer1_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position1 now: %d\t and count%d\n", writePos1,count);
return count-1;
}
I just wrote my own module and I suspect that your issue is that the process calling dev_write expects dev_write to return the number of bytes written. If you don't return the correct number (I see you are returning count - 1), dev_write will be called again and again.
dev_read I have found to be similar - until it returns 0, the process will repeatedly call it - expecting that there are more characters to be retrieved (this makes sense).
I have written/modified a much more simple module that illustrates using a module as a character buffer (sorry, it's quite hastily written). It should allow you to echo a string to it, and return that string when you cat or otherwise read from it. This is demonstrated when you make run. I am sure you will be able to easily modify it to become FIFO.
I don't accept any liability for crashing your kernel or other problems, and you should be using a VM anyway.
It is long, so on my github:
git clone https://github.com/n-hutton/tempRepo
cd tempRepo
make
make run
Finally I corrected complete code. It is very much immature, illogical for few logics still it works the I would like to do it. #Nathan Hutton helped a lot in improving it and making it work. I thank him for that. Though there are many things I still have one major doubt, if you could trace it in kernel logs, an extra line feed character is added every time whenever you write to a device with (echo "test" |cat >/dev/fifo0)
to run it properly you will also need to create 4 char devices with Major number 240, minor 0,1,2,3. mknod can be used as:
"mknod /dev/fifo(0,1,2,3) c 240 (0,1,2,3) -m 777" //0,1,2,3 at a time and 777 can be modified for granularity. Finally working code:
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/unistd.h>
#include <linux/time.h>
#include <linux/semaphore.h>
#define DEVNO 240
#define DEVNAME "fifo"
MODULE_LICENSE("GPL");
DECLARE_WAIT_QUEUE_HEAD(writing1);
//static short writeFlag1=0;
static DEFINE_SEMAPHORE(flag1);
static DEFINE_SEMAPHORE(flag2);
static struct semaphore buffer1_f;
static struct semaphore buffer2_f;
static struct semaphore buffer1_e;
static struct semaphore buffer2_e;
DECLARE_WAIT_QUEUE_HEAD(writing2);
//static short writeFlag2=0;
static char queue1[10]={0};
static short readPos1=0;
static short writePos1=0;
//static short qsize1=0;
static char queue2[10]={0};
static short readPos2=0;
static short writePos2=0;
//static short qsize2=0;
static int times=0;
static int dev_open(struct inode *,struct file *);
static int dev_rls(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={
.read=dev_read,
.write=dev_write,
.open=dev_open,
.release=dev_rls,
};
int init_module(void){
unsigned int devno = 240;
char *devname = "fifo";
int t;
sema_init(&buffer1_f,0);
sema_init(&buffer2_f,0);
sema_init(&buffer1_e,10);
sema_init(&buffer2_e,10);
memset(queue1,0,10);
memset(queue2,0,10);
t=register_chrdev(devno,devname,&fops);
if(t<0) printk(KERN_ALERT "device reg failed. \n");
else printk(KERN_ALERT "Device registered. \n");
return t;
}
void cleanup_module(void) {
unregister_chrdev(240,"fifo");
printk(KERN_ALERT "Device has been removed");
}
static int dev_open(struct inode *inod, struct file *fil){
times++;
printk(KERN_ALERT "Device opened %d times\n",times);
return 0;
}
static ssize_t dev_read(struct file *filep, char *buff, size_t len, loff_t *off) {
int mode = iminor((filep->f_dentry->d_inode));
short count;
printk(KERN_ALERT "Device minor when read : %d\n", mode);
if ((mode == 0) || (mode ==2))
return -EINVAL;
else if (mode == 1){
count = 0;
printk(KERN_ALERT "Read position1 when read: %d\n", readPos1);
while(len) { //loop untill we have something to write or empty buffer
if (readPos1==writePos1){
printk(KERN_ALERT "Returning chars put to buffer: %d\n", count);
return count;
}
if (down_interruptible(&buffer1_f)) {
printk(KERN_ALERT "flag1 didn't work");
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
return -EINVAL;
}
else {
printk(KERN_ALERT "Read %c\n",queue1[readPos1]);
put_user(queue1[readPos1],buff++);
if (writePos1==-1) writePos1=readPos1;
if (readPos1 == 9) readPos1 = 0;
else readPos1++;
count++;
}
up(&flag1);
}
up(&buffer1_e);
}
printk(KERN_ALERT "Read position1 now: %d\t and count%d\n", readPos1,count);
return count;
}
else if (mode == 3){
count = 0;
printk(KERN_ALERT "Read position2 when read: %d\n", readPos2);
while(len) { //loop untill we have something to write or empty buffer
if (readPos2==writePos2){
printk(KERN_ALERT "Returning chars put to buffer: %d\n", count);
return count;
}
if (down_interruptible(&buffer2_f)) {
printk(KERN_ALERT "flag2 didn't work");
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag2)){
return -EINVAL;
}
else {
printk(KERN_ALERT "Read %c\n",queue2[readPos2]);
put_user(queue2[readPos2],buff++);
if (writePos2==-1) writePos2=readPos2;
if (readPos2 == 9) readPos2 = 0;
else readPos2++;
count++;
}
up(&flag2);
}
up(&buffer2_e);
}
printk(KERN_ALERT "Read position2 now: %d\t and count%d\n", readPos2,count);
return count;
}
else {
printk(KERN_ALERT "Not correct mode\n");
return -1;
}
}
static char Message[100] = "Initial message\n";
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off) {
int mode;
int ind;
ssize_t count = -ENOMEM;
int i;
//Let's copy the message onto our stack so we can be clear what we are getting
for (i = 0; i < 99 && i < len; i++){
char getChar;
get_user(getChar, buff + i);
Message[i] = getChar;
}
Message[i] = '\0';
printk(KERN_ALERT "to be written : %s\n", Message);
mode = iminor(filp->f_dentry->d_inode);
printk(KERN_ALERT "Device minor : %d\n", mode);
if ((mode == 1) || (mode ==3))
return -EINVAL;
else if (mode == 0){
count = 0;
ind = 0;
if (( buff == NULL) || (*buff == 0)) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position1 : %d\n", writePos1);
while(ind<len) { //loop untill we have something to writer
if (down_interruptible(&buffer1_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer1 flag didn't work\t %d", buffer1_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag1)){
up(&buffer1_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue1[writePos1] = buff[ind];
printk(KERN_ALERT "Write ind:%d writepos:%d readpos;%d char:%c\tascii%d\n",ind,writePos1,readPos1,queue1[writePos1],(int)queue1[writePos1]);
if (readPos1==((writePos1+1)%10)) {
writePos1=-1;
}
else if (writePos1 == 9){
writePos1 = 0;
}
else
writePos1++;
count++;
}
printk(KERN_ALERT "writepos:%d",writePos1);
up(&flag1);
}
up(&buffer1_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position1 now: %d\t and count%d\n", writePos1,(int)count);
printk(KERN_ALERT "Note: our allowable buffer length was %d\n", (int)len);
return count;
}
else if (mode == 2){
count = 0;
ind = 0;
if (( buff == NULL) || (*buff == 0)) {
return -ENOMEM;
}
printk(KERN_ALERT "Write position2 : %d\n", writePos2);
while(ind<len) { //loop untill we have something to writer
if (down_interruptible(&buffer2_e)) { //taking flag first isn't right because that won't allow other guyto give access to our turn.
printk(KERN_ALERT "buffer2 flag didn't work\t %d", buffer2_e.count);
return -ERESTARTSYS;
}
else {
if (down_interruptible(&flag2)){
up(&buffer2_e); //must because we couldn't write it properly
return -EINVAL;
}
else {
queue2[writePos2] = buff[ind];
printk(KERN_ALERT "Write ind:%d writepos2:%d readpos2;%d char:%c\tascii%d\n",ind,writePos2,readPos2,queue2[writePos2],(int)queue2[writePos2]);
if (readPos2==((writePos2+1)%10)) {
writePos2=-1;
}
else if (writePos2 == 9){
writePos2 = 0;
}
else
writePos2++;
count++;
}
printk(KERN_ALERT "writepos:%d",writePos2);
up(&flag2);
}
up(&buffer2_f);
off += count;
ind++;
}
printk(KERN_ALERT "Write position2 now: %d\t and count%d\n", writePos2,(int)count);
printk(KERN_ALERT "Note: our allowable buffer length was %d\n", (int)len);
return count;
}
else {
printk(KERN_ALERT "This meant wrong device minor accessed\n");
return -1;
}
}
static int dev_rls(struct inode *inod, struct file *fil) {
printk(KERN_ALERT "Device is closed\n");
return 0;
}
Related
SDL capture audio callbacks seem to be called once for every 12 playback callbacks. Am I doing something wrong? This feels like an SDL or PulseAudio bug.
The program below prints "Reading audio..." once every ~12 "Writing audio..." prints. Tested via running the command directly, through gdb, and through Valgrind.
I've tried this in both C and Golang (using github.com/veandco/go-sdl2/sdl), on the same machine.
C code:
// A test program to copy audio in (microphone) to audio out (speaker) via SDL.
//
// Compile: cc inout.c -o inout -lSDL2
// Run: ./inout
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define BUF_SIZE 1024
// Stereo float32 samples.
static uint8_t saved[BUF_SIZE*2*sizeof(float)];
// Copies audio callback data into the saved buffer.
void audioReader(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Reading audio: %d -> %ld bytes\n", len, sizeof(saved));
memcpy(saved, buf, len);
}
// Copies saved audio data into the callback buffer.
void audioWriter(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Writing audio: %ld -> %d bytes\n", sizeof(saved), len);
memcpy(buf, saved, len);
}
// List all devices of the given type, and return the name of the first or NULL.
// Caller must free the returned pointer.
char* ChooseDevice(int is_capture) {
int dev_cnt = SDL_GetNumAudioDevices(is_capture);
if (dev_cnt < 1) {
fprintf(stderr, "No %s devices: %s\n", is_capture ? "capture" : "playback", SDL_GetError());
return NULL;
}
printf("%s devices:\n", is_capture ? "capture" : "playback");
char* dev_name = NULL;
for (int i = 0; i < dev_cnt; i++) {
printf("%c %s\n", !dev_name ? '*' : ' ', SDL_GetAudioDeviceName(i, is_capture));
if (!dev_name) {
const char* tmp = SDL_GetAudioDeviceName(i, is_capture);
dev_name = malloc(strlen(tmp)+1);
strcpy(dev_name, tmp);
}
}
if (!dev_name) {
fprintf(stderr, "No %s devices\n", is_capture ? "capture" : "playback");
}
return dev_name;
}
// Opens and unpauses the first device of the given type, returning its ID, or
// returns 0.
SDL_AudioDeviceID OpenDevice(int is_capture) {
char* dev_name = ChooseDevice(is_capture);
if (!dev_name) return 0;
SDL_AudioSpec spec;
SDL_memset(&spec, 0, sizeof(spec));
spec.freq = 48000;
spec.format = AUDIO_F32;
spec.channels = 2;
spec.samples = BUF_SIZE;
spec.callback = is_capture ? audioReader : audioWriter;
SDL_AudioDeviceID dev_id = SDL_OpenAudioDevice(dev_name, is_capture, &spec, NULL, 0);
if (dev_id == 0) {
fprintf(stderr, "Failed to open %s device %s: %s\n", is_capture ? "input" : "output", dev_name, SDL_GetError());
return 0;
}
free(dev_name);
SDL_PauseAudioDevice(dev_id, SDL_FALSE);
return dev_id;
}
int main(int argc, char** argv) {
SDL_memset(saved, 0, sizeof(saved));
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Failed to initialize SDL audio: %s\n", SDL_GetError());
return 1;
}
SDL_AudioDeviceID in_dev_id = OpenDevice(/* is_capture = */ SDL_TRUE);
if (in_dev_id == 0) return 1;
SDL_AudioDeviceID out_dev_id = OpenDevice(/* is_capture = */ SDL_FALSE);
if (out_dev_id == 0) return 1;
SDL_Delay(10000); // 10 seconds
SDL_CloseAudioDevice(in_dev_id);
SDL_CloseAudioDevice(out_dev_id);
SDL_Quit();
return 0;
}
In this code, I am trying to monitor two paths at the same time. I used while(1) for this purpose. But the problem that I am facing is that whenever I run the code, it gives me the same result two times like this.
Giving result
Pathname1 "file" is modified
Pathname1 "file" is modified
Expected result
Pathname1 "file" is modified
I debugged the code. After breaking the main function and stepping over it, the next command stops at this line length = read(fd, buffer, EVENT_BUF_LEN ). Whenever I break a line after this length variable command, the program starts and after modifying the file, the program stops at this line struct inotify_event *event = ( struct inotify_event *)&buffer[i]; Although the program should not break.
I also used IN_CLOSE_WRITE instead of IN_MODIFY but no change in the result.
typedef struct{
int length, fd, wd1, wd2;
char buffer[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1, char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);
while(1){
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
int i = 0;
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
if(event->len){
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n", event->name);
break;
}
if(event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n", event->name);
break;
}
}
}
i += EVENT_SIZE + event->len;
}
}
inotify_rm_watch(inotify.fd, inotify.wd1);
inotify_rm_watch(inotify.fd, inotify.wd2);
close(inotify.fd);
exit(0);
}
Some remarks:
You should not "break" as you go out of the inside "while" loop and call read() again
The IN_MODIFY event does not fill the event->name field (i.e. event->len = 0)
The number of IN_MODIFY events depends on how you modify the files ("echo xxx >> file" = write only = 1 IN_MODIFY; "echo xxx > file" = truncate + write = 2 IN_MODIFY)
Here is a proposition for your program (with additional prints):
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <stdlib.h>
#define EVENT_BUF_LEN 4096
#define EVENT_SIZE sizeof(struct inotify_event)
typedef struct{
int length, fd, wd1, wd2;
char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1, char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
printf("wd1 = %d\n", inotify.wd1);
inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);
printf("wd2 = %d\n", inotify.wd2);
while(1){
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
int i = 0;
printf("read() = %d\n", inotify.length);
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
printf("event->len = %u\n", event->len);
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 is modified\n");
} else if (event->wd == inotify.wd2){
printf("Pathname2 is modified\n");
}
}
i += (EVENT_SIZE + event->len);
printf("i=%d\n", i);
}
}
inotify_rm_watch(inotify.fd, inotify.wd1);
inotify_rm_watch(inotify.fd, inotify.wd2);
close(inotify.fd);
exit(0);
}
int main(void)
{
getNotified("/tmp/foo", "/tmp/bar");
return 0;
} // main
Here is an example of execution:
$ gcc notif.c -o notif
$ > /tmp/foo
$ > /tmp/bar
$ ./notif
wd1 = 1
wd2 = 2
====== Upon "> /tmp/foo": 1 event (truncate operation)
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty > /tmp/foo": 2 events (write operation, one event for truncate operation and one for the write of "qwerty" at the beginning of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
If the two pathnames refer to the same inode, then inotify.wd1 == inotify.wd2. In that case, because you have
if (event->wd == inotify.wd1) {
printf("Pathname1 '%s' is modified\n", event->name);
}
if (event->wd == inotify.wd2) {
printf("Pathname2 '%s' is modified\n", event->name);
}
both bodies would be executed; but the outputs would differ (Pathname1 ... and Pathname2 ...).
You really should be monitoring the directories the files reside in, rather than the actual pathnames, so you can catch rename events also. (Many editors create a temporary file, and rename or hardlink the temporary file over the old file, so that programs see either the old or the new file, and never a mix of the two.)
Consider the following program, which does not exhibit any undue duplication of events:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = signum;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
return sigaction(signum, &act, NULL);
}
struct monitor {
/* Supplied by caller */
const char *directory;
const char *pathname;
int (*modified)(struct monitor *);
int (*completed)(struct monitor *);
/* Reserved for internal use */
int dirwatch;
};
int monitor_files(struct monitor *const list, const size_t count)
{
char *events_ptr = NULL;
size_t events_len = 65536;
size_t i;
int err;
/* Verify sane parameters */
if (count < 1) {
errno = ENOENT;
return -1;
} else
if (!list) {
errno = EINVAL;
return -1;
}
for (i = 0; i < count; i++) {
if (!list[i].directory || !list[i].directory[0]) {
errno = EINVAL;
return -1;
}
if (!list[i].pathname || !list[i].pathname[0]) {
errno = EINVAL;
return -1;
}
list[i].dirwatch = -1;
}
/* Obtain a descriptor for inotify event queue */
int queue = inotify_init1(IN_CLOEXEC);
if (queue == -1) {
/* errno set by inotify_init1() */
return -1;
}
/* Use a reasonable dynamically allocated buffer for events */
events_ptr = malloc(events_len);
if (!events_ptr) {
close(queue);
errno = ENOMEM;
return -1;
}
/* Add a watch for each directory to be watched */
for (i = 0; i < count; i++) {
list[i].dirwatch = inotify_add_watch(queue, list[i].directory, IN_CLOSE_WRITE | IN_MOVED_TO | IN_MODIFY);
if (list[i].dirwatch == -1) {
err = errno;
close(queue);
free(events_ptr);
errno = err;
return -1;
}
}
/* inotify event loop */
err = 0;
while (!done) {
ssize_t len = read(queue, events_ptr, events_len);
if (len == -1) {
/* Interrupted due to signal delivery? */
if (errno == EINTR)
continue;
/* Error */
err = errno;
break;
} else
if (len < -1) {
/* Should never occur */
err = EIO;
break;
} else
if (len == 0) {
/* No events watched anymore */
err = 0;
break;
}
char *const end = events_ptr + len;
char *ptr = events_ptr;
while (ptr < end) {
struct inotify_event *event = (struct inotify_event *)ptr;
/* Advance buffer pointer for next event */
ptr += sizeof (struct inotify_event) + event->len;
if (ptr > end) {
close(queue);
free(events_ptr);
errno = EIO;
return -1;
}
/* Call all event handlers, even duplicate ones */
for (i = 0; i < count; i++) {
if (event->wd == list[i].dirwatch && !strcmp(event->name, list[i].pathname)) {
if ((event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE)) && list[i].completed) {
err = list[i].completed(list + i);
if (err)
break;
} else
if ((event->mask & IN_MODIFY) && list[i].modified) {
err = list[i].modified(list + i);
if (err)
break;
}
}
}
if (err)
break;
}
if (err)
break;
}
close(queue);
free(events_ptr);
errno = 0;
return err;
}
static int report_modified(struct monitor *m)
{
printf("%s/%s: Modified\n", m->directory, m->pathname);
fflush(stdout);
return 0;
}
static int report_completed(struct monitor *m)
{
printf("%s/%s: Completed\n", m->directory, m->pathname);
fflush(stdout);
return 0;
}
int main(void)
{
struct monitor watch[2] = {
{ .directory = ".",
.pathname = "file1",
.modified = report_modified,
.completed = report_completed },
{ .directory = ".",
.pathname = "file2",
.modified = report_modified,
.completed = report_completed }
};
int err;
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGTERM) == -1) {
fprintf(stderr, "Cannot set signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "To stop this program, press Ctrl+C, or send\n");
fprintf(stderr, "INT, HUP, or TERM signal (to process %ld).\n", (long)getpid());
fflush(stderr);
err = monitor_files(watch, 2);
if (err == -1) {
fprintf(stderr, "Error monitoring files: %s.\n", strerror(errno));
return EXIT_FAILURE;
} else
if (err) {
fprintf(stderr, "Monitoring files failed [%d].\n", err);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
If you compile it using e.g. gcc -Wall -Wextra -O2 example.c -o example and run it via ./example, it will report IN_MODIFY events (as "Modified") and IN_CLOSE_WRITE and IN_MOVED_TO events (as "Completed") for file1 and file2 in the same directory (current working directory). To exit the program, press Ctrl+C.
(Note that we should probably add third event type, "Deleted", corresponding to IN_DELETE and IN_MOVED_FROM events.)
If you open file1 or file2 in say a text editor, saving the file generates one or more Modified (IN_MODIFY) events, and exactly one Completed (IN_CLOSE_WRITE or IN_MOVED_TO) event. This is because each open()/truncate()/ftruncate() syscall that causes the file to be truncated, generates one IN_MODIFY event for that file; as does every underlying write() syscall that modifies the file contents. Thus, it is natural to receive multiple IN_MODIFY events when some process modifies the file.
If you have multiple struct monitor entries in the list with the same effective directory and the same pathname, the example program will provide multiple events for them. If you wish to avoid this, just make sure that whenever .pathname matches, the two get differing .dirwatches.
rewriting the while() loop into a for() loop, and flattening out the if()s"
while(1){
int i ;
struct inotify_event *event ;
inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN);
for(i=0; i < inotify.length; i += EVENT_SIZE + event->len ) {
event = (struct inotify_event *)&inotify.buffer[i];
if (!event->len) continue;
if (!(event->mask & IN_MODIFY)) continue;
if (event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n", event->name);
continue;
}
if (event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n", event->name);
continue;
}
}
}
I'm trying to build a small demonstration kernel module for Linux which finds a specific process and reads a value from that process' memory.
I have put together the following code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
unsigned long ret;
void __user *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
// Attempt to get the data from the start of the vma
addr = (void __user *)vma->vm_start;
if (access_ok(VERIFY_READ, addr, size)) {
ret = copy_from_user(buf, addr, size);
if (ret == 0) {
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
} else {
printk(KERN_ALERT "mymodule: Failed to copy %lu bytes from userspace\n", ret);
}
} else {
printk(KERN_ALERT "mymodule: access_ok check failed\n");
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
static int __init mymodule_init(void) {
struct task_struct *task;
bool found = false;
printk(KERN_ALERT "mymodule: Starting\n");
// Find the process
rcu_read_lock();
for_each_process(task) {
if (strcmp(task->comm, "example-process") == 0) {
printk(KERN_ALERT "mymodule: Found pid %d for process %s\n", task->pid, task->comm);
found = true;
break;
}
}
rcu_read_unlock();
if (!found) {
printk(KERN_ALERT "mymodule: Process not found, aborting\n");
return -1;
}
read_process_memory(task);
return 0;
}
static void __exit mymodule_exit(void) {
printk(KERN_ALERT "mymodule: Stopped\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("user");
Everything works fine until the copy_from_user call, which returns a non-zero count (actually, it returns size, meaning it didn't ready any data), regardless of the process and vma I try to read from.
Is there a misunderstanding on my part, or anything I'm doing wrong?
EDIT:
Here's a working version of read_process_memory:
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
int ret;
void *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
struct page *pages[1];
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Get the first page
ret = get_user_pages_remote(task, mm, vma->vm_start, 1, FOLL_FORCE, pages, NULL);
if (ret > 0) {
// We got our page, we should now be able to map it and read directly
addr = kmap(*pages);
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
strncpy(buf, addr, size);
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
// Make sure to release our page
kunmap(*pages);
put_page(*pages);
} else {
printk(KERN_ALERT "mymodule: Failed to read page at %p (errno=%d)\n", (void *)vma->vm_start, ret);
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
I'd say your code is wrong. Please be noted you are accessing memory of remote process (not current). To do that correctly you must use one of the GUP APIs(e.g. get_user_pages_remote()) to grab the pages first. The uaccess APIs (e.g. copy_from_user) only works for current process.
Here is an example in iouring:
https://elixir.bootlin.com/linux/latest/source/fs/io_uring.c#L4839
#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;
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