注:用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道 // GREE_ON BLUE_ON
功能:input output 的控制
user(应用层):
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);(RED_ON)
(让点灯的代码变得简洁)
参数:
@fd : 打开文件产生的文件描述符
@request: 请求码(读写|第三个参数传递的字节的个数),
:在sys/ioctl.h中有这个请求码的定义方式。
@... :可写、可不写,如果要写,写一个内存的地址
--------------------------------------------------------
Kernel(内核层):
(在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情;)
fops:
long (*unlocked_ioctl) (struct file *file, unsigned int request, unsigned long args);
对于使用ioctl函数时,主要的就是请求码的设计,请求码主要在sys/ioctl.h文件里面进行了设计。
表示我本次是读还是写的字节的大小;再往下看当调用_IOC的时候怎样把四个域组合在一起的。
一个一个看,鼠标放在_IOC_DIRSHIFT,进行跳转,出现下面的同学
#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 RDE_LED _IO(type,nr)
这些宏是帮助你完成请求码的封装的。#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
dir << 30 | size<<16 | type << 8 | nr << 0
2 14 8 8
方向 大小 类型 序号
(方向:00 01 10 11读写相关,)
(大小:sizeof(变量名))
(类型:组合成一个唯一的不重合的整数,一般传一个字符)
(序号:表示同类型中的第几个,当开灯的时候写0,那关的时候就不写0)。
#define RLED_ON _IOWR('a',0,int)//亮灯
#define RLED_OFF _IOWR('a',1,int) //灭灯
内核中已经使用的命令码的域在如下文档中已经声明了。
vi kernel-3.4.39/Documentation/ioctl$ vi ioctl-number.txt
(2^32次方 = 4G的数字,所以可以使用,内核的想法是:每一个数字代表一个,功能和数字一一对应,但是不一样的驱动使用的时候相同也是可以的)
ioctl控制
头函数
#ifndef __HEAD_H__
#define __HEAD_H__
#include <asm-generic/ioctl.h>
#define RED_ON _IO('A',0)
#define RED_OFF _IO('A',1)
#define GREE_ON _IO('A',2)
#define GREE_OFF _IO('A',3)
#define BLUE_ON _IO('A',4)
#define BLUE_OFF _IO('A',5)
#endif
应用层
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include "head.h"
int main(int argc,char *argv[])
{
int fd = open("/dev/led",O_RDWR);
if(fd < 0)
{
printf("open %s failed\n","led");
return 2;
}
while(1)
{
ioctl(fd,RED_ON);
sleep(1);
ioctl(fd,RED_OFF);
sleep(1);
ioctl(fd,GREE_ON);
sleep(1);
ioctl(fd,GREE_OFF);
sleep(1);
ioctl(fd,BLUE_ON);
sleep(1);
ioctl(fd,BLUE_OFF);
sleep(1);
}
close(fd);
return 0;
}
内核层
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include "head.h"
#define NAME "chrdev_dev"
//定义宏保存物理地址基地址
#define RED_BASE 0xc001a000
#define GRE_BASE 0xc001e000
#define BLU_BASE 0xc001b000
//定义指针保存映射后的虚拟地址首地址
unsigned int *red_addr=NULL;
unsigned int *gre_addr=NULL;
unsigned int *blu_addr=NULL;
struct class *cls=NULL;
struct device *dev=NULL;
int major = 0;
char kbuf[32];
char kbuf_r[32]="welcome to hqyj!";
//open read write close
int myopen(struct inode *node, struct file *file_t)
{
printk("%s %s %d\n", __FILE__, __func__, __LINE__);
return 0;
}
long myioctl(struct file *file_t,unsigned int request,unsigned long data)
{
printk("%s %s %d\n",__FILE__, __func__, __LINE__);
switch(request)
{
case RED_ON:
*red_addr |= (1<<28);
break;
case RED_OFF:
*red_addr &= (~(1<<28));
break;
case GREE_ON:
*gre_addr |= (1<<13);
break;
case GREE_OFF:
*gre_addr &= (~(1<<13));
break;
case BLUE_ON:
*blu_addr |= (1<<12);
break;
case BLUE_OFF:
*blu_addr &= (~(1<<12));
break;
}
}
int myclose(struct inode *node, struct file *file_t)
{
printk("%s %s %d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = myopen,
.unlocked_ioctl = myioctl,
.release = myclose,
};
static int __init chrdev_init(void)
{
printk("%s %s %d\n", __FILE__, __func__, __LINE__);
//注册字符设备驱动
major=register_chrdev(major, NAME, &fops);
if(major < 0)
{
printk("register_chrdev err.\n");
return -EINVAL ;
}
//建立虚拟地址和物理地址之间的映射关系-控制红灯
red_addr=(unsigned int *)ioremap(RED_BASE,40);
if(red_addr == NULL)
{
printk("ioremap red err.\n");
return -EINVAL ;
}
//初始化红灯
*(red_addr+9) &= (~(3<<24));//选择GPIOA28功能
//*(red_addr+9) =*(red_addr+9)& (~(3<<24));
*(red_addr+1) |= (1<<28); //选择输出使能
*red_addr &= (~(1<<28)); //红灯关闭
//////////////////////////////////////////////////////////////
gre_addr=(unsigned int *)ioremap(GRE_BASE,40);
if(gre_addr == NULL)
{
printk("ioremap gred err.\n");
return -EINVAL ;
}
//初始化绿灯
*(gre_addr+8) &= (~(3<<26));//选择GPIOA28功能
*(gre_addr+1) |= (1<<13); //选择输出使能
*gre_addr &= (~(1<<13)); //红灯关闭
///////////////////////////////////////////////////////////
blu_addr=(unsigned int *)ioremap(BLU_BASE,40);
if(blu_addr == NULL)
{
printk("ioremap blu err.\n");
return -EINVAL ;
}
//初始化蓝灯
*(blu_addr+8) &= (~(0<<24));//选择GPIOA28功能
*(blu_addr+8) |= (1<<25);//选择GPIOA28功能
*(blu_addr+1) |= (1<<12); //选择输出使能
*blu_addr &= (~(1<<12)); //红灯关闭
// 自动创建设备节点
// 1.提交目录信息
cls = class_create(THIS_MODULE,NAME);
if (IS_ERR(cls))
{
printk("class creat err\n");
return -EINVAL ;
}
// 提交文件信息
dev=device_create(cls,NULL,MKDEV(major,0),NULL,"led");
if (IS_ERR(dev))
{
printk("device creat err\n");
return -EINVAL ;
}
return 0;
}
static void __exit chrdev_exit(void)
{
printk("%s %s %d\n", __FILE__, __func__, __LINE__);
device_destroy(cls,MKDEV(major,0));
class_destroy(cls);
//取消映射
iounmap(red_addr);
iounmap(gre_addr);
iounmap(blu_addr);
//注销字符设备驱动
unregister_chrdev(major,NAME);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");