实验要求
通过ioctl函数实现6盏灯的亮灭(区分到底时拓展板还是底板)、蜂鸣器的打开关闭、马达的开关
分析
ioctl
应用层:
#include <sys/ioctl.h>
int which=10;
int ioctl(int fd, unsigned long request, ...);
功能:进行设备的读写控制
参数:
fd:设备文件的文件描述符
request:功能码
...:第三个参数,可以通过这个参数向内核实现数据拷贝,这个参数写要拷贝的数据所在空间首地址
返回值:成功返回0、失败返回错误码或者-1
*******************************************************************
内核层:
struct file_operations {
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg);
cmd:功能码
arg:用户空间ioctl第三个参数传递的参数
}
如果想要得到上面的代表特定功能的功能吗,可以通过下面的宏定义实现:
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) //ioctl不传递第三个参数时使用
#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))
copy_from_user
unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
功能:实现用户空间数据拷贝到内核空间
参数:
to:内核空间存放数据的首地址
from:用户空间存放数据的首地址
n:大小(字节)
返回值:成功返回0,失败但会未拷贝的数据大小
实现代码
驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include "test.h"
int major;
char kbuf[128] = {
0};
struct class *cls;
struct device *dev;
unsigned int *vir_rcc;
unsigned int *vir_m_rcc;
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
gpio_t *vir_m_led1;
gpio_t *vir_m_led2;
gpio_t *vir_m_led3;
gpio_t *vir_beep;
gpio_t *vir_motor;
int mycdev_open(struct inode *inode, struct file *file)
{
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *off)
{
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *off)
{
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
devices_t devs;
ret = copy_from_user((void *)&devs, (void *)arg, sizeof(struct devices)); // 把ioctl第三个参数的数值获取到
if (ret)
{
printk("copy_from_user err\n");
return -ENOMEM;
}
switch (cmd)
{
case MAIN_DEVICE:
switch (devs.which)
{
case 1:
if (devs.state == 1)
{
vir_m_led1->ODR |= (1 << 5);
}
else
{
vir_m_led1->ODR &= (~(1 << 5));
}
break;
case 2:
if (devs.state == 1)
{
vir_m_led2->ODR |= (1 << 6);
}
else
{
vir_m_led2->ODR &= (~(1 << 6));
}
break;
case 3:
if (devs.state == 1)
{
vir_m_led3->ODR |= (1 << 7);
}
else
{
vir_m_led3->ODR &= (~(1 << 7));
}
break;
}
break;
case OTHER_DEVICE:
if (devs.device == 0)
{
switch (devs.which)
{
case 1:
if (devs.state == 1)
{
vir_led1->ODR |= (1 << 10);
}
else
{
vir_led1->ODR &= (~(1 << 10));
}
break;
case 2:
if (devs.state == 1)
{
vir_led2->ODR |= (1 << 10);
}
else
{
vir_led2->ODR &= (~(1 << 10));
}
break;
case 3:
if (devs.state == 1)
{
vir_led3->ODR |= (1 << 8);
}
else
{
vir_led3->ODR &= (~(1 << 8));
}
break;
}
}
else if (devs.device == 1)
{
if (devs.state == 1)
{
vir_beep->ODR |= (1 << 6);
}
else
{
vir_beep->ODR &= (~(1 << 6));
}
}
else if (devs.device == 2)
{
if (devs.state == 1)
{
vir_motor->ODR |= (1 << 6);
}
else
{
vir_motor->ODR &= (~(1 << 6));
}
}
}
return 0;
}
struct file_operations fops =
{
.open = mycdev_open,
.write = mycdev_write,
.read = mycdev_read,
.release = mycdev_close,
.unlocked_ioctl = mycdev_ioctl,
};
int all_leds_init(void)
{
// 进行物理地址映射
vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
if (vir_led1 == NULL)
{
printk("LED1寄存器映射失败\n");
return -ENOMEM;
}
vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
if (vir_led2 == NULL)
{
printk("LED2寄存器映射失败\n");
return -ENOMEM;
}
vir_led3 = ioremap(PHY_LED3_ADDR, sizeof(gpio_t));
if (vir_led3 == NULL)
{
printk("LED3寄存器映射失败\n");
return -ENOMEM;
}
vir_rcc = ioremap(PHY_RCC_ADDR, 4);
if (vir_rcc == NULL)
{
printk("RCC寄存器映射失败\n");
return -ENOMEM;
}
// 主板LED地址映射
vir_m_led1 = ioremap(MAIN_LED_ADDR, sizeof(gpio_t));
if (vir_m_led1 == NULL)
{
printk("LED1寄存器映射失败\n");
return -ENOMEM;
}
vir_m_led2 = ioremap(MAIN_LED_ADDR, sizeof(gpio_t));
if (vir_m_led2 == NULL)
{
printk("LED2寄存器映射失败\n");
return -ENOMEM;
}
vir_m_led3 = ioremap(MAIN_LED_ADDR, sizeof(gpio_t));
if (vir_m_led3 == NULL)
{
printk("LED3寄存器映射失败\n");
return -ENOMEM;
}
vir_m_rcc = ioremap(RCC_AHB5_ADDR, 4);
if (vir_m_rcc == NULL)
{
printk("RCC寄存器映射失败\n");
return -ENOMEM;
}
printk("寄存器物理地址映射成功\n");
// rcc初始化
(*vir_rcc) |= (3 << 4); // rcc使能
(*vir_m_rcc) |= 0; // gpioz rcc使能
// LED1初始化
vir_led1->MODER &= (~(3 << 20)); // 设置为输出模式
vir_led1->MODER |= (1 << 20);
vir_led1->ODR &= (~(1 << 10)); // 输出低电平
// LED2初始化
vir_led2->MODER &= (~(3 << 20)); // 设置为输出模式
vir_led2->MODER |= (1 << 20);
vir_led2->ODR &= (~(1 << 10)); // 输出低电平
// LED3初始化
vir_led3->MODER &= (~(3 << 16)); // 设置为输出模式
vir_led3->MODER |= (1 << 16);
vir_led3->ODR &= (~(1 << 8)); // 输出低电平
// 主板LED的LED灯初始化
// LED1初始化
vir_m_led1->MODER &= (~(3 << 10)); // 设置为输出模式
vir_m_led1->MODER |= (1 << 10);
vir_m_led1->ODR &= (~(1 << 5)); // 输出低电平
// LED2初始化
vir_m_led2->MODER &= (~(3 << 12)); // 设置为输出模式
vir_m_led2->MODER |= (1 << 12);
vir_m_led2->ODR &= (~(1 << 6)); // 输出低电平
// LED3初始化
vir_m_led3->MODER &= (~(3 << 14)); // 设置为输出模式
vir_m_led3->MODER |= (1 << 14);
vir_m_led3->ODR &= (~(1 << 7)); // 输出低电平
printk("寄存器初始化成功\n");
return 0;
}
int beep_init(void)
{
// 内存映射
vir_beep = ioremap(PHY_BEEP_ADDR, sizeof(gpio_t));
if (vir_beep == NULL)
{
printk("LED1寄存器映射失败\n");
return -ENOMEM;
}
*vir_rcc |= (0x1 << 1); // GPIOB使能
vir_beep->MODER &= (~(3 << 12)); // 设置为输出模式
vir_beep->MODER |= (1 << 12);
vir_beep->ODR &= (~(1 << 6)); // 输出低电平
return 0;
}
int motor_init(void)
{
vir_motor = ioremap(PHY_MOTOR_ADDR, sizeof(gpio_t));
if (vir_motor == NULL)
{
printk("LED1寄存器映射失败\n");
return -ENOMEM;
}
vir_motor->MODER &= (~(3 << 12)); // 设置为输出模式
vir_motor->MODER |= (1 << 12);
vir_motor->ODR &= (~(1 << 6)); // 输出低电平
return 0;
}
static int __init mycdev_init(void)
{
int i;
major = register_chrdev(0, "mycdev", &fops);
if (major < 0)
{
printk("字符驱动注册失败\n");
return major;
}
else
{
printk("字符设备注册成功 major = %d\n", major);
}
// 向上提交目录
cls = class_create(THIS_MODULE, "mycdev");
if (IS_ERR(cls))
{
printk("向上提交目录失败\n");
return PTR_ERR(cls);
}
// 向上提交设备信息
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("向上提交设备结点失败\n");
return PTR_ERR(dev);
}
}
printk("向上提交设备结点成功\n");
// 初始化
all_leds_init();
beep_init();
motor_init();
return 0;
}
static void __exit mycdev_exit(void)
{
int i;
// 取消物理内存的映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_led3);
iounmap(vir_rcc);
iounmap(vir_m_led1);
iounmap(vir_m_led2);
iounmap(vir_m_led3);
iounmap(vir_m_rcc);
iounmap(vir_beep);
iounmap(vir_motor);
// 删除结点信息
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
// 删除目录信息
class_destroy(cls);
// 注销驱动
unregister_chrdev(major, "mycdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
测试代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "test.h"
int main(int argc, const char *argv[])
{
int chose;
devices_t dev;
int fd = open("/dev/mycdev1", O_RDWR);
if (fd < 0)
{
printf("文件打开失败\n");
exit(-1);
}
while (1)
{
printf("-----------------------------功能菜单------------------------------\n");
printf("\t\t\t0.开拓展板LED1灯\n");
printf("\t\t\t1.关拓展板LED1灯\n");
printf("\t\t\t2.开拓展板LED2灯\n");
printf("\t\t\t3.关拓展板LED2灯\n");
printf("\t\t\t4.开拓展板LED3灯\n");
printf("\t\t\t5.关拓展板LED3灯\n");
printf("\t\t\t6.开主板LED1灯\n");
printf("\t\t\t7.关主板LED1灯\n");
printf("\t\t\t8.开主板LED2灯\n");
printf("\t\t\t9.关主板LED2灯\n");
printf("\t\t\t10.开主板LED3灯\n");
printf("\t\t\t11.关主板LED3灯\n");
printf("\t\t\t12.开蜂鸣器\n");
printf("\t\t\t13.关蜂鸣器\n");
printf("\t\t\t14.开马达\n");
printf("\t\t\t15.关马达\n");
printf("------------------------------------------------------------------\n");
scanf("%d", &chose);
switch (chose)
{
case 0:
dev.device = 0;
dev.state = 1;
dev.which = 1;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 1:
dev.device = 0;
dev.state = 0;
dev.which = 1;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 2:;
dev.device = 0;
dev.state = 1;
dev.which = 2;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 3:
dev.device = 0;
dev.state = 0;
dev.which = 2;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 4:
dev.device = 0;
dev.state = 1;
dev.which = 3;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 5:
dev.device = 0;
dev.state = 0;
dev.which = 3;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 6:
dev.device = 0;
dev.state = 1;
dev.which = 1;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 7:
dev.device = 0;
dev.state = 0;
dev.which = 1;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 8:
dev.device = 0;
dev.state = 1;
dev.which = 2;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 9:
dev.device = 0;
dev.state = 0;
dev.which = 2;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 10:
dev.device = 0;
dev.state = 1;
dev.which = 3;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 11:
dev.device = 0;
dev.state = 0;
dev.which = 3;
ioctl(fd, MAIN_DEVICE, &dev);
break;
case 12:
dev.device = 1;
dev.state = 1;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 13:
dev.device = 1;
dev.state = 0;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 14:
dev.device = 2;
dev.state = 1;
ioctl(fd, OTHER_DEVICE, &dev);
break;
case 15:
dev.device = 2;
dev.state = 0;
ioctl(fd, OTHER_DEVICE, &dev);
break;
default:
break;
}
}
close(fd);
return 0;
}
头文件
#ifndef __TEST_H__
#define __TEST_H__
typedef struct
{
volatile unsigned int MODER;
volatile unsigned int OTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
volatile unsigned int BSRR;
} gpio_t;
typedef struct devices
{
int device; //对应哪一个设备;0:LED灯 1:蜂鸣器 2:马达
int state; //0:关 1:开
int which; //对应哪盏灯;
}devices_t;
#define PHY_RCC_ADDR 0X50000A28
#define RCC_AHB5_ADDR 0x50000210
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define MAIN_LED_ADDR 0x54004000
#define PHY_BEEP_ADDR 0x50003000
#define PHY_MOTOR_ADDR 0x50007000
#define MAIN_DEVICE _IOW('l',1,devices_t) //主板
#define OTHER_DEVICE _IOW('l',0,devices_t) //拓展板
#endif