Showing posts with label Embedded System. Show all posts
Showing posts with label Embedded System. Show all posts
0

kernel driver reading ok from user space, but writing back is 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:
#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:
#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:
./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:
./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)
I guess it's quite simple what I'm doing wrong, since if I don't return 0, I can get some data back, but for example if I read with cat, it will continue looping endlessly.
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 for your suggestions!
Edit: corrected the printk statement in memory_write function, and added memory_read function trace

Answers:-

Your read function always returns 0 because you are returning retval, and not the count of bytes read. As long as the copy_to_user() call always succeeds, retval will always be 0. Instead, as long as copy_to_user() succeeds, you should return the number of bytes actually written to user space. This documentation states that copy_to_user() returns the total number of bytes that it was unable to copy.
As an aside, you are ignoring the value of count. It is very possible that the user is requesting less data than you have available in your buffer. You should never ignore count.
Now you have the problem where your function never returns a 0. Returning a 0 is important because is tells the user application that there is no more data available for reading and the user application should close the device file.
You need to keep track in your driver how many bytes have been read vs. how many bytes have been written. This may be implemented using your actual_rx_size.
Try this:
//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;
    }
}
Read
0

Questions about register_chrdev_region() in linux device driver

I'm learning about the registration of a kernel module using register_chrdev_region(dev_t from, unsigned count, const char * name);.
I notice that with or without this function, my kernel module worked as expected. The code I used for testing:
first = MKDEV(MAJOR_NUM, MINOR_NUM);
register_chrdev_region(first, count, DEVICE_NAME);//<---with and without

mycdev=cdev_alloc();
mycdev->ops= &fops;
mycdev->owner = THIS_MODULE;

if (cdev_add(mycdev,first, count) == 0)
{printk(KERN_ALERT "driver loaded\n");}
I commented out the line register_chrdev_region(first, count, DEVICE_NAME);, and theprintk message still appeared. I tried to communicate with the driver with or without this from user space, and both are successful.
So my question is, is this function register_chrdev_region() only used to make my driver a good kernel citizen, just like telling the others that "I'm using up the major number, please don't use"?
I tried to have a look in the kernel source char_dev.c to understand the function, but I find it too difficult to understand, anyone that's familiar with this?

Answers:-

That will work because it's not actually necessary to allocate your device numbers up front. In fact, it's considered preferable by many kernel developers to use the dynamic (on-the-fly, as-needed) allocation function alloc_chrdev_region.
Whether you do it statically up front or dynamically as needed, it is something you should do to avoid conflict with other device drivers which may have played by the rules and been allocated the numbers you're trying to use. Even if your driver works perfectly well without it, that won't necessarily be true on every machine or at any time in the future.
The rules are there for a reason and, especially with low-level stuff, you are well advised to follow them.
See here for more details on the set-up process.
Read
0

Finding original MAC address from Hardware itself

Is this possible to read MAC address form NIC directly ? I have below code but it just read from above layer but not the card itself !!!
I'm trying to figure out how to find the original MAC address of an ethernet NIC on my linux box. I understand how to find the current MAC address using ifconfig, but if the address has been changed, say by using 'ifconfig eth0 hw ether uu:vv:ww:yy:xx:zz',or I set "permanent" it using vi /etc/sysconfig/network-scripts/ifcfg-eth0.this file...I can successfully UP it in REBOOT also. how do I find the original? There must be a way to find it, because it is still burned permanently into the card, but I can't find a tool to read the burned in address. is there any utility for it or command for it? I suppose to write C code for it. but don't know how to do this in above case.
** below code gives my current MAC but not original MAC
#include <stdio.h>              /* Standard I/O */
#include <stdlib.h>             /* Standard Library */
#include <errno.h>              /* Error number and related */


#define ENUMS
#include <sys/socket.h>
#include <net/route.h>
#include <net/if.h>
#include <features.h>           /* for the glibc version number */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>       /* the L2 protocols */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>     /* The L2 protocols */
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <netdb.h>


int main( int argc, char * argv[] ){

unsigned char  mac[IFHWADDRLEN];
int i;
    get_local_hwaddr( argv[1], mac );
    for( i = 0; i < IFHWADDRLEN; i++ ){
        printf( "%02X:", (unsigned int)(mac[i]) );
    }

}


