1.驱动代码,安装LED灯驱动注册设备文件,完成寄存器地址的映射,控制灯的亮灭
#include "head.h"
#include "cdev.h"
int mycdev_open(struct inode *inode, struct file *file)
{
//1.通过struct inode找到设备号
//2.根据设备号使用MINOR函数找到次设备号
//3.将次设备号放入到struct file文件中private_date中
led_minor=MINOR(inode->i_rdev);
file->private_data=(void *)led_minor;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 向用户空间读取拷贝
if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//获取struct file的private_data中存储的次设备号
led_x=(unsigned int)file->private_data;
//判断是哪个LED灯的设备文件
switch(led_x)
{
//LED1灯
case 0:
switch(cmd)
{
case LED_ON:
vir_led1->ODR |=(1<<10);
break;
case LED_OFF:
vir_led1->ODR &=(~(1<<10));
break;
}
break;
//LED2灯
case 1:
switch(cmd)
{
case LED_ON:
vir_led2->ODR |=(1<<10);
break;
case LED_OFF:
vir_led2->ODR &=(~(1<<10));
break;
}
break;
//LED3灯
case 2:
switch(cmd)
{
case LED_ON:
vir_led3->ODR |=(1<<8);
break;
case LED_OFF:
vir_led3->ODR |=(~(1<<8));
break;
}
break;
}
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int all_led_init(void)
{
// 寄存器地址的映射
vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
if (vir_led1 == NULL)
{
printk("ioremap filed:%d\n", __LINE__);
return -ENOMEM;
}
vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
if (vir_led2 == NULL)
{
printk("ioremap filed:%d\n", __LINE__);
return -ENOMEM;
}
vir_led3 = vir_led1;
vir_rcc = ioremap(PHY_RCC_ADDR, 4);
if (vir_rcc == NULL)
{
printk("ioremap filed:%d\n", __LINE__);
return -ENOMEM;
}
printk("物理地址映射成功\n");
// 寄存器的初始化
// rcc
(*vir_rcc) |= (3 << 4);
// 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_led1->MODER |= (1 << 16);
vir_led1->ODR &= (~(1 << 8));
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
// 从用户空间读取数据
if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(kbuf);
ret = copy_from_user(&sum, ubuf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
.unlocked_ioctl = mycdev_ioctl,
};
static int __init mycdev_init(void)
{
int i=-1;
/*****LED灯的驱动安装流程*****/
//1.安装驱动获取mojor设备号
printk("LED灯驱动安装\n");
major=register_chrdev(0,"mychrdev",&fops); //系统动态申请主设备号
if(major<0)
{
printk("主设备号申请失败\n");
return major;
}
printk("主设备号申请成功:major=%d\n",major);
//3.向上提交目录
cls=class_create(THIS_MODULE,"mychrdev");
if(IS_ERR(cls))
{
printk("向上提交目录失败\n");
return PTR_ERR(cls);
}
printk("向上提交目录成功\n");
/*
cls=class_create(THIS_MODULE,"mychrdev1");
cls=class_create(THIS_MODULE,"mychrdev2");
*/
//4.向上提交设备节点信息,分别为每一个灯创建设备文件
for(i=0;i<3;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mychrdev%d",i);
if(IS_ERR(dev))
{
printk("向上提交设备信息mychrdev%d失败\n",i);
return PTR_ERR(dev);
}
printk("向上提交设备信息mychrdev%d成功",i);
}
//LED灯的初始化
all_led_init();
return 0;
}
static void __exit mycdev_exit(void)
{
// 取消地址映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_rcc);
// 销毁设备信息
device_destroy(cls, MKDEV(major, 0));
device_destroy(cls, MKDEV(major, 1));
device_destroy(cls, MKDEV(major, 2));
// 销毁目录
class_destroy(cls);
// 注销字符设备驱动
unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
2.应用代码,打开不同的设备文件,控制对应的LED灯亮闪
#include "user.h"
#include "head.h"
int main(int argc, char const *argv[])
{
while (1)
{
my_select = -1;
key = -1;
fd = -1;
printf("请选择你要进行的操作1<亮灯>,2<熄灯>: ");
scanf("%d", &my_select);
switch (my_select)
{
// 亮灯
case 1:
printf("选择你要操作的指示灯 1<LED1>,2<LED2>,3<LED3>: ");
scanf("%d", &key);
switch (key)
{
// 操作LED1灯亮
case 1:
// 打开LED灯的设备文件
fd = open("/dev/mychrdev0", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_ON);
break;
// 操作LED2灯亮
case 2:
fd = open("/dev/mychrdev1", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_ON);
break;
// 操作LED3灯亮
case 3:
fd = open("/dev/mychrdev2", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_ON);
break;
}
break;
// 熄灯
case 2:
printf("选择你要操作的指示灯 1<LED1>,2<LED2>,3<LED3>: ");
scanf("%d", &key);
switch (key)
{
// 操作LED1灯熄灭
case 1:
// 打开LED灯的设备文件
fd = open("/dev/mychrdev0", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_OFF);
break;
// 操作LED2灯熄灭
case 2:
fd = open("/dev/mychrdev1", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_OFF);
break;
// 操作LED3灯熄灭
case 3:
fd = open("/dev/mychrdev2", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
ioctl(fd, LED_OFF);
break;
}
break;
default:
break;
}
}
close(fd);
return 0;
}