基于tiny4412开发板led灯字符设备ioctl驱动写法
来源:互联网 发布:海康网络视频服务器 编辑:程序博客网 时间:2024/05/21 18:07
ioctl接口作用
write:向设备写入数据,单独这个接口并不能满足现实设备控制的全部需求。例如一个LCD
控制器:主要作用是驱动lcd
屏,要显示就是通过write
接口把显示数据发给lcd
控制器指定的显存。而参数设置类参数通过write
接口设置就可能回和普通的显示数据弄混了。为了解决这个问题,内核提供一个接口ioctl
对设备进行控制(参数设置,参数查询等功能)。ioctl
主要实现不太好实现的功能。
ioctl系统调用接口
#include <sys/ioctl.h> int ioctl(int d, int request, ...);
功能:
给系统通过命令形式,控制硬件设备,相当于linux
系统给我们提供了系统扩展功能的一个接口,read`` write
等固定用法,而ioctl
可由用户自定义命令来执行不同代码。
参数:
d:文件描述符
request:命令(可以是系统命令,也可以是自定义的)
···:表示变参,相当于printf
参数一样,可以有,可以没有。是否需要和request
命令有关
示例说明可变参的用法:
1)0x10
表示开全部灯,
2)0x20
表示第N
个灯,
3)0x30
表示关第N
个灯,
4)0x40
表示关全部灯。
关闭、开启第几个灯N可以由可变参传入来决定
返回值:
>=0:成功,>0具体什么含义由驱动程序决定
-1:执行失败
ioctl接口驱动模板
文件操作结构体中的定义如下; long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
所以函数原型应该是: long xxx_unlocked_ioctl (struct file *pfile, unsigned int cmd, unsigned long args);
功能:
对应于系统调用API的ioctl函数int ioctl(int d, int request, ...);
参数对应关系:
pfile:文件结构指针,间接对应与用户空间ioctl函数的fd参数
cmd:直接对应于request
args:对应于可选参数···
返回值:
>=0:成功,>0具体什么含义由驱动程序决定
<1:执行失败 返回失败错误码,不一定是-1
,但是只要是错误码,在上层一定是-1
,这里的错误码提供给内核,比如内存不足,返回-EFAULT
,所返回的错误码会被储存在系统全局变量errno
中。
EFAULT:args 非法
EINVAL:参数无效
接口测试实验
写一个驱动代码实现unlocked_ioctl接口
设计思路:由于控制程序ioctl
函数中request
参数对应驱动程序cmd
参数,而应用程序通过传递不同的值来告诉驱动程序做不同的事情,所以,驱动中unlocked_ioctl
接口代码函数内部一定是要判断cmd
值执行不同的代码段,所以就是一个if
语句或者是一个switch
语句。完成对cmd
的判断。
标准ioctl接口命令的合成
ioctl命令规则
1.系统自定义命令:执行优先级高于用户自定义命令
2.用户自定义命令
ioctl命令执行:不直接就执行驱动中的unlocked_ioctl
接口,而是先根据cmd
情况是否属于预定义命令,如果是则去执行,完成后返回,返回后可能执行用户自定义的命令,也可能直接返回,不执行用户命令。
避免命令冲突:内核为解决这个问题,定义了一个规则,命令是特定格式组成的,
内核说明文档:
ioctl-decoding.txt \linux-3.5\Documentation\ioctl
ioctl-number.txt \linux-3.5\Documentation\ioctl
编码格式:
内核合成宏:
路径: ioctl.h \linux-3.5\include\asm-generic
参数:
type:表示命令组成的魔数,也就是8~15
位
nr:表示命令的编号,也就是0~7
位
size:b
表示命令组成参数传递的大小,但是这里传递的不是数字,而是数据类型
参数:
nr:命令
利用ioctl向驱动写数据
//例如用户空间有unsigned char buf[100];ioctl(fd,cmd,(unsigned long)buf);
long unlocked_ioctl(struct file *pfil, unsigned int cmd, unsigned long args){//这里args就是把用户空间传下来的buf的地址转换成数字//这里再把数字还原成指针copy_from_user(kbuf,(void*)args,4);//固定写4个字节,这样写不太规范实际应该从命令中获取数据数目}
利用ioctl从驱动读取数据
long unlocked_ioctl(struct file *pfil, unsigned int cmd, unsigned long args){//这里args就是把用户空间传下来的buf的地址转换成数字//这里再把数字还原成指针copy_to_user(kbuf,(void*)args,4);}
交互数据 先读取用户空间 再传给用户空间
long unlocked_ioctl(struct file *pfil, unsigned int cmd, unsigned long args){//这里args就是把用户空间传下来的buf的地址转换成数字//这里再把数字还原成指针copy_from_user(kbuf,(void*)args,4);//固定写4个字节,这样写不太规范实际应该从命令中获取数据数目······copy_to_user(kbuf,(void*)args,4);//固定写4个字节,这样写不太规范实际应该从命令中获取数据数目}
驱动接口函数接口函数示例
long leddriver_ioctl (struct file *pfile, unsigned int cmd, unsigned long args){ unsigned char LEN_NUM=4; int ret=0; int nr=0; switch(cmd) { case LED_ALL_ON: GPM4DAT &= ~(0xf<<0); break; case LED_ALL_OFF: GPM4DAT |= (0XF<<0); break; case LED_ON_N: case LED_OFF_N: ret=copy_from_user(&nr,(void *)args,_IOC_SIZE(cmd)); //数据拷贝失败 返回错误码 if(ret) { return -EFAULT; } //如果灯的标号大于等于4 返回错误码 if(nr>= LED_NUM) { return -EINVAL; } if(cmd == LED_ON_N) { GPM4DAT &= ~(1<<nr); }else{ GPM4DAT |= (1<<nr); } break; default: return -EINVAL; break; }}
完整代码
#include<linux/kernel.h>#include<linux/module.h>#include<linux/init.h>#include<asm/io.h>#include<asm/uaccess.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/kdev_t.h>#include<linux/slab.h>#include<linux/device.h> //增加自动创建设备头文件#include<linux/uaccess.h>#include "iocmd.h"//定义字符设备结构体static struct cdev *leddriver_cdev;//定义设备号(包含主次)static dev_t leddriver_num=0;//定义设备类static struct class *leddriver_class;//定义设备结构体static struct device *leddriver_device;//定义错误返回类型static int err;//定义设备名称#define LEDDRIVER_NAME "myled"#define GPM4CON_ADDR 0x110002E0 #define GPM4DAT_ADDR 0X110002E4static volatile unsigned long *gpm4con=NULL; static volatile unsigned long *gpm4dat=NULL;#define GPM4CON *gpm4con#define GPM4DAT *gpm4datssize_t leddriver_read(struct file *file, char __user *usr, size_t size, loff_t *loft){ loff_t cur_pos=*loft;//取出当前读写位置值 unsigned char led_statue[10],i,LED_NUM=4; //读取数据长度为0什么也不做 返回0 退出程序的执行 if(size<=0) { return 0; } //读取位置在末尾 无论size是多少都不能读出数据 数据有效区域越界 if(cur_pos>=LED_NUM) { return 0; } //判断size+当前位置是大于文件大小,只读取有效位的内容 if(cur_pos+size>LED_NUM) { size=LED_NUM-cur_pos; } for(i=0;i<LED_NUM;i++) { if(GPM4DAT &(1<<i)) led_statue[i]=1; else led_statue[i]=0; } if(copy_to_user(usr,&led_statue[cur_pos],size)){ printk("copy to user err\r\n"); return -EFAULT; }; //指针重新定位当前位置 *loft+=size; return size;}ssize_t leddriver_write (struct file *file, const char __user *usr, size_t size, loff_t *loft){ loff_t cur_pos=*loft; unsigned char led_statue[10],i,LED_NUM=4; //写入数据大小为0 什么也不操作返回0退出 if(size<=0) { return 0; } //当前位置大于等于文件最大有效数据,即使写入数据也是无效,所依不进行操作 返回0退出 if(cur_pos>=LED_NUM) { return 0; } //如果当前位置加上所要读取数据的长度大于剩余有效位 只读取有效数据位的数值 if(size+cur_pos>LED_NUM) { size=LED_NUM-cur_pos; } if(copy_from_user(&led_statue[cur_pos],usr,size)) { printk("copy from user err\r\n"); return -EFAULT; } for(i=0;i<size;i++) { if(led_statue[i+cur_pos]==0) GPM4DAT &= ~(1<<(i+cur_pos)); else GPM4DAT |= (1<<(i+cur_pos)); } *loft+=size; return size;}int leddriver_open (struct inode *node, struct file *pfile){ printk("files open is success\r\n"); return 0;}loff_t leddriver_llseek(struct file *pfile, loff_t loft, int whence){ loff_t tmp;unsigned char LED_NUM=4;switch(whence){case SEEK_SET: tmp=loft; break;case SEEK_CUR: tmp=pfile->f_pos+loft; //当前位置加上调整值 break;case SEEK_END: tmp=LED_NUM+loft; break;default:return -EINVAL;//告诉程序具体错误原因 参数无效break;}//检测最后的结果是否合法if(tmp<0 || tmp>LED_NUM){return -EINVAL;}//更新文件调整后的结果到文件结构体中pfile->f_pos=tmp;//返回调整后的结果return tmp;}int leddriver_release (struct inode *node, struct file *file){ printk("leddriver close is success\r\n"); return 0;}long leddriver_ioctl (struct file *pfile, unsigned int cmd, unsigned long args){ unsigned char LEN_NUM=4; int ret=0; int nr=0; switch(cmd) { case LED_ALL_ON: GPM4DAT &= ~(0xf<<0); break; case LED_ALL_OFF: GPM4DAT |= (0XF<<0); break; case LED_ON_N: case LED_OFF_N: ret=copy_from_user(&nr,(void *)args,_IOC_SIZE(cmd)); //数据拷贝失败 返回错误码 if(ret) { return -EFAULT; } //如果灯的标号大于等于4 返回错误码 if(nr>= LED_NUM) { return -EINVAL; } if(cmd == LED_ON_N) { GPM4DAT &= ~(1<<nr); }else{ GPM4DAT |= (1<<nr); } break; default: return -EINVAL; break; } return 0;}//文件操作函数结构体static struct file_operations leddriver_fops={ .owner=THIS_MODULE, .open=leddriver_open, .release=leddriver_release, .read=leddriver_read, .write=leddriver_write, .llseek=leddriver_llseek, unlocked_ioctl=leddriver_ioctl,};static __init int ldedriver_init(void){//分配字符设备结构体,前面只是定义没有分配空间leddriver_cdev=cdev_alloc();//判断分配成功与否if(leddriver_cdev==NULL){ err=-ENOMEM; printk("leddriver alloc is err\r\n"); goto err_leddriver_alloc;}//动态分配设备号err=alloc_chrdev_region(&leddriver_num, 0, 1, LEDDRIVER_NAME);//错误判断if(err<0){ printk("alloc leddriver num is err\r\n"); goto err_alloc_chrdev_region;}//初始化结构体cdev_init(leddriver_cdev,&leddriver_fops);//驱动注册err=cdev_add(leddriver_cdev,leddriver_num,1);if(err<0){ printk("cdev add is err\r\n"); goto err_cdev_add;}//创建设备类leddriver_class=class_create(THIS_MODULE,"led_class"); err=PTR_ERR(leddriver_class); if(IS_ERR(leddriver_class)) {printk("leddriver creat class is err\r\n");goto err_class_create; }//创建设备 leddriver_device=device_create(leddriver_class,NULL, leddriver_num,NULL, "leddevice"); err=PTR_ERR(leddriver_device); if(IS_ERR(leddriver_device)) {printk("leddriver device creat is err \r\n");goto err_device_create; }//led灯寄存器配置 gpm4con=ioremap(GPM4CON_ADDR, 4); gpm4dat=ioremap(GPM4DAT_ADDR, 4); GPM4CON &= ~(0XFFFF<<0); GPM4CON |= (0x1111<<0); GPM4DAT |= (0XF<<0);printk("leddriver init is success\r\n");return 0;err_device_create:class_destroy(leddriver_class);err_class_create: cdev_del(leddriver_cdev);err_cdev_add:unregister_chrdev_region(leddriver_num, 1);err_alloc_chrdev_region:kfree(leddriver_cdev);err_leddriver_alloc:return err;}static __exit void leddriver_exit(void){ //取消映射 iounmap(gpm4con); iounmap(gpm4dat); device_destroy(leddriver_class,leddriver_num); class_destroy(leddriver_class); cdev_del(leddriver_cdev); unregister_chrdev_region(leddriver_num, 1); printk("leddriver is exit\r\n");}module_init(ldedriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");
app函数
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include "iocmd.h"int main(int argc,char *argv[]){ int led_fd,i; led_fd = open(argv[1],O_RDWR); while(1) { ioctl(led_fd,LED_ALL_ON); sleep(2); for(i=0;i<4;i++) { ioctl(led_fd,LED_OFF_N,i); sleep(1); } for(i=0;i<4;i++) { ioctl(led_fd,LED_ON_N,i); sleep(1); } ioctl(led_fd,LED_ALL_OFF); sleep(2); } sleep(1); close(led_fp);}
公共头文件
#ifndef _IOCMD_H_#define _IOCMD_H_#define LED_ALL_ON _IO('L',0)#define LED_ALL_OFF _IO('L',1)#define LED_ON_N _IOW('L',2,int)#define LED_OFF_N _IOW('L',3,int)#endif //_IOCMD_H_
Makefile
KERN_DIR = /zhangchao/linux3.5/linux-3.5all: make -C $(KERN_DIR) M=`pwd` modulescp: cp ./* /zhangchao/rootfs/zhangchaoclean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.orderobj-m += led.o
- 基于tiny4412开发板led灯字符设备ioctl驱动写法
- tiny4412开发板LED灯驱动写法
- 基于tiny4412开发板LED灯驱动标准的read write函数写法
- 基于tiny4412开发板LED灯驱动标准的llseek函数写法
- 基于TQ2440的led字符设备驱动
- Tiny4412 字符设备驱动(一)
- Tiny4412开发板 LED灯的控制
- LED字符设备驱动
- led字符设备驱动
- 字符设备驱动---Led
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 从零开始写linux字符设备驱动程序(一)(基于友善之臂tiny4412开发板)
- 从零开始写linux字符设备驱动程序(二)(基于友善之臂tiny4412开发板)
- C/C++中RAND_MAX的用法
- 蓝桥杯练习——C++输出阶乘的最右边一位非零数
- C++DFS方法全排列
- 简单的Java网络爬虫(获取一个网页中的邮箱)
- Java Swing简单的加法器
- 基于tiny4412开发板led灯字符设备ioctl驱动写法
- C++求矩阵的鞍点
- 分布式入门:副本控制
- Matlab重新激活
- LeetCode (258):Add Digits
- java输出前50个素数
- 关于seo写作内容的一些探讨
- LeetCode (237):Delete Node in a Linked List
- 技术的热门度曲线