int get_local_hwaddr(const char *ifname, unsigned char *mac)
{
    struct ifreq ifr;
    int fd;
    int rv;                     // return value - error value from df or ioctl call

    /* determine the local MAC address */
    strcpy(ifr.ifr_name, ifname);
    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (fd < 0)
        rv = fd;
    else {
        rv = ioctl(fd, SIOCGIFHWADDR, &ifr);
        if (rv >= 0)            /* worked okay */
            memcpy(mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
    }

    return rv;
}

Answers;-

Well, the old ethernet address remains in the first bytes of the card eeprom (at least for some types of cards), so it is possible to extract it using ethtool
bash$ sudo ethtool -e eth1
Offset      Values
------      ------
0x0000      tt uu ww xx yy zz 79 03 
0x....
where tt:uu:ww:xx:yy:zz is old mac address
Read
0

how to find if unregister_chrdev() call was successful

The return of void was done by changeset: e53252d97e670a38b1d2e9723b48077bba11ddda
commit e53252d97e670a38b1d2e9723b48077bba11ddda
Author: Akinobu Mita 
Date:   Thu Jul 19 01:47:51 2007 -0700

    unregister_chrdev() return void

    unregister_chrdev() does not return meaningful value.  This patch makes it
    return void like most unregister_* functions.
In fact if you look at the code before (from 2.6.12-rc2 onwards) you can see it always returned 0 so this is a function that is always expected to succeed.
Read
0

How do I determine if a connected USB device is a USB flash drive?

how do you determine what kind of media has been attached to the system?
I have Ubuntu, and when I inserted an SD-card, it notices that it is in fact an SD card. Same counts for USB sticks.
But how can I determine on low level when a new device is inserted, what kind of type it is?
There seems to be no information to be found on this at all.
edit: just to be more complete: I said it is a Linux environment, but actually it is Android in an Embedded environment. I tagged it Linux because I am indeed trying to check from command line.
The udevadm command is not available, and lsusb -vv shows:
Bus 001 Device 001: ID 1d6b:0002
Bus 001 Device 002: ID 0424:2640
Bus 001 Device 003: ID 0424:4040
Bus 002 Device 001: ID 1d6b:0001

Answers:-

It may be useful to run such a command:
$ udevadm info -a -p $(udevadm info -q path -n /dev/sdX)
The output may looks like follows:
[...]
  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.5':
    KERNELS=="1-1.5"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bmAttributes}=="80"
    ATTRS{bMaxPower}=="200mA"
    ATTRS{urbnum}=="6519"
    ATTRS{idVendor}=="13fe"
    ATTRS{idProduct}=="1d00"
    ATTRS{bcdDevice}=="0100"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{speed}=="480"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="3"
    ATTRS{devpath}=="1.5"
    ATTRS{version}==" 2.00"
    ATTRS{maxchild}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{authorized}=="1"
    ATTRS{manufacturer}=="Kingston"
    ATTRS{product}=="DataTraveler 2.0"
    ATTRS{serial}=="5B7A08A1010F"
[...]
You can see some ATTRS that describes the device.
Read
0

How to get linux kernel page size programatically

I am working on a Linux module for IA64. My current problem is that the driver uses the PAGE_SIZE and PAGE_SHIFT macros for dma page allocation. The problem I am having is that the machine compiling the driver is not the ones that needed to run the driver. So, if the PAGE_SIZE on the compiling machine is 2^14K and the destination machine is 2^16K then the driver fails.
I don't want to turn this question into a 'best practice' issue about compiling modules on machines which are not the ones running the modules. I understand the issues about that. What I found is that people mostly uses getpagesize() or sysconf(_SC_PAGE_SIZE). These two options are out of the ia64 kernel headers so I can't use them. Is there another way that I could get the runtime PAGE_SIZE?
Options I am looking at:
  • Reading some file in /proc?
  • syscall?
  • Other function that let me calculate the PAGE_SIZE by inference (e.g ORDER, getpageshift, etc)?
  • Other??
Answers;-

If you are trying to build a kernel module, you will need to have at least the kernel headers that are configured for the kernel the module will run on. Those will define the page size macros you need. If you don't have the correctly configured headers, the kernel will refuse to load your module.
And there is nothing wrong with compiling a module on one machine to run on another, even if it's a different architecture. You just need to build against the correct kernel source.
Read
0

mapping memory reserved by mmap kernel boot param into user space

As discussed in this question, i am reserving a memory chunk at the boot time using a kernel boot parameter memmap=8G$64G
I have written a character driver kernel module which , during initialization does a ioremap of this reserved memory chunk. As explained here , in my driver mmap all i need to do is remap_pfn_rangefor this memory chunk pointer returned by the ioremap.
I am running this on 3.0 linux kernel. My user space application opens this memory chunk as a device mounted by the driver. When i do mmap from the use space application i see a system hang. mydmesg don't provide me much information.
Any inputs ?
static int __init myDev_module_init(void)
{
   int retval;

   myDev_major = register_chrdev(0, DEVICE_NAME, &myDevfops);
   if (myDev_major < 0) 
   {
       err("failed to register device: error %d\n", myDev_major);
       retval = myDev_major;
       goto FAILED_CHRDEVREG;
   }

   myDev_class = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(myDev_class)) 
   {   
       err("failed to register device class '%s'\n", CLASS_NAME);
       retval = PTR_ERR(myDev_class);
       goto FAILED_CLASSREG;
   }


   myDev_device = device_create(myDev_class, NULL, MKDEV(myDev_major, 0), NULL, CLASS_NAME "_" DEVICE_NAME);
   if (IS_ERR(myDev_device)) 
   {
       err("failed to create device '%s_%s'\n", CLASS_NAME, DEVICE_NAME);
       retval = PTR_ERR(myDev_device);
       goto FAILED_DEVREG;
   }
here the myDev.startOffset is initialized to #defined 64GB and myDev.memSize to 8GB.
 //myDev.startAddr = ioremap(myDev.startOffset,myDev.memSize);

 //memset_io(myDev.startAddr, 0, myDev.memSize);  
 return 0;

  FAILED_DEVREG:
   class_unregister(myDev_class);
   class_destroy(myDev_class);
  FAILED_CLASSREG:
   unregister_chrdev(myDev_major, DEVICE_NAME);
  FAILED_CHRDEVREG:
   return -1;
}

static int myDev_device_open(struct inode* inode, struct file* filp)
{
    dbg("");

    if ( ((filp->f_flags & O_ACCMODE) == O_WRONLY) || ((filp->f_flags & O_ACCMODE) == O_RDWR) ) 
    {
        warn(" Opening the device with write access\n");
        //return -EACCES;
    }

    info(" device Open is called\n");
    filp->private_data = &myDev;
    return 0;
 }
And the mmap is pretty straight forward.
static int myDev_device_mmap(struct file * f, struct vm_area_struct * vma)
{
 int retval = 0;
struct myDevDev * pDev = (struct myDevDev *)(f->private_data);

dbg("");
if(vma)
{
    if(f)
    {
        if(f->private_data)
            warn("mmap: f->private_data  : %p\n", f->private_data);
        else
            warn(" mmap :f->private_data  : NULL \n");
    }
    else
    {
        warn("mmap: f  :NULL\n");
    }
    warn(": mmap: vm start : %lu\n", vma->vm_start);
    warn(" mmap: vm end  : %lu\n", vma->vm_end);
    warn(" mmap: vm pg offset  : %lu\n", vma->vm_pgoff);



    //retval = remap_pfn_range(vma, vma->vm_start, pDev->startOffset >> PAGE_SHIFT,    pDev->memSize, PAGE_SHARED) ;
    // retval = remap_pfn_range(vma, vma->vm_start, pDev->startAddr >> PAGE_SHIFT,    pDev->memSize, PAGE_SHARED) ;
    //retval = remap_pfn_range(vma,pDev->startAddr ,pDev->startOffset >> PAGE_SHIFT,  pDev->memSize, PAGE_SHARED);
    retval = remap_pfn_range(vma,vma->vm_start ,pDev->startOffset >> PAGE_SHIFT,  pDev->memSize, PAGE_SHARED);
    if(retval <0)
    {
        warn(" ERROR : in mapping kernel virtual space to user space return value : %d \n",retval);
        return -EINVAL;
    }

    //if(0)
    {
        vma->vm_flags |=VM_LOCKED;
        vma->vm_ops = &myRemapVMOps;
        vma->vm_flags |= VM_RESERVED;

        vma->vm_private_data = f->private_data;
        myDevice_VMA_Open(vma);
    }
}
else
{
    warn ("vma is NULL");
}

dbg(" Done ");
warn("mmpaing done : \n");

return 0;
}
from my user space application i am doing the following :
int err, i=0;
void * mptr = NULL;
printf(" Access the reserved memory chunk \n  ");
int fd = open("/dev/myDevice", O_RDWR | O_SYNC);

if(fd <=0)
{
    printf("ERROR: my device driver is not loaded \n");
    return 1;
}

  printf("\n mmaping mem chunk size :%llu pagesize :%lu input mptr :%p\n", sz,getpagesize (), mptr);

  mptr = mmap(0, sz , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);

if(mptr == MAP_FAILED) 
{
    close(fd);
    perror("Error mmapping the file");
    printf("\nmmapped mem address %p\n",mptr);
    exit(1);
}
printf("\nmmapped mem address %p\n",mptr);

//char * ptr = (char *)mptr;
//*ptr = 'a';

//int * pInt =  (int *) (((char *) mptr)+1); 
//for(;i<10000; ++i)
{
  //  pInt[i] = 2*i;
}

 /* free the mmapped memory
 */
if (munmap(mptr, sz) == -1) 
{
    perror("Error un-mmapping the file");
}

close(fd);

Answers;-

  1. You can use standard phram driver in steps to access your memory from userspace. No coding is needed. Kernel recompilation at maximum.
  2. Do You really have more then 64Gb of RAM? Does Your hardware really support it?
Read