linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】
转自:http://my.oschina.net/u/274829/blog/285014
1,ioctl介绍
ioctl控制设备读写数据以及关闭等。
用户空间函数原型:int ioctl(int fd,unsinged long cmd,...)
fd-文件描述符
cmd-对设备的发出的控制命令
...表示这是一个可选的参数,存在与否依赖于cmd,如cmd为修改波特率,那么....就表示波特率的值。如果cmd表示关闭,则不需要参数
内核函数原型
file_operations结构体里面long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
ioctl命令段分为:类型,序号,传递方向,参数大小
Type 类型:表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽
Number 序号:表明设备命令中的第几个,8位宽
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实现例程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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 |
测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#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; }
1,ioctl介绍ioctl控制设备读写数据以及关闭等。 用户空间函数原型:int ioctl(int fd,unsinged long cmd,...)
内核函数原型 file_operations结构体里面long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); ioctl命令段分为:类型,序号,传递方向,参数大小
_IOC_NONE(没数据传输) _IOC_READ(从设备读) _IOC_WRITE
内核提供了一些宏来帮助定义命令:
从驱动中读数据,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实现例程
.h
Makefile
测试程序
|
linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】的更多相关文章
- 【转】Linux高级字符设备之Poll操作
原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275559.html 在用户程序中,select()和poll()也是与设备阻塞与非阻塞 ...
- Linux高级字符设备驱动
转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm 1.什么是Poll方法,功能是什么? 2.Select系统调用(功能) Select ...
- Linux高级字符设备驱动 poll方法(select多路监控原理与实现)
1.什么是Poll方法,功能是什么? 2.Select系统调用(功能) Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程. int selec ...
- linux字符设备驱动--基本知识介绍
一.设备驱动的分类 1.字符设备 字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯.键盘.鼠标等.字符设备一般需要在驱动层实现open().close().read().write().i ...
- Linux实现字符设备驱动的基础步骤
Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现. 1.先在rootfs中的 /dev/ 下生成一个字符设备.注意主设备号 和 从设备号 ...
- linux学习--字符设备驱动
目录 1.字符设备驱动抽象结构 2.设备号及设备节点 2.1 设备号分配与管理 2.2 设备节点的生成 3.打开设备文件 linux驱动有基本的接口进行注册和卸载,这里不再做详细说明,本文主要关注li ...
- linux driver ------ 字符设备驱动 之 “ 创建设备节点流程 ”
在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device ...
- Linux LED字符设备驱动
// 申请IO资源 int gpio_request(unsigned gpio, const char *label); // 释放IO资源 void gpio_free(unsigned gpio ...
- 字符设备驱动ioctl实现用户层内核层通信
测试代码实现 memdev.h #ifndef _MEMDEV_H_ #define _MEMDEV_H_ #include<linux/ioctl.h> #ifndef MEMDEV_M ...
随机推荐
- 05 Zabbix triggers--action--event
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 05 Zabbix triggers--action--event 动作action: 在配置好监 ...
- 洛谷 P2463 [SDOI2008]Sandy的卡片 解题报告
P2463 [SDOI2008]Sandy的卡片 题意 给\(n(\le 1000)\)串,定义两个串相等为"长度相同,且一个串每个数加某个数与另一个串完全相同",求所有串的最长公 ...
- httpd.yml实例
httpd.ymlapiVersion: apps/v1beta1kind: Deploymentmetadata: name: httpdspec: replicas: 4 template: me ...
- luogu3810 陌上花开 (cdq分治)
求三维偏序 设三维为a,b,c.先对a排序,这样i的偏序就只能<i. 然而排序的时候需要三个维度都判断一遍,最后还要去重,不然会出现实际应该记答案的数出现在它后面的情况. (排序用的函数里不要写 ...
- AtCoder Grand Contest 004 C - AND Grid
题意: 给出一张有紫色点的网格,构造一张红点网格和一张蓝点网格,使红蓝点的交集为紫色点. 保证网格四周没有紫色点. 构造一下,使蓝点和红点能够到每个点. #include<bits/stdc++ ...
- POJ 1459 Power Network / HIT 1228 Power Network / UVAlive 2760 Power Network / ZOJ 1734 Power Network / FZU 1161 (网络流,最大流)
POJ 1459 Power Network / HIT 1228 Power Network / UVAlive 2760 Power Network / ZOJ 1734 Power Networ ...
- [luogu2114][起床困难综合症]
luogu2114 思路 因为位运算对于每一位是独立的,所以对每一位都对这n个数进行操作,然后观察最后得出的是1还是0.并且保证每一位拼起来之后要比m小. 代码 #include<cstdio& ...
- SNP (Single Nucleotide Polymorphism), SNV ( single nucleotide variants ) , Indel (insertion-deletion) 的区别
SNP (Single Nucleotide Polymorphism):强调在一个群体中具有一定频率的变异,一般为二态性.比如G→C SNV ( single nucleotide variants ...
- 有意思的undefined columns selected,源于read.table和read.csv
输入以下语法时: read.table(site_file,header=T)->data data<-data[which(data[,5]=="ADD"),] 出现 ...
- HBase基础之常用过滤器hbase shell操作
创建表 create 'test1', 'lf', 'sf' lf: column family of LONG values (binary value) -- sf: column family ...