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灯熄灭
要求:
1)分部实现注册字符设备驱动
2)自动创建设备节点
3)通过结构体对led灯地址进行映射
4)次设备号完成私有数据传参
driver.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "myled.h"
#define CNAME "myled"
struct cdev* cdev;
dev_t devnum;
struct device* dev;
struct class* cls;
#if 0
unsigned int major = 0;
#else
unsigned int major = 500;
#endif
int minor=1;
const int count=3;
int k_open(struct inode *inode, struct file *file)
{
//终端在传参时会打开对应的驱动文件,相当于调用了open和write函数,
//inode结构体中存放着该驱动的设备号,次设备号不同,通过私有数据传参到write函数中进行判断
file->private_data=(void*)MINOR(inode->i_rdev);
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t k_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 K_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
char ch;
int ret;
int kminjor;
//接收终端传入的字符(控制led的亮灭)
ret=copy_from_user(&ch,ubuf,sizeof(char));
if(ret)
{
printk("copy from user is error\n");
return -EIO;
}
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
//接收k_open函数传递的私有数据(次设备号)
kminjor=(int)file->private_data;
if(ch=='1')
{
if(kminjor==1)
{
gpioe->ODR |= (0x1 << 10);
}
if(kminjor==2)
{
gpiof->ODR |= (0x1 << 10);
}
if(kminjor==3)
{
gpioe->ODR |= (0x1 << 8);
}
}
else
{
if(kminjor==1)
{
gpioe->ODR &= (~(0x1 << 10));
}
if(kminjor==2)
{
gpiof->ODR &= (~(0x1 << 10));
}
if(kminjor==3)
{
gpioe->ODR &= (~(0x1 << 8));
}
}
return size;
}
int k_close (struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
//初始化操作方法结构体
const struct file_operations fops=
{
.open=k_open,
.read=k_read,
.write=K_write,
.release=k_close,
};
//入口,安装驱动时执行
static int __init k_init(void)
{
int ret,i;
//分配字符设备驱动
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=-ENOMEM;
goto ERR2;
}
}
else
{
ret=alloc_chrdev_region(&devnum,1,count,CNAME);
if(ret)
{
printk("alloc_chrdev_region is error\n");
ret=-ENOMEM;
goto ERR2;
}
major=MAJOR(devnum);
minor=MINOR(devnum);
}
//驱动的注册
ret=cdev_add(cdev,MKDEV(major,minor),count);
if(ret)
{
printk("cdev add is error\n");
ret = -EIO;
goto ERR3;
}
//自动创建设备节点
cls = class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls))
{
printk("class create is error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for(i=1;i<=count;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
if(IS_ERR(dev))
{
printk("device create is error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
//RCC寄存器地址映射
rcc=ioremap(RCC,4);
if(NULL == rcc)
{
printk("rcc ioremap is error\n");
return -ENOMEM;
}
//GPIOE寄存器地址映射
gpioe=ioremap(GPIOE,sizeof(gpio_t));
if(NULL == gpioe)
{
printk("gpioe ioremap is error\n");
return -ENOMEM;
}
//GPIOF寄存器地址映射
gpiof=ioremap(GPIOF,sizeof(gpio_t));
if(NULL == gpiof)
{
printk("gpiof ioremap is error\n");
return -ENOMEM;
}
//LED1--->PE10引脚初始化
*rcc|= (0x1 << 4);
gpioe->MODER &= (~(0x3 << 20));
gpioe->MODER |= (0x1 << 20);
gpioe->ODR &= (~(0x1 << 10));
// LED2--->PF10初始化
*rcc|= (0x1 << 5);
gpiof->MODER &= (~(0x3 << 20));
gpiof->MODER |= (0x1 << 20);
gpiof->ODR &= (~(0x1 << 10));
//LED3--->PE8初始化
*rcc|= (0x1 << 4);
gpioe->MODER &= (~(0x3 << 16));
gpioe->MODER |= (0x1 << 16);
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 k_exit(void)
{
int i;
//销毁设备节点信息
for(i=1;i<=count;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//销毁目录信息
class_destroy(cls);
//驱动的注销
cdev_del(cdev);
//销毁设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//释放cdev结构体
kfree(cdev);
//取消地址映射
iounmap(rcc);
iounmap(gpioe);
iounmap(gpiof);
}
module_init(k_init);
module_exit(k_exit);
MODULE_LICENSE("GPL");
myled.h:
#ifndef __MYLED__H__
#define __MYLED__H__
typedef struct{
volatile unsigned int MODER;
volatile unsigned int OTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
}gpio_t;
#define GPIOE 0x50006000
#define GPIOF 0x50007000
#define RCC 0x50000A28
volatile unsigned int* rcc;
gpio_t* gpioe;
gpio_t* gpiof;
#endif
实验现象:
串口工具如图: