1)分部实现注册字符设备驱动
2)自动创建设备节点
3)通过结构体对led灯地址进行映射
4)次设备号完成私有数据传参
5)在open函数中获取到次设备号,用私有数据传参,传递给write函数
6)在write函数,判断次设备号,就知道操作的是哪盏灯
在串口工具进行输入:
echo 1 > /dev/myled0 ---->led1灯点亮
echo 0 > /dev/myled0 ---->led1灯熄灭
echo 1 > /dev/myled1 ---->led1灯点亮
echo 0 > /dev/myled1 ---->led1灯熄灭
- #ifndef __LED_H__
- #define __LED_H__
-
- typedef struct{
- volatile unsigned int MODER;
- volatile unsigned int OPTYPER;
- volatile unsigned int OSPEEDR;
- volatile unsigned int PUPDR;
- volatile unsigned int IDR;
- volatile unsigned int ODR;
- }gpio_t;
- typedef enum{
- LED1,
- LED2,
- LED3
- }led_t;
- #define GPIOE 0x50006000
- #define GPIOF 0x50007000
- #define PHY_RCC 0x50000A28
-
-
- #endif
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include "led.h"
- #include <linux/uaccess.h>
- #include <linux/io.h>
- #define CNAME "myled"
- struct class *cls;
- struct device *dev;
- struct cdev *mycdev;
- #if 1
- unsigned int major = 0;
- #else
- unsigned int major = 500;
- #endif
- int minor =0;
- const int count = 3;
- volatile unsigned int* virt_rcc;
- gpio_t *virt_gpioe;
- gpio_t *virt_gpiof;
- int mycdev_open(struct inode *inode,struct file *file)
- {
- int pos = MINOR(inode->i_rdev);
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- file->private_data = (void*)pos;
- return 0;
- }
- ssize_t mycdev_read(struct file* file,char __user *ubuf,size_t size,loff_t *loffs)
- {
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- ssize_t mycdev_write(struct file* file,const char __user *ubuf,size_t size,loff_t *loffs)
- {
- int pos;
- int ret = 0;
- char kbuf[5]={0};
- if(size > 5) size=5;
- ret = copy_from_user(kbuf,ubuf,size);
- if(ret)
- {
- printk("copy from user error\n");
- return -EIO;
- }
- //判断是点灯还是熄灭
- pos =(int)file->private_data;
- if('1' == kbuf[0])
- {
- //通过次设备号判断是哪个灯需要亮
- switch (pos)
- {
- case 0:
- virt_gpioe->ODR |= (0x1 << 10); //led1输出高电平
- break;
- case 1:
- virt_gpiof->ODR |= (0x1 << 10); //led2输出高电平
- break;
- case 2:
- virt_gpioe->ODR |= (0x1 << 8); //led3输出高电平
- break;
- }
- }
- if('0' == kbuf[0])
- {
- //通过次设备号判断是哪个灯需要灭
- switch (pos)
- {
- case 0:
- virt_gpioe->ODR &= ~(0x1 << 10); //led1输出低电平
- break;
- case 1:
- virt_gpiof->ODR &= ~(0x1 << 10); //led2输出低电平
- break;
- case 2:
- virt_gpioe->ODR &= ~(0x1 << 8); //led3输出低电平
- break;
- }
- }
- printk("kbuf=%s\n",kbuf);
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return size;
- }
- 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,
- .release = mycdev_close,
- };
- static int __init demo_init(void)
- {
- int ret = 0;
- dev_t devno;
- int i;
- //分配cdev结构体空间
- mycdev = cdev_alloc();
- if(NULL == mycdev)
- {
- printk("cdev alloc error\n");
- ret = -EIO;
- goto ERR1;
- }
- //初始化结构体
- cdev_init(mycdev,&fops);
- //申请设备号
- if(major >0)
- {
- //静态申请设备号
- ret = register_chrdev_region(MKDEV(major,minor),count,CNAME);
- if(ret)
- {
- printk("register chrdev regin error\n");
- ret = -ENOMEM;
- goto ERR2;
- }
- }
- else
- {
- //动态申请设备号
- ret = alloc_chrdev_region(&devno,0,count,CNAME);
- if(ret)
- {
- printk("alloc_chrdev error\n");
- ret = -ENOMEM;
- goto ERR2;
- }
- major = MAJOR(devno);
- minor = MINOR(devno);
- }
- //驱动注册
- ret = cdev_add(mycdev,MKDEV(major,minor),count);
- if(ret)
- {
- printk("cdev add error\n");
- ret = -EIO;
- goto ERR3;
- }
- //自动创建设备节点
- //提交目录信息
- cls = class_create(THIS_MODULE,CNAME);
- if(IS_ERR(cls))
- {
- printk("class create error\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("device create error\n");
- ret = PTR_ERR(dev);
- goto ERR5;
- }
- }
- //对灯的物理地址进行映射
- virt_rcc = ioremap(PHY_RCC,4);
- if(NULL == virt_rcc)
- {
- printk("rcc ioremap is error\n");
- return -ENOMEM;
- }
- virt_gpioe = ioremap(GPIOE,sizeof(GPIOE));
- if(NULL == virt_gpioe)
- {
- printk("gpio moder ioremap is error\n");
- return -ENOMEM;
- }
- virt_gpiof = ioremap(GPIOF,sizeof(GPIOF));
- if(NULL == virt_gpiof)
- {
- printk("gpio odr ioremap is error\n");
- return -ENOMEM;
- }
- //将rcc、gpio初始化 PE10\PF10\PE8
- *virt_rcc |= (0x3<<4);
- //PE10初始化
- virt_gpioe->MODER &= (~(0x3<<20));
- virt_gpioe->MODER |= 0x1<<20;
- virt_gpioe->ODR &= (~(0x1<<10));
- //PE8初始化
- virt_gpioe->MODER &= (~(0x3<<16));
- virt_gpioe->MODER |= 0x1<<16;
- virt_gpioe->ODR &= (~(0x1<<8));
- //PF10初始化
- virt_gpiof->MODER &= (~(0x3<<20));
- virt_gpiof->MODER |= 0x1<<20;
- virt_gpiof->ODR &= (~(0x1<<10));
- return 0;
- ERR5:
- for(--i;i>=0;i--)
- {
- device_destroy(cls,MKDEV(major,i));
- }
- class_destroy(cls);
-
- ERR4:
- cdev_del(mycdev);
- ERR3:
- unregister_chrdev_region(MKDEV(major,minor),count);
- ERR2:
- kfree(mycdev);
- ERR1:
- return -EIO;
- }
- static void __exit demo_exit(void)
- {
- int i = 0;
- //销毁设备节点信息
- for(i=0;i<count;i++)
- {
- device_destroy(cls,MKDEV(major,i));
- }
- //销毁目录信息
- class_destroy(cls);
- //驱动注销
- cdev_del(mycdev);
- //注销设备号
- unregister_chrdev_region(MKDEV(major,minor),count);
- //释放结构体指针
- kfree(mycdev);
- }
-
- module_init(demo_init);
- module_exit(demo_exit);
- MODULE_LICENSE("GPL");
在串口工具依次输入命令,点亮LED1\LED2\LED3,然后依次熄灭LED1\LED2\LED3,如下图
LED1亮:

LED1\LED2亮:

LED1\LED2\LED3亮:

LED1熄灭:

LED1\LED2熄灭:

LED1\LED2\LED3熄灭:
