obayhan obayhan - 16 days ago 4
C Question

Can't read from linux character device

I am implementing an SPI driver for an accelerometer. The SPI part is done but I can't read values from userspace.

static char charDevMessage[CD_BUFFER_SIZE] = { 0 };
...
static ssize_t char_dev_read(struct file *filep, char *buffer, size_t len, loff_t *position)
{

int error_count = 0;
struct xyz_values xyz;
size_t size_requested;

xyz = adxl345_get_xyz();
memset(charDevMessage, 0, CD_BUFFER_SIZE);
sprintf(charDevMessage, "%d,%d,%d", xyz.x, xyz.y, xyz.z);
printk(KERN_INFO "MOB: %s, requested size: %d\n", charDevMessage, len);

if (len >= CD_BUFFER_SIZE)
{
size_requested = CD_BUFFER_SIZE;
}
else
{
size_requested = len;
}

error_count = copy_to_user(buffer, charDevMessage, size_requested);

if (error_count == 0)
{
printk(KERN_INFO "MOB: Sent %d characters to the user\n", size_requested);
return (size_requested = 0);

}
else
{
printk(KERN_INFO "MOB: Failed to send %d characters to the user\n", error_count);
return -EFAULT;

}
}


The node has been created when I installed the driver. But when I tried
cat
or tried to read by python it returns an empty string.

dmesg
says it has been sent to userspace successfully:


[ 3094.495972] MOB: SPI Character device has been opened 1 time(s)
[ 3094.506075] MOB: -349,-512,511 , requested size:49
[ 3094.514487] MOB: Sent 256 characters to the user
[ 3094.522646] MOB: Character device successfully closed
[ 3120.658568] MOB: SPI Character device has been opened 2 time(s)
[ 3120.668609] MOB: 0,0,0 , requested size:48
[ 3120.676392] MOB: Sent 256 characters to the user
[ 3120.684740] MOB: Character device successfully closed



What am I doing wrong?

Answer

You misaunderstood the concept of the .read function:

The reader (user space) sees only value returned by the .read and interpret it as a number of bytes which has been read.

As for the last parameter to the .read (position in your case), its interpretation is fully up to the driver's author. Value, pointed by position, is initialized to 0 by the kernel core when the file is opened. After that, kernel itself never modifies it.

If you want to always read from the beginning, you may just ignore position parameter:

return size_requested;

Or, semantically better, you may increment value, pointed by the position, so it will reflect total number of bytes read. But otherwise ignore it:

*position += size_requested;
return size_requested;