1.在串口工具进行输入:
echo 1 > /dev/myled0 ---->led1灯点亮
echo 0 > /dev/myled0 ---->led1灯熄灭
echo 1 > /dev/myled1 ---->led1灯点亮
echo 0 > /dev/myled1 ---->led1灯熄灭
echo 1 > /dev/myled2 ---->led1灯点亮
echo 0 > /dev/myled2 ---->led1灯熄灭
linux@ubuntu:/sys/class/myled$ ls /dev/myled* -ll
crw------- 1 root root 236, 0 Nov 18 14:55 /dev/myled0 ----->控制PE10(LED1)
crw------- 1 root root 236, 1 Nov 18 14:55 /dev/myled1----->控制PF10(LED2)
crw------- 1 root root 236, 2 Nov 18 14:55 /dev/myled2----->控制PE8(LED3)
2.驱动:
open:在open函数中获取到次设备号,用私有数据传参,传递给write函数
write:在write函数,判断次设备号,就知道操作的是哪盏灯
3.要求:
1)分部实现注册字符设备驱动
2)自动创建设备节点
3)通过结构体对led灯地址进行映射
4)次设备号完成私有数据传参
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include<linux/slab.h>
#include "demo2.h"
#define CNAME "myled" //宏定义设备名
//定义设备号
#if 1
unsigned int major = 0; //动态设备号
#else
unsigned int major = 500; //动态设备号
#endif
//int all_led_init();
const int count = 3;
int mnior = 0; //次设备号
struct cdev *cdev; //
struct class *cls; //目录句柄
struct device *dev; //
gpio_t *led1;
unsigned int *RCC;
gpio_t *led2;
gpio_t *led3;
//打开
int mydemo_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
file->private_data = (void*)(MINOR(inode->i_rdev));
return 0;
}
//读
ssize_t mydemo_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//写
ssize_t mydemo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
char kbuf[10]={0};
int ret;
int a;
if(size>sizeof(kbuf))
size=sizeof(kbuf);
a=(int)file->private_data;
printk("myled%d open\n",a);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy from user is error\n");
return -EIO;
}
//接收第一个灯信息
if (kbuf[0] == '1') //开灯
{
if (a == 0)
led1->ODR |= (1 << 10);
else if (a == 1)
led2->ODR |= (1 << 10);
else if (a == 2)
led3->ODR |= (1 << 8);
}
else if (kbuf[0] == '0') //关灯
{
if (a == 0)
led1->ODR &= ~(1 << 10);
else if (a == 1)
led2->ODR &= ~(1 << 10);
else if (a == 2)
led3->ODR &= ~(1 << 8);
}
return size;
}
//关闭
int mydemo_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//操作方法结构体初始化
const struct file_operations fops =
{
.open = mydemo_open,
.read = mydemo_read,
.write = mydemo_write,
.release = mydemo_close,
};
int all_led_init(void)
{
//映射地址
//映射物理地址为虚拟地址
RCC = ioremap(0x50000A28, 4); // GPLOE使能
led1 = ioremap(0x50006000, 4); // PE引脚设置为输出模式
led2 = ioremap(0x50007000, 4);
led3 = ioremap(0x50006000, 4);
(*RCC) |= (0x3 << 4);
// led1
led1->MODER &= ~(0x3 << 20);
led1->MODER |= (0x1 << 20);
led1->ODR &= ~(0x1 << 10);
// led2
led2->MODER &= ~(0x3 << 20);
led2->MODER |= (0x1 << 20);
led2->ODR &= ~(0x1 << 10);
// led3
led3->MODER &= ~(0x3 << 16);
led3->MODER |= (0x1 << 16);
led3->ODR &= ~(0x1 << 8);
return 0;
}
//入口函数
static int __init demo_init(void)
{
int ret, i;
dev_t devno;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 1.分配结构体对象
cdev = cdev_alloc();
if (NULL == cdev)
{
printk("cdev alloc is error\n");
ret = -ENOMEM;
goto ERR1;
}
printk("cdev 分配成功\n");
// 2.设备对象初始化
cdev_init(cdev, &fops);
// 3.设备号申请
if (major == 0)
{
ret = alloc_chrdev_region(&devno, mnior, count, CNAME); //动态申请设备号
if (ret)
{
printk("alloc_chrdev_register is error \n");
goto ERR2;
}
major = MAJOR(devno);
mnior = MINOR(devno);
printk("动态申请设备号成功\n");
}
else
{
ret = register_chrdev_region(MKDEV(major, mnior), count, CNAME); //静态申请设备号
if (ret)
{
printk("静安申请设备号失败\n");
goto ERR2;
}
printk("静态申请设备号成功\n");
}
// 4.注册字符设备驱动
ret = cdev_add(cdev,MKDEV(major, mnior), count);
if (ret)
{
printk("字符设备驱动注册失败\n");
goto ERR3;
}
printk("字符设备注册成功\n");
// 5.自动创建设备结点
cls = class_create(THIS_MODULE, "led");
if (IS_ERR(cls))
{
printk("创建设备目录失败\n");
ret = PTR_ERR(cls);
goto ERR4;
}
//向上提交设备结点信息
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev))
{
printk("创建逻辑结点失败\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
printk("创建逻辑节点成功\n");
//寄存器的初始化
all_led_init();
return 0;
ERR5:
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, mnior), count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
//出口函数
static void __exit demo_exit(void)
{
// 1.销毁设备节点
int i;
for (i = 0; i < count; i++)
{
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
// 2.注销字符设备驱动
cdev_del(cdev);
// 3.释放设备号
unregister_chrdev_region(MKDEV(major, mnior), count);
// 4.释放动态申请的空间
kfree(cdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
}
//内核寻找入口出口宏封装
module_init(demo_init);
module_exit(demo_exit);
//许可证
MODULE_LICENSE("GPL");