一、ioctl工作原理
1.前面通过read和write函数完成对设备的读写操作
2.在驱动中,有的时候需要完成对设备的控制
3.所以需要将设备的控制,与设备的读写分开
二、对应关系
应用层:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
函数功能:对设备的控制
参数:
fd:文件描述符
request:设备命令码
.....:可以传参,也可以不传参,如果传递需要传递地址
内核层:
long myled_ioctl (struct file *file, unsigned int cmd, unsigned long args);
三、解析命令码
四、命令码格式
五、封装命令码宏
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOC(dir,type,nr,size) \
((unsigned int) (((dir) << 30) | (size) << 16) | (type) << 8) | ((nr) << 0)
六、封装灯命令码
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
七、使用ioctl进行点灯
myled.h:
#ifndef __MYLED_H__ #define __MYLED_H__ enum led{ LED1, //0 LED2, //1 LED3, //2 }; typedef struct { volatile unsigned int MODER; // 0x00 volatile unsigned int OTYPER; // 0x04 volatile unsigned int OSPEEDR; // 0x08 volatile unsigned int PUPDR; // 0x0C volatile unsigned int IDR; // 0x10 volatile unsigned int ODR; // 0x14 volatile unsigned int BSRR; // 0x18 volatile unsigned int LCKR; // 0x1C volatile unsigned int AFRL; // 0x20 volatile unsigned int AFRH; // 0x24 volatile unsigned int BRR; // 0x28 volatile unsigned int res; volatile unsigned int SECCFGR; // 0x30 }gpio_t; #define GPIOE (0x50006000) //GPIOE组基地址 #define GPIOF (0x50007000) //GPIOF组基地址 #define RCC_MP_AHB4ENSETR 0x50000A28//RCC_MP_AHB4ENSETR寄存器地址 #define LED_ON _IOW('a',1,int) //封装灯亮命令码 #define LED_OFF _IOW('a',0,int) //封装灯灭命令码 #endif
demo.c:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include "myled.h" #define CNAME "myled" unsigned int major = 0; char kbuf[128] = {}; unsigned int* rcc_virt = NULL; //RCC虚拟地址 gpio_t* gpioe_virt = NULL; //gpioe组虚拟地址 gpio_t*gpiof_virt = NULL; //gpiof组虚拟地址 #define LED1_ON (gpioe_virt->ODR |= (0x1 << 10)) //LED1点亮 #define LED1_OFF (gpioe_virt->ODR &= (~(0x1 << 10))) //LED1熄灭 #define LED2_ON (gpiof_virt->ODR |= (0x1 << 10)) //LED2点亮 #define LED2_OFF (gpiof_virt->ODR &= (~(0x1 << 10))) //LED2熄灭 #define LED3_ON (gpioe_virt->ODR |= (0x1 << 8)) //LED3点亮 #define LED3_OFF (gpioe_virt->ODR &= (~(0x1 << 8))) //LED3熄灭 int myled_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args) { int ret; int whitch = 0; printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); switch(cmd) //解析命令码 { case LED_ON: //灯亮命令码 ret = copy_from_user(&whitch,(void*)args,sizeof(int)); //将用户空间数据,传递内核空间 if(ret){ printk("copy from user is error\n"); return -EIO; } switch(whitch) //操作哪一盏灯 { case LED1: //操作第一盏灯 LED1_ON; //LED1灯亮 break; case LED2: LED2_ON; break; case LED3: LED3_ON; break; } break; case LED_OFF: //灯灭命令码 ret = copy_from_user(&whitch,(void*)args,sizeof(int)); //将用户空间数据,传递内核空间 if(ret){ printk("copy from user is error\n"); return -EIO; } switch(whitch) //操作哪一盏灯 { case LED1: //操作第一盏灯 LED1_OFF; //LED1熄灭 break; case LED2: LED2_OFF; break; case LED3: LED3_OFF; break; } break; } return 0; } int myled_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } //操作方法结构体 const struct file_operations fops = { .open = myled_open, .unlocked_ioctl = myled_ioctl, .release = myled_close, }; //入口函数 static int __init demo_init(void) { //注册字符设备驱动 major = register_chrdev(0,CNAME,&fops); if(major < 0){ printk("register chrdev is error\n"); return -EIO; } printk("major = %d\n",major); //RCC_MP_AHB4ENSETR寄存器地址映射 rcc_virt = ioremap(RCC_MP_AHB4ENSETR,4); if(rcc_virt == NULL){ printk("rcc ioremap is error\n"); return -EIO; } //GPIOE组寄存器地址映射 gpioe_virt = ioremap(GPIOE,sizeof(gpio_t)); if(gpioe_virt == NULL){ printk("gpioe_virtr ioremap is error\n"); return -EIO; } //GPIOF组寄存器地址映射 gpiof_virt = ioremap(GPIOF,sizeof(gpio_t)); if(gpiof_virt == NULL){ printk("gpiof_virtr ioremap is error\n"); return -EIO; } //LED1初始化 //使能GPIOE组控制器 rcc_virt[4] = 1 *rcc_virt |= (0x1 << 4); //设置PE10引脚为输出模式 MODER[21:20] = 01 gpioe_virt->MODER &= (~(0x3 << 20)); gpioe_virt->MODER |= (0x1 << 20); //设置PE10引脚输出低电平,LED1熄灭 ODR[10] = 0 gpioe_virt->ODR &= (~(0x1 << 10)); //LED2初始化 //使能GPIOE组控制器 rcc_virt[5] = 1 *rcc_virt |= (0x1 << 5); //设置PF10引脚为输出模式 MODER[21:20] = 01 gpiof_virt->MODER &= (~(0x3 << 20)); gpiof_virt->MODER |= (0x1 << 20); //设置PF10引脚输出低电平,LED2熄灭 ODR[10] = 0 gpiof_virt->ODR &= (~(0x1 << 10)); //LED3初始化 //设置PE8引脚为输出模式 MODER[17:16] = 01 gpioe_virt->MODER &= (~(0x3 << 16)); gpioe_virt->MODER |= (0x1 << 16); //设置PE8引脚输出低电平,LED3熄灭 ODR[8] = 0 gpioe_virt->ODR &= (~(0x1 << 8)); return 0; //函数的返回值 } //出口函数 static void __exit demo_exit(void) { iounmap(gpioe_virt); iounmap(gpiof_virt); iounmap(rcc_virt); //注销字符设备驱动 unregister_chrdev(major,CNAME); } module_init(demo_init); //指定入口地址 module_exit(demo_exit); //指定出口地址 MODULE_LICENSE("GPL"); //许可证,遵循GPL协议
应用层代码:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include "myled.h" int main(int argc,const char * argv[]) { int fd = -1; int whitch = 0; char buf[128] = ""; fd = open("/dev/myled",O_RDWR); //打开 if(fd == -1){ perror("open is error\n"); exit(1); } while(1) { whitch = LED1; //操作LED1 ioctl(fd,LED_ON,&whitch); sleep(1); ioctl(fd,LED_OFF,&whitch); sleep(1); whitch = LED2; //操作LED2 ioctl(fd,LED_ON,&whitch); sleep(1); ioctl(fd,LED_OFF,&whitch); sleep(1); whitch = LED3; //操作LED3 ioctl(fd,LED_ON,&whitch); sleep(1); ioctl(fd,LED_OFF,&whitch); sleep(1); } close(fd); return 0; }