implementing ioctl's for device drivers

Apr 29, 2005 3 Replies

hi,



thanks for the help.. im inserting the code ive used for the simple ioctl.



//chardev.c /*all necessary header files included*/



#include chardev.h /*header file for ioctl*/



static int Device_open=0; static char Message[80]; static char *msg_ptr; static int device_open(struct inode *inode, struct file *file) { if(Device_open) return -EBUSY; Device_open++; MOD_INC_USE_COUNT; return 0; }



static void device_release(struct inode *inode, struct file *file) { Device_open--; MOD_DEC_USE_COUNT; return 0; }



int device_ioctl( struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { int i; char *temp; temp=(char *)ioctl_param; get_user(i,temp); printk(KERN_ALERT" the value passed from user space is %d\n",i); }


struct file_operations Fops = { .open=device_open, .release=device_release, .ioctl=device_ioctl }; // then ive written the init & cleanup functions..//



now the user space program //ioctl.c



//included fcntl.h,unistd.h,sys/ioctl.h & chardev.h



ioctl_set_length( int file_desc, int *pt) { int ret_val = ioctl(file_desc, IOCTL_SET_LENGTH, pt); if(ret_val < 0) { printf("ioctl_set_length failed"); exit(-1) } }



main() { int file_desc, ret_val; int pass; int *p; p= &pass; printf("enter val to be passed"); scanf("%d",&pass); file_desc=open("chardev",0); if(file_desc


hy sapna, here is a working ioctl example with copy_to_user():

// our magic number for our IOCTLs #define FBUS_IOC_MAGIC 'L' // the LOWEST nr we define here, 0x20 #define FBUS_IOC_OFFSET 0x20 // the REAL maximum nr we have defined. absolute maximum is 0x3f #define FBUS_IOC_MAXNR 0x21 // here we define our IOCTLs, Numbers Range from 0x20-0x21: #define FBUS_IOCTL_CPLD_W _IOW(FBUS_IOC_MAGIC, 0x20, data_t *) #define FBUS_IOCTL_CPLD_R _IOR(FBUS_IOC_MAGIC, 0x21, data_t *)

#define EPRINT(args...) do { if (PRINTERRORS) { \ printk(KERN_INFO DEVNAME ": " args); \ } \ } while(0);

static int fbus_ioctl(struct inode *ip, struct file *filp, unsigned int cmd, unsigned long data) { // this variable is needed for the IOCTLs which exchange data with the chip fbus_t *bus = filp->private_data;

// plausibilittschecks: if (_IOC_TYPE(cmd) != FBUS_IOC_MAGIC) return -EINVAL; if (_IOC_NR(cmd) < FBUS_IOC_OFFSET) return -EINVAL; if (_IOC_NR(cmd) > FBUS_IOC_MAXNR) return -EINVAL;

switch(cmd) { case FBUS_IOCTL_CPLD_W: { // write data from userspace to here data_t lb; copy_from_user(&lb, (data_t *)data, sizeof(data_t)); /* * now your data is here in the kernelspace variable lb. use it: */ break; } case FBUS_IOCTL_CPLD_R: { // copy data from here to userspace data_t lb; /* * set the values in "lb", then copy it to userspace: */ copy_to_user((data_t *)data, &lb, sizeof(data_t)); break; } default: EPRINT("unknown IOCTL-nr. 0x%04x\n", _IOC_NR(cmd)); return -EINVAL; }

return 0; }

  1. You are not checking the ioctl_num. This is OK for experimenting but you should definitely check it later on and do the operation only if it has the expeced value (IOCTL_SET_LENGTH).
  2. You should check the result value of get_user(). It returns -EFAULT, if you passed an invalid pointer.
  3. Take a look at the get_user() implementation (taken from uaccess.h):

--------- #define get_user(x,ptr) \ ({ int __ret_gu,__val_gu; \ switch(sizeof (*(ptr))) { \ case 1: __get_user_x(1,__ret_gu,__val_gu,ptr); break; \ case 2: __get_user_x(2,__ret_gu,__val_gu,ptr); break; \ case 4: __get_user_x(4,__ret_gu,__val_gu,ptr); break; \ default: __get_user_x(X,__ret_gu,__val_gu,ptr); break; \ } \ (x) = (__typeof__(*(ptr)))__val_gu; \ __ret_gu; \ })

----------

As you can see, get_user() is a macro. The size of the operand to be copied is determined from the sizeof(*ptr). Your pointer ("temp") is declared as a char* (why? you are copying an integer, so it should be an int*), so this sizeof() will evaluate to a 1 and so only the first byte of the parameter will be copied. This is probably not what you wanted.

You are passing an integer (a 32-bit value), and you do this by passing its address (another 32-bit value). This works, but it is kind of copious (would you do it this way if you passed the value to another function rather than to a driver?) Passing the integer the driver directly would have been easier and would avoid all the get_user() business in the driver.

HTH

Rob

Robert Kaiser email: rkaiser AT sysgo DOT com SYSGO AG http://www.elinos.com Klein-Winternheim / Germany http://www.sysgo.com

oops ! typing errors... temp is typecast as an int pointer not char pointer as typed. also, the message & msg_ptr are not required. they r actually parts of a bigger code.. not reqd now.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required