LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel
User Name
Password
Linux - Kernel This forum is for all discussion relating to the Linux kernel.

Notices


Reply
  Search this Thread
Old 06-08-2013, 04:03 AM   #1
wedz
LQ Newbie
 
Registered: Jun 2008
Posts: 12

Rep: Reputation: 0
Question Writing a char driver. Sending from user space works, copy_to_user always 0


So I'm working my way through kernel driver programming, and currently I'm trying to build a simple data transfer between application and kernel driver.

I am using simple character device as a link between these two, and I have succeeded to transfer data to driver, but I can't get meaningful data back to user space.

Kernel driver looks like this:

Code:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <asm/uaccess.h> /* copy_from/to_user */

MODULE_LICENSE("GPL");

//Declarations
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file access functions */
struct file_operations memory_fops = {
    read: memory_read,
    write: memory_write,
    open: memory_open,
    release: memory_release
};

//Default functions
module_init(memory_init);
module_exit(memory_exit);

/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char* tx_buffer;
char* rx_buffer;

int BUFFER_SIZE=64;
int actual_rx_size=0;

int memory_init(void) {
    int result;

    /* Registering device */
    result = register_chrdev(memory_major, "move_data", &memory_fops);
    if (result < 0) {
        printk(
        "<1>move_data: cannot obtain major number %d\n", memory_major);
        return result;
    }

    /* Allocating memory for the buffers */
    //Allocate buffers
    tx_buffer = kmalloc(BUFFER_SIZE,  GFP_KERNEL);
    rx_buffer = kmalloc(BUFFER_SIZE,  GFP_KERNEL);

    //Check allocation was ok
    if (!tx_buffer || !rx_buffer) {
        result = -ENOMEM;
        goto fail;
    }

    //Reset the buffers
    memset(tx_buffer,0, BUFFER_SIZE);
    memset(rx_buffer,0, BUFFER_SIZE);

    printk("<1>Inserting memory module\n"); 
    return 0;

    fail:
        memory_exit(); 
        return result;
}

void memory_exit(void) {
    /* Freeing the major number */
    unregister_chrdev(memory_major, "memory");

    /* Freeing buffers */
    if (tx_buffer) {
        kfree(tx_buffer); //Note kfree
    }

    if (rx_buffer) {
        kfree(rx_buffer); //Note kfree
    }
    printk("<1>Removing memory module\n");
}


//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { 

    printk("user requesting data, our buffer has (%d) \n", actual_rx_size);

    /* Transfering data to user space */ 
    int retval = copy_to_user(buf,rx_buffer,actual_rx_size);

    printk("copy_to_user returned (%d)", retval);

    return retval;
}

ssize_t memory_write( struct file *filp, char *buf,
                  size_t count, loff_t *f_pos) {

    //zero the input buffer
    memset(tx_buffer,0,BUFFER_SIZE);
    memset(rx_buffer,0,BUFFER_SIZE);

    printk("New message from userspace - count:%d\n",count);

    int retval = copy_from_user(tx_buffer,buf,count);

    printk("copy_from_user returned (%d) we read [%s]\n",retval , tx_buffer);
    printk("initialize rx buffer..\n");

    memcpy(rx_buffer,tx_buffer, count);
    printk("content of rx buffer [%s]\n", rx_buffer);

    actual_rx_size = count;

    return count; //inform that we read all (fixme?)
}

//Always successfull
int memory_open(struct inode *inode, struct file *filp) { return 0; }
int memory_release(struct inode *inode, struct file *filp) { return 0; }
And the userspace application is simple as well:

Code:
#include <unistd.h>     //open, close | always first, defines compliance
#include <fcntl.h>      //O_RDONLY
#include <stdio.h>
#include <stdlib.h>     //printf
#include <string.h>

