5

Così sto lavorando alla programmazione del driver del kernel, e attualmente sto cercando di creare un semplice trasferimento di dati tra l'applicazione e il driver del kernel.legge ok dallo spazio utente, ma la scrittura è sempre 0

Sto usando un semplice dispositivo di carattere come collegamento tra questi due, e sono riuscito a trasferire i dati al driver, ma non riesco a restituire dati significativi allo spazio dell'utente.

conducente del kernel simile a questa:

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

E l'applicazione userspace è semplice così:

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

Quando eseguo la mia domanda, questo è ciò che accade:

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

Quindi tutto sembra corretto. Ma quando provo a leggere:

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

Credo che sia abbastanza semplice quello che sto facendo male, dal momento che se non ritorno 0, posso ottenere alcuni dati indietro, ma per esempio se ho letto con il gatto , continuerà a scorrere all'infinito.

Mi piacerebbe capire quali errori ho commesso nel mio modo di pensare. C'è un modo in cui il driver del kernel dovrebbe solo sputare il suo buffer, e quindi restituire 0, in modo da non dover costruire qualche protocollo in mezzo per occuparmi di quanti dati sono stati letti ecc.

Grazie per i vostri suggerimenti!

Edit: corretto la dichiarazione printk nella funzione memory_write, e ha aggiunto la funzione di traccia memory_read

risposta

6

tua funzione di lettura restituisce sempre 0, perché si sta tornando retval, e non il conteggio di byte letti. Finché la chiamata copy_to_user() riesce sempre, retval sarà sempre uguale a 0. Invece, fino a quando copy_to_user() ha esito positivo, è necessario restituire il numero di byte effettivamente scritto nello spazio utente. This documentation indica che copy_to_user() restituisce il numero totale di byte che non è stato in grado di copiare.

Per inciso, si ignora il valore del conteggio. È molto probabile che l'utente richieda meno dati di quelli disponibili nel proprio buffer. Non si dovrebbe mai ignorare il conteggio.

Ora avete il problema in cui la funzione mai restituisce un 0. restituisce un 0 è importante perché a dire racconta l'applicazione utente che non ci sono più dati disponibili per la lettura e l'applicazione utente dovrebbe chiudere il file del dispositivo.

È necessario tenere traccia nel driver quanti byte sono stati letti rispetto a quanti byte sono stati scritti. Questo può essere implementato usando il tuo actual_rx_size.

Prova questo:

//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, avevi ragione sui soldi. Tracciare ciò che è stato inviato è stata la chiave per questo. Ottima soluzione, e funziona esattamente come speravo! Grazie! Ps. manca l'ultima istruzione printk ");". – julumme

+0

Oops, mi dispiace per quello. Non ho un sistema di sviluppo a casa quindi non ho avuto la possibilità di compilarlo.Risolto ora. –

+0

Grazie @ BenjaminLeinweber..I stava avendo problemi con la scrittura function.Returning 0 inviati ad un ciclo infinito somehow.Now so perché :) – SteveIrwin

Problemi correlati