Copy_to_user returns a zeroed out string in a scull like driver
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Copy_to_user returns a zeroed out string in a scull like driver
Hello everyone, I am trying to write a scull like dummy device driver. I have written it almost exactly like in the book but I haven't enabled semaphore locking and IOCTL calls. The problem I am facing is that the write method seems to work fine, but the read method returns a zeroed out string. Copy_to_user seems to return this zeroed out buffer, but if I use printk to find out what I have in the quantum, then I can see that it has the data written to it by copy_from_user.
Here is the code from readmemorydriver.c :
Code:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include "readmemorydriver.h"
#include <linux/kernel.h>
#include <asm/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("0.1");
dev_t devno;
unsigned int count = 5;
unsigned int major = SCULL_MAJOR;
unsigned int firstminor=0;
unsigned char *name = "readmemory";
unsigned int scull_nr_devs = 1;
struct scull_dev *scull_devices;
unsigned int scull_quantum = SCULL_QUANTUM;
unsigned int scull_qset = SCULL_QSET;
loff_t scull_llseek(struct file *filp, loff_t off, int whence){
return 0;
}
int scull_release(struct inode *inode, struct file *filep){
return 0;
}
int scull_ioctl(struct inode *inode, struct file *filp, unsigned int icmd, unsigned long arg){
return 0;
}
struct struct_qset *scull_follow(struct scull_dev *scull_dev, int qsetnum){
int i;
if (!scull_dev)
return NULL;
struct struct_qset *struct_qset = scull_dev->quantum_data; //find 0th
printk(KERN_ALERT "FOLLOW: qsetnum = %d, struct_qset = %u \n", qsetnum, struct_qset );
if(!struct_qset){
struct_qset = scull_dev->quantum_data = (struct struct_qset*) kmalloc(sizeof(struct struct_qset),GFP_KERNEL);
memset(struct_qset, 0, sizeof(struct struct_qset));
return struct_qset;
}
//element this way
for(i = 1; i <= qsetnum; i++){ //qsetnum is the position of the
//wanted qset element - get the element at this position
if(!struct_qset->next){
struct_qset->next = (struct struct_qset*) kmalloc(sizeof(struct struct_qset),GFP_KERNEL);
memset(struct_qset->next, 0, sizeof(struct struct_qset));
return struct_qset->next;
}
struct_qset = struct_qset->next;
}
return struct_qset;
}
int scull_trim(struct scull_dev *dev){
int j;
if (!dev) //B diagnostics, remove later
return;
struct struct_qset *next, *qsetptr = dev->quantum_data;
if (!qsetptr)
return 0; //B don't know what I should return
printk(KERN_ALERT "Value of qsetptr = %u\n", qsetptr);
char **data;
while(qsetptr ){
data = qsetptr->data;
if(data){
for(j=0;j<scull_qset;j++){
if(data[j])
kfree(data[j]);
// data[j] = NULL; //B should I really do this or is it not necessary
}
kfree(data);
// data = NULL;
}
next = qsetptr->next;
kfree(qsetptr);
qsetptr = next;
}
dev->size=0;
dev->quantum_data = NULL;
return 0;
}
int scull_open(struct inode *inode, struct file *filp){
struct scull_dev *scull_dev;
scull_dev = container_of(inode->i_cdev, struct scull_dev,cdev);
if((filp->f_flags & O_ACCMODE)==O_WRONLY){
scull_trim(scull_dev);
}
filp->private_data = scull_dev;
return 0;
}
ssize_t scull_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos){
struct scull_dev *scull_dev = filep->private_data;
struct struct_qset *struct_qset;
int size, pos;
int qsetnum, qsetpos;
int quantumnum, quantumpos;
int retval = -ENOMEM;
size = scull_dev->size;
pos = *f_pos;
qsetnum = size/scull_qset;
qsetpos = size%scull_qset;
quantumnum = qsetpos/scull_quantum;
quantumpos = qsetpos%scull_quantum;
struct_qset = scull_follow(scull_dev, qsetnum);
if(!struct_qset)
goto out;
if(!struct_qset->data)
struct_qset->data = kmalloc(scull_qset*(sizeof(void*)),GFP_KERNEL);
if(!struct_qset->data)
goto out;
memset(struct_qset->data, 0, scull_qset*(sizeof(void*)));
if(!struct_qset->data[quantumnum])
struct_qset->data[quantumnum] = kmalloc(scull_quantum*(sizeof(char)),GFP_KERNEL);
if(!struct_qset->data[quantumnum])
goto out;
memset(struct_qset->data[quantumnum], 0, scull_quantum*(sizeof(char)));
if (count > scull_quantum - quantumpos)
count = scull_quantum - quantumpos;
printk(KERN_ALERT "WRITE: Value of pos = %d, quantumnum = %u, struct_qset->data[quantumnum] = %u, size=%d, count=%d,buf = %s \n",pos, quantumnum, struct_qset->data[quantumnum], size, count, buf);
if(copy_from_user(struct_qset->data[quantumnum]+quantumpos, buf, count)){
retval = -EFAULT;
goto out;
}
printk(KERN_ALERT "WRITE: quantum = %s\n", struct_qset->data[quantumnum]);
*f_pos = pos + count;
if (size < *f_pos )
scull_dev->size = pos + count;
return count;
out:
return retval;
}
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos){
struct scull_dev *scull_dev = filp->private_data;
struct struct_qset *struct_qset ;
int size, pos = *fpos;
int retval = 0;
int qsetnum, qsetpos;
int quantumnum, quantumpos;
size = scull_dev->size;
pos = *fpos;
if (size <= pos)
goto out;
if (count > size - pos)
count = size - pos;
qsetnum = size/scull_qset;
qsetpos = size%scull_qset;
quantumnum = qsetpos/scull_quantum;
quantumpos = qsetpos%scull_quantum;
struct_qset = scull_follow(scull_dev, qsetnum);
if(count>scull_quantum - quantumpos)
count = scull_quantum - quantumpos;
if(!struct_qset||!struct_qset->data||!struct_qset->data[quantumnum]){
goto out;
}
printk(KERN_ALERT "READ: Value of pos = %d, quantumnum = %u, struct_qset->data[quantumnum] = %u, size=%d, count = %d, buf = %s\n",pos, quantumnum, struct_qset->data[quantumnum], size, count, buf);
if(copy_to_user(buf, struct_qset->data[quantumnum]+quantumpos, count)){
retval = -EFAULT;
goto out;
}
printk(KERN_ALERT "READ: Value of pos = %d, quantumnum = %u, struct_qset->data[quantumnum] = %u, size=%d, count = %d, buf = %s\n",pos, quantumnum, struct_qset->data[quantumnum], size, count, buf);
printk(KERN_ALERT "READ: quantum = %s", struct_qset->data[quantumnum]);
*fpos = pos + count;
return count;
out:
return retval;
}
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.open = scull_open,
.llseek = scull_llseek,
.write = scull_write,
.read = scull_read,
.release = scull_release,
.ioctl = scull_ioctl,
};
static void scull_setup_cdev(struct scull_dev *dev, int index){
int err, devno = MKDEV(major, firstminor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
void read_memory_exit(){
int i;
if(scull_devices){
for (i = 0; i < scull_nr_devs; i++){
cdev_del(&scull_devices[i].cdev);
scull_trim(&scull_devices[i]);
}
kfree(scull_devices);
unregister_chrdev_region(devno,count);
}
}
int __init read_memory_init(){
// read_memory;
/* int major = 12;
int minor = 0;
devno = MKDEV(major, minor);
*/
int i, result;
if(!major){
result = alloc_chrdev_region(&devno, 0,count, "readmemory" );
major = MAJOR(devno);
} else {
result = register_chrdev_region(devno, count,"readmemory");
}
if(result < 0){
printk(KERN_ALERT "error in allocating device");
return result;
}
scull_devices = kmalloc(scull_nr_devs*sizeof(struct scull_dev), GFP_KERNEL);
if(!scull_devices){
result = -ENOMEM;
goto fail;
}
memset(scull_devices, 0, scull_nr_devs*sizeof(struct scull_dev));
for (i = 0; i < scull_nr_devs;i++){
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
// printk(KERN_ALERT "VALUE OF quantum_data is %u", scull_devices[i].quantum_data);
scull_setup_cdev(&scull_devices[i], i);
}
return 0;
fail:
read_memory_exit();
return result;
}
/*read_memory(){
int *krnlprt = 0x0c000001;
return *krnlprt;
}
*/
module_init(read_memory_init);
module_exit(read_memory_exit);
Here is the readmemorydriver.h file :
Code:
#ifndef READMEMORYDRIVER_H
#define READMEMORYDRIVER_H
#ifndef CDEV_H
#define CDEV_H
#include <linux/cdev.h>
#endif
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0
#endif
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif
#ifndef SCULL_QSET
#define SCULL_QSET 1000
#endif
int scull_open (struct inode*, struct file*);
loff_t scull_llseek (struct file*, loff_t, int);
ssize_t scull_write (struct file*, const char __user*, size_t, loff_t*);
ssize_t scull_read (struct file*, char __user*, size_t, loff_t*);
int scull_release (struct inode*, struct file*);
int scull_ioctl (struct inode*, struct file*, unsigned int, unsigned long);
struct struct_qset{
char **data;
struct struct_qset *next;
};
struct scull_dev {
struct struct_qset *quantum_data;
int quantum;
int qset;
unsigned long size;
/*sem and accesskey to be used later as the driver goes further */
struct semaphore sem;
struct cdev cdev;
};
#endif
I am not able to understand why copy_to_user is not able to fetch my data. Please help me with it as it will help me in avoiding this problem in future.
thanks in advance
Dhruwat
Last edited by unitedroad; 12-24-2008 at 10:04 AM.
Reason: adding code for readmemorydriver.h
Hi everyone, it was a problem with my code hwere I was taking the offest in the qset data structure. I divided size by quantum*qset whereas I should have used the *f_pos as the dividend.
It is now working fine.
Thanks
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.