5

私はカーネルドライバのプログラミングを進めています。現在、アプリケーションとカーネルドライバの間で簡単なデータ転送を構築しようとしています。カーネルドライバはユーザー空間からOKを読み込んでいますが、書き戻しは常に0です

私はこれら2つの間のリンクとしてシンプルキャラクタデバイスを使用していますが、ドライバにデータを転送するのに成功しましたが、ユーザー空間に意味のあるデータを戻すことができません。

カーネルドライバは次のようになります。

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

そして、ユーザ空間のアプリケーションが同様に簡単です:

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

私は自分のアプリケーションを実行すると、これは何が起こるかです:

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

だから、すべて正しいように見える。私は読んしようとすると、しかし:

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

Kernel 
[ 617.096521] user requesting data, our buffer has (15) 
[ 575.797668] copy_to_user returned (0) 
[ 617.096528] copy_to_user returned (0) 

を私は0が返されない場合は、私は戻っていくつかのデータを取得することができるので、それは、私が間違ってやっている非常に簡単だと思うが、例えば、私は猫を読めば、それは無限にループし続けます。

私は自分の考えに間違いがあったことを理解したいと思います。 カーネルドライバがバッファを吐き出してから0を返すという方法はありますか。そのため、読み込まれたデータの量などを考慮してプロトコルを構築する必要はありません。

あなたの提案をありがとう!

編集:memory_write関数内のprintk文を修正し、あなたが読み込まれたバイト数をretvalを返す、とされていないため、memory_read関数トレース

答えて

6

あなたのread関数は常に0を返しを追加しました。 copy_to_user()呼び出しが常に成功する限り、retvalは常に0になります。代わりに、copy_to_user()が成功する限り、実際にユーザー空間に書き込まれたバイト数を返す必要があります。 This documentationは、copy_to_user()がコピーできなかった合計バイト数を返すことを示します。

脇に、カウントの値を無視しています。ユーザーがあなたのバッファーよりも少ないデータを要求している可能性は非常に高いです。カウントを無視してはいけません。

ここでは、関数は決してに0を返します。ユーザーアプリケーションには読み込み可能なデータがなくなり、ユーザーアプリケーションはデバイスファイルを閉じる必要があるため、0を返すことが重要です。

ドライバに書き込まれたバイト数と書き込まれたバイト数を追跡​​する必要があります。これは、actual_rx_sizeを使用して実装できます。

はこれを試してみてください:

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

    ssize_t bytes; 

    if (actual_rx_size < count) 
     bytes = actual_rx_size; 
    else 
     bytes = count; 

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

    /* Check to see if there is data to transfer */ 
    if (bytes == 0) 
     return 0; 

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

    if (retval) { 
     printk("copy_to_user() could not copy %d bytes.\n", retval); 
     return -EFAULT; 
    } else { 
     printk("copy_to_user() succeeded!\n"); 
     actual_rx_size -= bytes; 
     return bytes; 
    } 
} 
+0

ベンジャミン、あなたはお金で正しかったです。送信されたものを追跡することがこれの鍵でした。素晴らしいソリューションと、私が望んだとおりに正確に動作します!ありがとう! Ps。最後のprintkステートメントがありません ");" – julumme

+0

おっと、申し訳ありません。私は自宅に開発システムを持っていないので、コンパイルする機会はありませんでした。今修正されました。 –

+0

Thanks @ BenjaminLeinweber ..私はwrite関数に問題がありました。0を返すと、無限ループに何とか送信されました。なぜ今は知っていますか? – SteveIrwin

関連する問題