int main(int args, char *argv[])
{
int BUFFER_SIZE = 20;

char internal_buf[BUFFER_SIZE];
int to_read = 0;

memset(internal_buf,0,BUFFER_SIZE);

if (args < 3) {
    printf("2 Input arguments needed\nTo read 10 bytes: \"%s read 10\" \
    \nTo write string \"hello\": \"%s write hello\"\nExiting..\n", argv[0], argv[0]);
    return 1;
}


//Check the operation
if (strcmp(argv[1],"write") == 0) {

    printf("input lenght:%d", strlen(argv[2]));
    //Make sure our write fits to the internal buffer
    if(strlen(argv[2]) >= BUFFER_SIZE) {
        printf("too long input string, max buffer[%d]\nExiting..", BUFFER_SIZE);
        return 2;
    }

    printf("write op\n");
    memcpy(internal_buf,argv[2], strlen(argv[2]));

    printf("Writing [%s]\n", internal_buf);

    FILE * filepointer;
    filepointer = fopen("/dev/move_data", "w");
    fwrite(internal_buf, sizeof(char) , strlen(argv[2]), filepointer);
    fclose(filepointer);

} else if (strcmp(argv[1],"read") == 0) {
    printf("read op\n");

    to_read = atoi(argv[2]);

    FILE * filepointer;
    filepointer = fopen("/dev/move_data", "r");
    int retval = fread(internal_buf, sizeof(char) , to_read, filepointer);
    fclose(filepointer);

    printf("Read %d bytes from driver string[%s]\n", retval, internal_buf);
} else {
    printf("first argument has to be 'read' or 'write'\nExiting..\n");
    return 1;
}


return 0;
}
When I execute my application, this is what happens:

Code:
./rw write "testing testing"

kernel side:
[ 2696.607586] New message from userspace - count:15
[ 2696.607591] copy_from_user returned (0) we read [testing testing]
[ 2696.607593] initialize rx buffer..
[ 2696.607594] content of rx buffer [testing testing]
So all look correct. But when I try to read:

Code:
./rw read 15
read op
Read 0 bytes from driver string[]

kernel side: 
[  617.096521] user requesting data, our buffer has (15) 
[  575.797668] copy_to_user returned (0)
[  617.096528] copy_to_user returned (0)
I guess it's quite simple what I'm doing wrong, since if I don't return 0 (retval) from memory_read, I can get some data back, but for example if I read with cat, it will continue looping endlessly.

For example, if I make this kind of change:

Code:
ssize_t memory_read {
...
    //return retval;
    return 5;
}

And in userspace I read:

./rw read 20
read op
Read 20 bytes from driver string[testitestitestitesti�]
So the driver will keep looping the first 5 chars. I guess this is because it thinks that kernel still has 5 more bytes left to read ? Or I'm not sure, this is where I'm confused. According to all instructions I should return return value from copy_to_user.. and 0 indicates succesfull transfer.

I would like to understand what mistakes I have made in my thinking. Is there a way that kernel driver would just spit out it's buffer, and then return 0, so that I wouldn't have to build some protocol there in between to take care of how much data has been read etc.
Thanks!
 
Old 06-09-2013, 11:48 PM   #2
bsat
Member
 
Registered: Feb 2009
Posts: 347

Rep: Reputation: 72
In the read function change the return to

Code:
     return actual_rx_size;
The kernel read function should return the amount of data that was read, transferred to user space. The copy_to_user returns "0" on successful transfer.
If a read function returns 0, it indicates that the end of file has been reached and there is no more data to copy. Thus if 0 is returned to user space it will understand that no data was copied to user space and thus nothing will be visible in the user space.
 
1 members found this post helpful.
Old 06-11-2013, 09:37 PM   #3
wedz
LQ Newbie
 
Registered: Jun 2008
Posts: 12

Original Poster
Rep: Reputation: 0
Yes, you are right. After starting to use actual_rx_size to keep track what has been sent to user space, the driver now works. Thanks!
 
  


Reply

Tags
char, driver, kernel, kernel module



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Sending data from kernel to user space ratbat Linux - Newbie 3 04-02-2013 02:33 AM
how to know which user space process is writing to SD card? sanrio alvares Linux - Newbie 2 09-07-2012 01:03 AM
Linux kernel copy_to_user to user space display different result dennisdd Programming 7 04-12-2012 04:15 PM
what is the difference between kernel space driver and user space driver? boidi Linux - Newbie 1 07-23-2010 12:35 AM
Sending packet from Kernel to User Space kaustubh_pict Linux - Networking 0 03-06-2006 07:52 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software > Linux - Kernel

All times are GMT -5. The time now is 12:57 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration