高级字符设备驱动-Ioctl设备控制笔记

用户使用方法

在用户空间,使用ioctl系统调用来控制设备,原型如下:

intioctl(intfd,unsingedlongcmd,...)

fd-文件描述符

cmd-对设备的发出的控制命令

...表示这是一个可选的参数,存在与否依赖于cmd

驱动使用方法

原型:

int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg)

*inode-物理上的设备信息,如设备号

*filp-对应是打开的文件的信息,如读写位置

cmd-是用户空间传下来的对设备的发出的控制命令

arg-若用户使用时第三个参数没有,这个参数没有任何意义

如何实现ioctl方法?

步骤:

1)定义命令

Include/asm/ioctl.h中定义了这些位字段:类型、序号、传送方向、参数大小

Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了的类型。

Type类型:表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽

Number序号:表明设备命令中的第几个,8位宽

Direction传送方向:可能的值是

_IOC_NONE(没数据传输)

_IOC_READ(从设备读)

_IOC_WRITE

Size用户参数大小:13/14位宽,视处理器而定)

内核提供了一些宏来帮助定义命令:

_IO(type,nr)nr为序号,datatype为数据类型如int

没有参数的命令

_IOR(type,nr,datatype)

从驱动中读数据

_IOW(type,nr,datatype)

写数据到驱动

_IOWR(type,nr,datatype)

双向传送,typenumber成员作为参数传递

定义命令例子#defineMEM_IOC_MAGIC'm'//定义类型

#defineMEM_IOCSET

_IOW(MEM_IOC_MAGIC,0,int)

#defineMEM_IOCGQSET

_IOR(MEM_IOC_MAGIC,1,int)

2)实现命令

定义好了命令,下一步就是要实现ioctl函数了

1)返回值

2)参数使用

3)命令操作

Ioctl函数实现(返回值)

Ioctl函数根据命令执行的一个switch语句

但是当命令不能匹配任何一个命令时,通常返回-EINVAL(非法参数)

Ioctl函数实现(参数)

用户使用intioctl(intfd,unsingedlongcmd,...)时,...就是要传递的参数

再通过int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg)

中的arg传递

如果arg是一个整形,可以直接使用,如果是指针,我们必须确保这个用户地址是有效的,

因此,使用之前需要进行正解的检查。

Ioctl函数实现(参数检查)

内部有检查的,不需要检测的:

Copy_from_user

Copy_to_user

Get_user

Put_user

需要检测的:

__get_user

__put_user

使用intaccess_ok(inttype,constvoid*addr,unsignedlongsize)检测

Type是VERIFY_READ或者VERIFY_WRITE用来表明是读用户内存还是写用户内存。

Addr参数是要操作的用户内存地址,size是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数就等于sizeof(int)

Access_ok返回一个布尔值:1,是成功(存取没问题)

0,是失败,ioctr返回-EFAULT

Ioctl函数实现(命令操作)

Switch(cmd)

{

Case

}

驱动程序代码: /* 定义幻数 */ #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 /*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; }


驱动程序编译:

把驱动程序s3c2440_leds.c文件放到内核drivers/char子目录下,在drivers/char/Makefile中增加一句:

Obj-m+=s3c2440_leds.o

然后在内核根目录下执行“makemodules”,就可以生成模块drivers/char/s3c2440_leds.ko

把它放到开发板根文件系统的/lib/modules/2.6.30.2/目录下,就可以使用insmods3c2440_ledsrmmods3c2440_leds命令进行加载、卸载了。

测试程序代码: #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; }


编译测试程序:

在所在目录Make上面的程序,生成可执行程序led_test,把它放到开发板/usr/bin目录下后。

打开设备文件前要创建设备文件:

#mknod设备文件名设备类型主设备号次设备号

#mknod/dev/ledsc2340

使用驱动程序:

根据led_test测试代码说明使用!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值