demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include "myled.h"
#include <linux/io.h>
#define CNAME "myled"
struct cdev *cdev;
#if 0
unsigned int major = 500; //静态指定设备号
#else
unsigned int major = 0; //动态分配主设备号的值
#endif
unsigned int count = 3;
unsigned int minor = 0;//保存次设备号的值
struct class* cls;
struct device* device;
char kbuf[128] = "";
unsigned int* rcc_virt = NULL; //RCC虚拟地址
gpio_t* gpioe_virt = NULL; //gpioe组虚拟地址
gpio_t*gpiof_virt = NULL; //gpiof组虚拟地址
#define LED1_ON (gpioe_virt->ODR |= (0x1 << 10)) //LED1点亮
#define LED1_OFF (gpioe_virt->ODR &= (~(0x1 << 10))) //LED1熄灭
#define LED2_ON (gpiof_virt->ODR |= (0x1 << 10)) //LED2点亮
#define LED2_OFF (gpiof_virt->ODR &= (~(0x1 << 10))) //LED2熄灭
#define LED3_ON (gpioe_virt->ODR |= (0x1 << 8)) //LED3点亮
#define LED3_OFF (gpioe_virt->ODR &= (~(0x1 << 8))) //LED3熄灭
int myled_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 myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
int minor = 0;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
minor = (int)(file->private_data);
//如果用户想写空间的大小大于内核空间的大小,更改用户写数据的大小
if(size > sizeof(kbuf)) size = sizeof(kbuf);
//将用户空间的数据拷贝到内核空间
ret = copy_from_user(kbuf,ubuf,size);
if(ret){
printk("copy_from_user is error\n");
return -EIO;
}
//根据次设备号的值来决定操作哪一栈灯
switch(minor)
{
case LED1:
kbuf[0] == '1' ? LED1_ON : LED1_OFF;
break;
case LED2:
kbuf[0] == '1' ? LED2_ON : LED2_OFF;
break;
case LED3:
kbuf[0] == '1' ? LED3_ON : LED3_OFF;
break;
}
return size;
}
int myled_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
//操作方法结构体
const struct file_operations fops = {
.open = myled_open,
.write = myled_write,
.release = myled_close,
};
//入口
static int __init demo_init(void)
{
int ret;
dev_t devno;
int i = 0;
//分配对象
cdev = cdev_alloc();
if(cdev == NULL){
printk("cdev alloc is error\n");
ret = -EIO;
goto ERR1;
}
//对象初始化
cdev_init(cdev,&fops);
if(major > 0)
{
//静态指定设备号
ret = register_chrdev_region(MKDEV(major,minor), count, CNAME);
if(ret){
printk("register chrdev region is error\n");
ret = -EIO;
goto ERR2;
}
}else{ //动态指定设备号
ret = alloc_chrdev_region(&devno, 0, count,CNAME);
if(ret){
printk("alloc chrdev region is error\n");
ret = -EIO;
goto ERR2;
}
major = MAJOR(devno);//根据设备号,获取主设备号的值
minor = MINOR(devno);//根据设备号,获取次设备号的值
}
//对象注册
ret = cdev_add(cdev,MKDEV(major,minor),count);
if(ret){
printk("dev add is error\n");
ret = -EIO;
goto ERR3;
}
//三盏灯,自动创建三个设备节点 /dev/myled0 /dev/myled1 /dev/myled2
cls = class_create(THIS_MODULE, CNAME); //向上层提交目录信息
if(IS_ERR(cls))
{
printk("class create is error\n");
ret = EIO;
goto ERR4;
}
for(i=0;i<count;i++) //向上层提交设备节点信息
{
device = device_create(cls, NULL, MKDEV(major,i),NULL, "myled%d", i);
if(IS_ERR(device))
{
printk("device create is error\n");
ret = EIO;
goto ERR5;
}
}
//RCC_MP_AHB4ENSETR寄存器地址映射
rcc_virt = ioremap(RCC_MP_AHB4ENSETR,4);
if(rcc_virt == NULL){
printk("rcc ioremap is error\n");
return -EIO;
}
//GPIOE组寄存器地址映射
gpioe_virt = ioremap(GPIOE,sizeof(gpio_t));
if(gpioe_virt == NULL){
printk("gpioe_virtr ioremap is error\n");
return -EIO;
}
//GPIOF组寄存器地址映射
gpiof_virt = ioremap(GPIOF,sizeof(gpio_t));
if(gpiof_virt == NULL){
printk("gpiof_virtr ioremap is error\n");
return -EIO;
}
//LED1初始化
//使能GPIOE组控制器 rcc_virt[4] = 1
*rcc_virt |= (0x1 << 4);
//设置PE10引脚为输出模式 MODER[21:20] = 01
gpioe_virt->MODER &= (~(0x3 << 20));
gpioe_virt->MODER |= (0x1 << 20);
//设置PE10引脚输出低电平,LED1熄灭 ODR[10] = 0
gpioe_virt->ODR &= (~(0x1 << 10));
//LED2初始化
//使能GPIOE组控制器 rcc_virt[5] = 1
*rcc_virt |= (0x1 << 5);
//设置PF10引脚为输出模式 MODER[21:20] = 01
gpiof_virt->MODER &= (~(0x3 << 20));
gpiof_virt->MODER |= (0x1 << 20);
//设置PF10引脚输出低电平,LED2熄灭 ODR[10] = 0
gpiof_virt->ODR &= (~(0x1 << 10));
//LED3初始化
//设置PE8引脚为输出模式 MODER[17:16] = 01
gpioe_virt->MODER &= (~(0x3 << 16));
gpioe_virt->MODER |= (0x1 << 16);
//设置PE8引脚输出低电平,LED3熄灭 ODR[8] = 0
gpioe_virt->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 ret;
}
//出口
static void __exit demo_exit(void)
{
int i;
iounmap(gpioe_virt);
iounmap(gpiof_virt);
iounmap(rcc_virt);
for(i=0;i<count;i++)
{
device_destroy(cls, MKDEV(major,i)); //取消向上层提交设备节点信息
}
class_destroy(cls); //取消向上层提交目录信息
cdev_del(cdev); //对象注销
unregister_chrdev_region(MKDEV(major,minor), count); //注销设备号
kfree(cdev); //释放结构体指针
}
module_init(demo_init); //指定入口地址
module_exit(demo_exit); //指定出口地址
MODULE_LICENSE("GPL");//许可证
myled.h
#ifndef __MYLED_H__
#define __MYLED_H__
enum
{
LED1,//0
LED2,//1
LED3,//2
};
typedef struct {
volatile unsigned int MODER; // 0x00
volatile unsigned int OTYPER; // 0x04
volatile unsigned int OSPEEDR; // 0x08
volatile unsigned int PUPDR; // 0x0C
volatile unsigned int IDR; // 0x10
volatile unsigned int ODR; // 0x14
volatile unsigned int BSRR; // 0x18
volatile unsigned int LCKR; // 0x1C
volatile unsigned int AFRL; // 0x20
volatile unsigned int AFRH; // 0x24
volatile unsigned int BRR; // 0x28
volatile unsigned int res;
volatile unsigned int SECCFGR; // 0x30
}gpio_t;
#define GPIOE (0x50006000)
#define GPIOF (0x50007000)
#define RCC_MP_AHB4ENSETR 0x50000A28//RCC_MP_AHB4ENSETR寄存器地址
#endif