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/uaccess.h>
- #include<linux/io.h>
- #include<linux/device.h>
- #include <linux/ioctl.h>
- #include <linux/cdev.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/kernel.h>
- #include <uapi/linux/kdev_t.h>
- #include "myled.h"
-
- #define CNAME "myled"
- char kbuf[128] = {0};
- struct class *cls;
- struct device *dev;
- //虚拟地址
- gpio_t *virt_gpioe; //PE
- gpio_t *virt_gpiof; //PF
- volatile unsigned int *virt_rcc; //RCC
-
- //cdev结构体指针
- struct cdev *cdev;
- #if 1
- unsigned int major=0; //动态申请设备号
- #else
- unsigned int major=500; //静态申请设备号
- #endif
- int minor = 0;
- const int count=3;
- int mycdev_open(struct inode *inode, struct file *file)
- {
-
- int minor;
- minor=MINOR(inode->i_rdev);
- file->private_data=(void*)minor;
-
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
- {
-
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return size;
- }
- ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
- {
-
- int minor;
- int ret;
- minor=(int)file->private_data;
-
- switch(minor)
- {
- case 0: //LED1---->PE10
- if(size > sizeof(kbuf))
- {
- size=sizeof(kbuf);
- }
- ret=copy_from_user(kbuf,ubuf,size);
- if(ret)
- {
- printk("传递数据失败\n");
- return -EIO;
- }
- if('0'==kbuf[0])
- {
- virt_gpioe->ODR &= (~(0x1<<10));
- }
- else
- {
- virt_gpioe->ODR |=(0x1<<10);
- }
- break;
- case 1: //LED2---->PF10
- if(size > sizeof(kbuf))
- {
- size=sizeof(kbuf);
- }
- ret=copy_from_user(kbuf,ubuf,size);
- if(ret)
- {
- printk("传递数据失败\n");
- return -EIO;
- }
- if('0'==kbuf[0])
- {
- virt_gpiof->ODR &= (~(0x1<<10));
- }
- else
- {
- virt_gpiof->ODR |=(0x1<<10);
- }
- break;
- case 2: //LED3---->PE8
- if(size > sizeof(kbuf))
- {
- size=sizeof(kbuf);
- }
- ret=copy_from_user(kbuf,ubuf,size);
- if(ret)
- {
- printk("传递数据失败\n");
- return -EIO;
- }
- if('0'==kbuf[0])
- {
- virt_gpioe->ODR &= (~(0x1<<8));
- }
- else
- {
- virt_gpioe->ODR |=(0x1<<8);
- }
- break;
- }
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return size;
- }
- long mycdev_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
- {
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- int mycdev_close(struct inode *inode, struct file *file)
- {
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- //操作方法结构体
- const struct file_operations fops =
- {
- .open = mycdev_open,
- .read = mycdev_read,
- .write = mycdev_write,
- .unlocked_ioctl = mycdev_ioctl,
- .release = mycdev_close,
- };
- //入口
- static int __init mycdev_init(void)
- {
- int i;
- int ret;
- dev_t devno; //动态分配的设备号
- //1.分配cdev结构体
- cdev=cdev_alloc();
- if(NULL==cdev)
- {
- printk("cdev_alloc fail\n");
- goto ERR1;
- return -EIO;
- }
- //2.初始化结构体
- cdev_init(cdev,&fops);
- //3.申请设备号
- if(major>0)
- {
- //静态
- ret=register_chrdev_region(MKDEV(major,minor),count,CNAME);
- if(ret)
- {
- printk("静态申请设备号失败\n");
- ret=-ENOMEM;
- goto ERR2;
- }
- }
- else
- {
- //动态申请设备号
- ret=alloc_chrdev_region(&devno,0,count,CNAME);
- if(ret)
- {
- printk("动态申请设备失败\n");
- ret=-ENOMEM;
- goto ERR2;
- }
- major=MAJOR(devno);
- minor=MINOR(devno);
- }
-
- //4.驱动的注册
- ret=cdev_add(cdev,MKDEV(major,minor),count);
- if(ret)
- {
- printk("驱动注册失败\n");
- return -EIO;
- goto ERR3;
- }
- //5.自动创建设备节点
- cls=class_create(THIS_MODULE,CNAME);
- if(IS_ERR(cls))
- {
- printk("向上层提交目录信息失败\n");
- ret=PTR_ERR(cls);
- goto ERR4;
- }
- //提交设备结点信息
- for(i=0;i<count;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;
- }
- }
- //对灯地址进行映射
- virt_rcc=ioremap(PHY_RCC,4);
- if(NULL==virt_rcc)
- {
- printk("RCC映射失败\n");
- return -ENOMEM;
- }
- virt_gpioe=ioremap(PHY_GPIOE,sizeof(gpio_t));
- if(NULL==virt_gpioe)
- {
- printk("GPIOE映射失败");
- return -ENOMEM;
- }
- virt_gpiof=ioremap(PHY_GPIOF,sizeof(gpio_t));
- if(NULL==virt_gpiof)
- {
- printk("GPIOF映射失败\n");
- return -ENOMEM;
- }
- //对灯进行初始化
- //RCC使能
- *virt_rcc |= (0x3 << 4);
- //1.对LED1--->PE10初始化
- //设置PE10引脚为输出模式
- virt_gpioe->MODER &= (~(0x3 << 20));
- virt_gpioe->MODER |= (0x1 << 20);
- //设置PE10引脚默认为低电平
- virt_gpioe->ODR &= (~(0x1 << 10));
-
- //2.对LED2--->PF10初始化
- //设置PF10引脚为输出模式
- virt_gpiof->MODER &= (~(0x3 << 20));
- virt_gpiof->MODER |= (0x1 << 20);
- //设置PF10引脚默认为低电平
- virt_gpiof->ODR &= (~(0x1 << 10));
-
- //3.对LED3--->PE8初始化
- //设置PE8引脚为输出模式
- virt_gpioe->MODER &= (~(0x3 << 16));
- virt_gpioe->MODER |= (0x1 << 16);
- //设置PE8引脚默认为低电平
- virt_gpioe->ODR &= (~(0x1 << 8));
- 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,minor),count);
- ERR2:
- kfree(cdev);
- ERR1:
- //返回错误码
- return -EIO;
- }
- //出口
- static void __exit mycdev_exit(void)
- {
- int i;
- //1。销毁设备节点信息
- for(i=0;i<count;i++)
- {
- device_destroy(cls,MKDEV(major,i));
- }
- //2.销毁目录信息
- class_destroy(cls);
- //3.驱动的注销
- cdev_del(cdev);
- //4.销毁设备号
- unregister_chrdev_region(MKDEV(major,minor),count);
- //5.释放cdev结构体
- kfree(cdev);
- }
- //指定入口地址
- module_init(mycdev_init);
- module_exit(mycdev_exit);
- //指定出口地址
- //许可证
- MODULE_LICENSE("GPL");