close
原文出自:http://nano-chicken.blogspot.tw/2009/12/linux-modulesv-ioctl.html
(V)將介紹file operations中的ioctl。ioctl的prototype為:
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
ioctl藉由cmd來判斷後面所接的參數為何,而早期的ioctl號碼並沒有規則,所以很容易重複,後來為了避免重複,採行編碼方式,將cmd拆成幾個部份,包含:
type:
即magic number,可以根據Document/ioctl/ioctl-number.txt挑選一個。
number:
為sequential number或者稱為ordinal number,讓user自行定義,只要自己不重複即可。
direction:
傳輸的方向,不外乎NONOE/READ/WRITE等等。
size:
即參數的size。
因為ioctl藉由cmd來判斷user想要的指令為何,以及後面所帶的參數為何,所以免不了的就會有一個switch/case來判斷,這也算是ioctl的特色吧。
怎麼定義ioctl的command以及如何解譯ioctl的command,我想直接拿ioctl.h來說明。
#define _IOC(dir,type,nr,size) \ (((dir) > _IOC_DIRSHIFT) & _IOC_DIRMASK) #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)在定義ioctl的command時,我們會根據資料傳輸的方向使用_IO(不需要傳輸資料)/_IOR(讀取)/_IOW(寫入)/_IOWR(讀寫),type是我們挑選的magic number,nr即number,是流水號,size,就是the size of argument,下面是我們的範例brook_ioctl.h:
#ifndef IOC_BROOK_H #define IOC_BROOK_H #define BROOK_IOC_MAGIC 'k' #define BROOK_IOCSETNUM _IOW(BROOK_IOC_MAGIC, 1, int) #define BROOK_IOCGETNUM _IOR(BROOK_IOC_MAGIC, 2, int) #define BROOK_IOCXNUM _IOWR(BROOK_IOC_MAGIC, 3, int) #define BROOK_IOC_MAXNR 3 #endif這邊定義三個ioctl的command,分別為設定數值(BROOK_IOCSETNUM),取得數值(BROOK_IOCGETNUM)和交換數值(BROOK_IOCXNUM)。
以下是我的module:
#include#include #include // chrdev #include // cdev_add()/cdev_del() #include // up()/down_interruptible() #include // copy_*_user() #include "ioc_brook.h" MODULE_LICENSE("GPL"); #define DEV_BUFSIZE 1024 static int dev_major; static int dev_minor; struct cdev *dev_cdevp = NULL; static int dev_open(struct inode*, struct file*); static int dev_release(struct inode*, struct file*); static int dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long); static void __exit exit_modules(void); struct file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, .release = dev_release, .ioctl = dev_ioctl }; static int dev_open(struct inode *inode, struct file *filp) { printk("%s():\n", __FUNCTION__); return 0; } static int dev_release(struct inode *inode, struct file *filp) { printk("%s():\n", __FUNCTION__); return 0; } static int brook_num = 0; static int dev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args) { int tmp, err = 0, ret = 0; if (_IOC_TYPE(cmd) != BROOK_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > BROOK_IOC_MAXNR) return -ENOTTY; if (_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd)); } else if (_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ, (void __user *)args, _IOC_SIZE(cmd)); } if (err) return -EFAULT; switch (cmd) { case BROOK_IOCSETNUM: // don't need call access_ok() again. using __get_user(). ret = __get_user(brook_num, (int __user *)args); printk("%s(): get val = %d\n", __FUNCTION__, brook_num); break; case BROOK_IOCGETNUM: ret = __put_user(brook_num, (int __user *)args); printk("%s(): set val to %d\n", __FUNCTION__, brook_num); break; case BROOK_IOCXNUM: tmp = brook_num; ret = __get_user(brook_num, (int __user *)args); if (!ret) { ret = __put_user(tmp, (int __user *)args); } printk("%s(): change val from %d to %d\n", __FUNCTION__, tmp, brook_num); break; default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return 0; } static int __init init_modules(void) { dev_t dev; int ret; ret = alloc_chrdev_region(&dev, 0, 1, "brook"); if (ret owner = THIS_MODULE; ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1); if (ret 在dev_ioctl()先檢視command的type(magic number)和number(sequential number)是否正確,接著在根據command的read/write特性,使用access_ok()檢驗該位址是否合法,後面就是ioctl慣有的switch/case了,根據不同的case執行不同的command和解釋後面所攜帶的參數。
底下是我的application:#include#include #include #include #include #include "ioc_brook.h" int main(int argc, char *argv[]) { int fd, ret; if (argc 在app.c中,將開啟上面註冊的device,並且設定數值(BROOK_IOCSETNUM),讀取數值(BROOK_IOCGETNUM),和交換數值(BROOK_IOCXNUM)。
全站熱搜