通过字符设备驱动分步注册过程实现LED驱动的编写
mychrdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "head.h"
unsigned int major; // 保存申请的主设备号
unsigned int minor; // 保存申请的次设备号
char kbuf[128]={0};
dev_t devno;
struct class *cls;
struct device *dev;
struct cdev *cdev;
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
// 封装常用的操作方法
int mychrdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *lof)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
unsigned int ret;
ret=copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("copy_to_user err\n");
return -ret;
}
return 0;
}
ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lof)
{
//ubuf就是应用程序中write函数第二个参数传过来的值
//size就是应用程序中write函数第三个参数传过来的值
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
unsigned int ret;
ret=copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("copy_from_user err\n");
return -ret;
}
if(kbuf[0]=='0')//关灯
{
vir_led1->ODR &=(~(0x1<<10));
vir_led2->ODR &=(~(0x1<<10));
vir_led3->ODR &=(~(0x1<<8));
}
else if(kbuf[0]=='1')//开led1灯
{
vir_led1->ODR|=(0x1<<10);
}
else if(kbuf[0]=='2')//开led2灯
{
vir_led2->ODR|=(0x1<<10);
}
else if(kbuf[0]=='3')//开led3灯
{
vir_led3->ODR|=(0x1<<8);
}
return 0;
}
int mychrdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
struct file_operations fops = {
.open=mychrdev_open,
.release=mychrdev_close,
.read=mychrdev_read,
.write=mychrdev_write,
};
void all_led_init(void)
{
// 注册字符设备驱动
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0)
{
printk("注册字符设备驱动失败%d\n", __LINE__);
}
printk("注册字符设备驱动成功 major=%d\n", major);
//进行LED寄存器的地址映射
vir_led1 =ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
if(vir_led1==NULL)
{
printk("地址映射失败%d\n",__LINE__);
return 0;
}
vir_led2 =ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
if(vir_led2==NULL)
{
printk("地址映射失败%d\n",__LINE__);
return 0;
}
vir_led3 =ioremap(PHY_LED3_ADDR,sizeof(gpio_t));
if(vir_led3==NULL)
{
printk("地址映射失败%d\n",__LINE__);
return 0;
}
//GPIOF
vir_rcc =ioremap(PHY_RCC_ADDR,sizeof(gpio_t));
if(vir_rcc ==NULL)
{
printk("地址映射失败%d\n",__LINE__);
return 0;
}
printk("地址映射成功\n");
//硬件初始化
//使能时钟
(*vir_rcc) |= (0X3<<4);
//设置PE10为输出
vir_led1->MODER &= (~(0X3<<20));
vir_led1->MODER |= (0X1<<20);
//设置PF10为输出
vir_led2->MODER &= (~(0X3<<20));
vir_led2->MODER |= (0X1<<20);
//设置PE8为输出
vir_led3->MODER &= (~(0X3<<16));
vir_led3->MODER |= (0X1<<16);
//默认输出低电平
vir_led1->ODR &=(~(0x1<<10));
vir_led2->ODR &=(~(0x1<<10));
vir_led3->ODR &=(~(0x1<<8));
return 0;
}
static int __init mycdev_init(void)
{
int ret;
//分配字符设备驱动对象
cdev = cdev_alloc();
if(cdev == NULL)
{
return -EFAULT;
}
printk("字符设备驱动对象申请成功\n");
//初始化驱动对象
cdev_init(cdev,&fops);
//申请设备号
if(major == 0) //动态申请
{
ret=alloc_chrdev_region(&devno,minor,3,"myled");
if(ret)
{
printk("动态申请设备号失败\n");
goto out1;
}
major=MAJOR(devno);
minor=MINOR(devno);
}
else //静态指定
{
ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
if(ret)
{
printk("动态申请设备号失败\n");
goto out1;
}
}
printk("动态申请设备号成功\n");
//注册驱动
ret = cdev_add(cdev,MKDEV(major,minor),3);
if(ret)
{
printk("注册驱动失败\n");
goto out2;
}
//向上提交目录
cls = class_create(THIS_MODULE,"led");
if(ret)
{
printk("向上提交目录失败\n");
ret=-PTR_ERR(cls);
goto out3;
}
printk("向上提交目录成功\n");
//向上提交设备节点
int i;
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 out3;
}
}
all_led_init();
return 0;
out4:
//销毁提交成功的设备信息
for(i=0;i<3;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//销毁目录
class_destroy(cls);
out3:
cdev_del(cdev);
out2:
unregister_chrdev_region(MKDEV(major,minor),3);
out1:
kfree(cdev);
return ret;
}
static void __exit mycdev_exit(void)
{
//取消内存映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_led3);
iounmap(vir_rcc);
//销毁提交成功的设备信息
int i;
for(i=0;i<3;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//销毁目录
class_destroy(cls);
cdev_del(cdev);
unregister_chrdev_region(MKDEV(major,minor),3);
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
head.h
#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct{
unsigned int MODER;
unsigned int OTYPER;
unsigned int OSPEEDR;
unsigned int PUPDR;
unsigned int IDR;
unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0x50006000
#define PHY_LED2_ADDR 0x50007000
#define PHY_LED3_ADDR 0x50006000
#define PHY_RCC_ADDR 0X50000A28
#endif
led.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[128] = {};
int fd = open("/dev/myled0", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
return -1;
}
while (1)
{
printf("请输入控制码:1(led1开灯) 2(led2开灯) 3(led3开灯) 0(关灯)>");
fgets(buf, sizeof(buf), stdin); // 从终端输入一个数据
buf[strlen(buf) - 1] = '\0';
write(fd, buf, sizeof(buf));
}
close(fd);
return 0;
}