5

So arbeite ich meinen Weg durch Kernel-Treiber-Programmierung, und derzeit versuche ich eine einfache Datenübertragung zwischen Anwendung und Kernel-Treiber zu bauen.Kernel-Treiber lesen ok aus Benutzerraum, aber immer wieder schreiben 0

Ich verwende ein einfaches Zeichen Gerät als eine Verbindung zwischen diesen beiden, und es ist mir gelungen, Daten an den Treiber zu übertragen, aber ich kann keine aussagekräftigen Daten zurück in den Benutzerraum bekommen.

Kernel-Treiber sieht wie folgt aus:

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

Und die User-Space-Anwendung ist ebenso einfach:

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

Wenn ich meine Anwendung ausführen, das ist, was passiert:

./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 sehen alle richtig aus. Aber wenn ich zu lesen versuchen:

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

Ich denke, es ist ganz einfach, was ich falsch mache, denn wenn ich 0 nicht zurück, ich einige Daten zurück bekommen kann, aber zum Beispiel, wenn ich mit Katze lesen Es wird endlos weiterlaufen.

Ich würde gerne verstehen, welche Fehler ich in meinem Denken gemacht habe. Gibt es eine Möglichkeit, dass Kernel-Treiber wäre es nur ausspucken Puffer, und dann 0 zurück, so dass ich nicht dort einige Protokoll bauen müsste zwischen kümmern, wie viele Daten gelesen wurde usw.

Danke für deine Vorschläge!

Edit: korrigiert die printk Anweisung in memory_write Funktion und hinzugefügt memory_read Funktion Spur

Antwort

6

Ihre Lesefunktion immer 0 zurück, weil Sie retval zurückkehren, und nicht die Anzahl der gelesenen Bytes. Solange der Aufruf copy_to_user() immer erfolgreich ist, wird retval immer 0 sein. Stattdessen, solange copy_to_user() erfolgreich ist, sollten Sie die Anzahl der tatsächlich in Benutzerbereich geschriebenen Bytes zurückgeben. This documentation gibt an, dass copy_to_user() die Gesamtzahl der Byte zurückgibt, die nicht kopiert werden konnten.

Nebenbei ignorieren Sie den Wert der Anzahl. Es ist sehr gut möglich, dass der Benutzer weniger Daten anfordert, als in Ihrem Puffer verfügbar sind. Sie sollten niemals zählen zählen.

Jetzt haben Sie das Problem, wo Ihre Funktion nie eine 0 zurückgibt. Rückgabe einer 0 ist wichtig, weil es der Benutzeranwendung mitteilt, dass keine Daten mehr zum Lesen verfügbar sind und die Benutzeranwendung die Gerätedatei schließen soll.

Sie müssen in Ihrem Treiber nachverfolgen, wie viele Bytes gelesen wurden und wie viele Bytes geschrieben wurden. Dies kann mit Ihrem actual_rx_size implementiert werden.

Versuchen Sie folgendes:

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

Benjamin, du hast Recht auf dem Geld. Das Tracking des Gesendeten war der Schlüssel dazu. Tolle Lösung und funktioniert genauso wie ich gehofft habe! Vielen Dank! Ps. die letzte printk-Anweisung fehlt ");". – julumme

+0

Hoppla, tut mir leid. Ich habe kein Entwicklungssystem zu Hause, also hatte ich keine Chance, es zu kompilieren.Jetzt behoben. –

+0

Danke @ BenjaminLeinweber..Ich hatte ein Problem mit der Funktion write.Returning 0 schickte es irgendwie zu einer Endlosschleife.Jetzt weiß ich warum :) – SteveIrwin