(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) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) /* used to create numbers */ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) #define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) #define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) /* used to decode ioctl numbers.. */ #define _IOC_DIR(nr) (((nr) >> _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 <linux/init.h> #include <linux/module.h> #include <linux/fs.h> // chrdev #include <linux/cdev.h> // cdev_add()/cdev_del() #include <linux/semaphore.h> // up()/down_interruptible() #include <asm/uaccess.h> // 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 < 0) { printk("can't alloc chrdev\n"); return ret; } dev_major = MAJOR(dev); dev_minor = MINOR(dev); printk("register chrdev(%d,%d)\n", dev_major, dev_minor); dev_cdevp = kmalloc(sizeof(struct cdev), GFP_KERNEL); if (dev_cdevp == NULL) { printk("kmalloc failed\n"); goto failed; } cdev_init(dev_cdevp, &dev_fops); dev_cdevp->owner = THIS_MODULE; ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1); if (ret < 0) { printk("add chr dev failed\n"); goto failed; } return 0; failed: if (dev_cdevp) { kfree(dev_cdevp); dev_cdevp = NULL; } return 0; } static void __exit exit_modules(void) { dev_t dev; dev = MKDEV(dev_major, dev_minor); if (dev_cdevp) { cdev_del(dev_cdevp); kfree(dev_cdevp); } unregister_chrdev_region(dev, 1); printk("unregister chrdev\n"); } module_init(init_modules); module_exit(exit_modules);在dev_ioctl()先檢視command的type(magic number)和number(sequential number)是否正確,接著在根據command的read/write特性,使用access_ok()檢驗該位址是否合法,後面就是ioctl慣有的switch/case了,根據不同的case執行不同的command和解釋後面所攜帶的參數。
底下是我的application:
#include <stdio.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include "ioc_brook.h" int main(int argc, char *argv[]) { int fd, ret; if (argc < 2) { printf("Usage: prog在app.c中,將開啟上面註冊的device,並且設定數值(BROOK_IOCSETNUM),讀取數值(BROOK_IOCGETNUM),和交換數值(BROOK_IOCXNUM)。\n"); return -1; } fd = open(argv[1], O_RDWR); if (fd < 0) { printf("open %s failed\n", argv[1]); return -1; } ret = 10; if (ioctl(fd, BROOK_IOCSETNUM, &ret) < 0) { printf("set num failed\n"); return -1; } if (ioctl(fd, BROOK_IOCGETNUM, &ret) < 0) { printf("get num failed\n"); return -1; } printf("get value = %d\n", ret); ret = 100; if (ioctl(fd, BROOK_IOCXNUM, &ret) < 0) { printf("exchange num failed\n"); return -1; } printf("get value = %d\n", ret); return 0; }
關於ioctl有些更正,先post參考文件供大家參考
回覆刪除The new way of ioctl()
kill .ioctl file_operation
也就是說kernel 2.6.36之後就沒有ioctl這個file operation了
Removing the big kernel lock. A big deal?
回覆刪除