ioctl控制設備讀寫數據以及關閉等。node
用戶空間函數原型:int ioctl(int fd,unsinged long cmd,...)linux
fd-文件描述符函數
cmd-對設備的發出的控制命令測試
...表示這是一個可選的參數,存在與否依賴 於cmd,如cmd爲修改波特率,那麼....就表示波特率的值。若是cmd表示關閉,則不須要參數spa
內核函數原型指針
file_operations結構體裏面long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);code
ioctl命令段分爲:類型,序號,傳遞方向,參數大小orm
Type 類型:代表哪一個設備的命令,在參考了ioctl-number.txt以後選出,8位寬內存
Number 序號:代表設備命令中的第幾個,8位寬get
Direction 傳送方向:可能的值是
_IOC_NONE(沒數據傳輸)
_IOC_READ(從設備讀)
_IOC_WRITE
Size 用戶參數大小:(13/14位寬,視處理器而定)
內核提供了一些宏來幫助定義命令:
_IO(type, nr ) type爲命令類型 如int, nr爲序號
沒有參數的命令
_IOR(type, nr, datatype)
從驅動中讀數據,datatype 讀的數據參數的類型
_IOW(type, nr, datatype)
寫數據到驅動
_IOWR(type,nr, datatype)
讀寫數據
定義命令例子 #define MEM_IOC_MAGIC 'm' //定義幻數,由於命令類型type是8位的因此找個字符代替
#define MEM_IOCSET
_IOW(MEM_IOC_MAGIC,0,int) //命令 去寫 一個 int型的數據
#define MEM_IOCGQSET
_IOR(MEM_IOC_MAGIC, 1, int)
2,ioctl命令使用時注意事項
用戶使用int ioctl(int fd,unsinged long cmd,...)時,...就是要傳遞的參數
再經過long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long arg);中的arg傳遞,若是arg是一個整形,能夠直接使用,若是是指針,咱們必須確保這個用戶地址是有效的,所以,使用以前須要進行正解的檢查。
內部有檢查的,不須要檢測的:
Copy_from_user
Copy_to_user
Get_user
Put_user
須要檢測的:
__get_user
__put_user
使用int access_ok(int type, const void *addr, unsigned long size)檢測
Type 是VERIFY_READ 或者VERIFY_WRITE用來代表是讀用戶內存仍是寫用戶內存。
Addr參數是要操做的用戶內存地址,size是操做的長度。若是ioctl須要從用戶空間讀一個整數,那麼size參數就等於sizeof(int)
內核讀,寫入用戶空間,因此用戶空間使用VERIFY_WRITE寫驗證用戶內存是否可用。
Access_ok返回一個布爾值:1,是成功(存取沒問題),失敗,ioctr返回-EFAULT
3,ioctl實現例程
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include "memdev.h" static int mem_major = MEMDEV_MAJOR; module_param(mem_major, int, S_IRUGO); struct mem_dev *mem_devp; /*設備結構體指針*/ struct cdev cdev; /*文件打開函數*/ int mem_open(struct inode *inode, struct file *filp) { struct mem_dev *dev; /*獲取次設備號*/ int num = MINOR(inode->i_rdev); if (num >= MEMDEV_NR_DEVS) return -ENODEV; dev = &mem_devp[num]; /*將設備描述結構指針賦值給文件私有數據指針*/ filp->private_data = dev; return 0; } /*文件釋放函數*/ int mem_release(struct inode *inode, struct file *filp) { return 0; } /*IO操做*/ int memdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; int ret = 0; int ioarg = 0; /* 檢測命令的有效性 */ if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) return -EINVAL; if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) return -EINVAL; /* 根據命令類型,檢測參數空間是否能夠訪問 */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; /* 根據命令,執行相應的操做 */ switch(cmd) { /* 打印當前設備信息 */ case MEMDEV_IOCPRINT: printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n"); break; /* 獲取參數 */ case MEMDEV_IOCGETDATA: ioarg = 1101; ret = __put_user(ioarg, (int *)arg); break; /* 設置參數 */ case MEMDEV_IOCSETDATA: ret = __get_user(ioarg, (int *)arg); printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg); break; default: return -EINVAL; } return ret; } /*文件操做結構體*/ static const struct file_operations mem_fops = { .owner = THIS_MODULE, .open = mem_open, .release = mem_release, .ioctl = memdev_ioctl, }; /*設備驅動模塊加載函數*/ static int memdev_init(void) { int result; int i; dev_t devno = MKDEV(mem_major, 0); /* 靜態申請設備號*/ if (mem_major) result = register_chrdev_region(devno, 2, "memdev"); else /* 動態分配設備號 */ { result = alloc_chrdev_region(&devno, 0, 2, "memdev"); mem_major = MAJOR(devno); } if (result < 0) return result; /*初始化cdev結構*/ cdev_init(&cdev, &mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; /* 註冊字符設備 */ cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); /* 爲設備描述結構分配內存*/ mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL); if (!mem_devp) /*申請失敗*/ { result = - ENOMEM; goto fail_malloc; } memset(mem_devp, 0, sizeof(struct mem_dev)); /*爲設備分配內存*/ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devp[i].size = MEMDEV_SIZE; mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devp[i].data, 0, MEMDEV_SIZE); } return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } /*模塊卸載函數*/ static void memdev_exit(void) { cdev_del(&cdev); /*註銷設備*/ kfree(mem_devp); /*釋放設備結構體內存*/ unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*釋放設備號*/ } MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
.h
#ifndef _MEMDEV_H_ #define _MEMDEV_H_ #include <linux/ioctl.h> #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 190 /*預設的mem的主設備號*/ #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*設備數*/ #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem設備描述結構體*/ struct mem_dev { char *data; unsigned long size; }; /* 定義幻數 */ #define MEMDEV_IOC_MAGIC 'k' /* 定義命令 */ #define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1) #define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int) #define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int) #define MEMDEV_IOC_MAXNR 3 #endif /* _MEMDEV_H_ */
Makefile
ifneq ($(KERNELRELEASE),) obj-m := memdev.o else KDIR := /forlinux/linux-3.0.1 all: make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers modul* endif
測試程序
#include <stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include "memdev.h" /* 包含命令定義 */ int main() { int fd = 0; int cmd; int arg = 0; char Buf[4096]; /*打開設備文件*/ fd = open("/dev/memdev0",O_RDWR); if (fd < 0) { printf("Open Dev Mem0 Error!\n"); return -1; } /* 調用命令MEMDEV_IOCPRINT */ printf("<--- Call MEMDEV_IOCPRINT --->\n"); cmd = MEMDEV_IOCPRINT; if (ioctl(fd, cmd, &arg) < 0) { printf("Call cmd MEMDEV_IOCPRINT fail\n"); return -1; } /* 調用命令MEMDEV_IOCSETDATA */ printf("<--- Call MEMDEV_IOCSETDATA --->\n"); cmd = MEMDEV_IOCSETDATA; arg = 2007; if (ioctl(fd, cmd, &arg) < 0) { printf("Call cmd MEMDEV_IOCSETDATA fail\n"); return -1; } /* 調用命令MEMDEV_IOCGETDATA */ printf("<--- Call MEMDEV_IOCGETDATA --->\n"); cmd = MEMDEV_IOCGETDATA; if (ioctl(fd, cmd, &arg) < 0) { printf("Call cmd MEMDEV_IOCGETDATA fail\n"); return -1; } printf("<--- In User Space MEMDEV_IOCGETDATA Get Data is %d --->\n\n",arg); close(fd); return 0; }