mycdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include "myled.h"
#define CNAME "myled"
struct cdev *cdev; // cdev结构体指针
dev_t devt; //申请的设备号
int major; //主设备号
int minor = 0; //次设备号
unsigned int count = 3; //设备个数
struct class *cls; //目录句柄,class结构体指针
struct device *dev; // device结构体指针
char kbuf[128] = {};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
volatile unsigned int *vir_rcc;
int mycdev_open(struct inode *inode, struct file *file)
{
dev_t i_rdev = inode->i_rdev;
dev_t min = MINOR(i_rdev);
file->private_data = (void*)min;
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 *loff)
{
int ret;
int min;
min = (int)file->private_data;
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("data copy failed\n");
return -EIO;
}
switch (min)
{
case 0:
if (kbuf[0] == '1')
vir_led1->ODR |= (1 << 10);
else if (kbuf[0] == '0')
vir_led1->ODR &= ~(1 << 10);
break;
case 1:
if (kbuf[0] == '1')
vir_led2->ODR |= (1 << 10);
else if (kbuf[0] == '0')
vir_led2->ODR &= ~(1 << 10);
break;
case 2:
if (kbuf[0] == '1')
vir_led3->ODR |= (1 << 8);
else if (kbuf[0] == '0')
vir_led3->ODR &= ~(1 << 8);
break;
}
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;
}
struct file_operations fops =
{
.open = mycdev_open,
.write = mycdev_write,
.release = mycdev_close,
};
//初始化函数
int leds_init(void)
{
// rcc物理地址映射
vir_rcc = ioremap(PHY_RCC_ADDR, 4);
if (vir_rcc == NULL)
{
printk("vir_rcc faild\n");
return -ENOMEM;
}
// led1物理地址映射
vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
if (vir_led1 == NULL)
{
printk("vir_led1 faild\n");
return -ENOMEM;
}
// led2物理地址映射
vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
if (vir_led2 == NULL)
{
printk("vir_led2 faild\n");
return -ENOMEM;
}
// led3物理地址映射
vir_led3 = ioremap(PHY_LED3_ADDR, sizeof(gpio_t));
if (vir_led3 == NULL)
{
printk("vir_led3 faild\n");
return -ENOMEM;
}
//初始化寄存器部分
// rcc
*vir_rcc |= (0x3 << 4);
// led1
vir_led1->MODER &= ~(0x3 << 20);
vir_led1->MODER |= (0x1 << 20);
vir_led1->ODR &= ~(0x1 << 10);
// led2
vir_led2->MODER &= ~(0x3 << 20);
vir_led2->MODER |= (0x1 << 20);
vir_led2->ODR &= ~(0x1 << 10);
// led3
vir_led3->MODER &= ~(0x3 << 16);
vir_led3->MODER |= (0x1 << 16);
vir_led3->ODR &= ~(0x1 << 8);
return 0;
}
//入口函数
static int __init mycdev_init(void)
{
int ret, i;
// 1.分配字符设备驱动
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory err\n");
goto ERR1;
}
printk("cdev success\n");
// 2.设备驱动初始化
cdev_init(cdev, &fops);
// 3.申请设备号
ret = alloc_chrdev_region(&devt, minor, count, CNAME);
if (ret)
{
printk("alloc dev_no failed\n");
goto ERR2;
}
major = MAJOR(devt);
minor = MINOR(devt);
printk("alloc devt success\n");
// 4.注册字符设备驱动
ret = cdev_add(cdev, MKDEV(major, minor), count);
if (ret)
{
printk("add is failed\n");
goto ERR3;
}
// 5.自动创建设备节点
//向上提交节点目录
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls))
{
printk("up submit dir failed\n");
return PTR_ERR(cls);
goto ERR4;
}
printk("up submit dir success\n");
//创建设备节点
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev))
{
printk("create jiedian failed\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
printk("create jiedian success\n");
//寄存器的初始化
leds_init();
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 -ENOMEM;
}
//出口函数
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);
//取消映射
iounmap(vir_led1);
iounmap(vir_led2);
iounmap(vir_led3);
iounmap(vir_rcc);
// 3.注销字符设备驱动
cdev_del(cdev);
// 4.释放设备号
unregister_chrdev_region(MKDEV(major, minor), count);
// 5.释放动态申请的空间
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
// GPL协议
MODULE_LICENSE("GPL");
串口工具输入:echo 1 > /dev/myled0
echo 1 > /dev/myled1
echo 1 > /dev/myled2
点亮灯
输入: echo 0 > /dev/myled0
echo 0 > /dev/myled1
echo 0 > /dev/myled2
熄灭灯
实验现象: