代码:
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define DTSLED_CNT 1 // 设备号个数
#define DTSLED_NAME "dtsled" // 名字
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;
void led_unmap(void){
/* 取消映射 */
iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);
iounmap(PMU_GRF_GPIO0C_DS_0_PI);
iounmap(GPIO0_SWPORT_DR_H_PI);
iounmap(GPIO0_SWPORT_DDR_H_PI);
}
/*dtsled设备结构体*/
struct dtsled_dev{
dev_t devid; // 设备号
struct cdev cdev; // 字符设备
struct class* class; // 类
struct device* device; // 设备
int major; // 主设备号
int minor; // 次设备号
struct device_node *nd; // 设备节点
};
struct dtsled_dev dtsled; // led设备
void led_switch(u8 sta)
{
u32 val = 0;
if(sta == LEDON) {
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,
bit0,高电平*/
writel(val, GPIO0_SWPORT_DR_H_PI);
}else if(sta == LEDOFF) {
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,
bit0,低电平 */
writel(val, GPIO0_SWPORT_DR_H_PI);
}
}
static int dtsled_open(struct inode* inode, struct file* filp){
filp->private_data = &dtsled;
return 0;
}
static int dtsled_release(struct inode* inode, struct file* filp){
//struct dtsled_dev* dev = (struct dtsled_dev* )filp->private_data;
return 0;
}
static ssize_t dtsled_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos){
//struct dtsled_dev* dev = (struct dtsled_dev* )filp->private_data;
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, count);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
led_switch(LEDON); /* 打开LED灯 */
} else if(ledstat == LEDOFF) {
led_switch(LEDOFF); /* 关闭LED灯 */
}
return 0;
}
// dtsled 字符设备操作集
static const struct file_operations dtsled_fops = {
.owner = THIS_MODULE,
.write = dtsled_write,
.open = dtsled_open,
.release = dtsled_release,
};
/*入口函数*/
static int __init dtsled_init(void){
int ret = 0;
const char* str;
u32 regdata[16];
int i = 0;
unsigned int val = 0;
// 注册字符设备,新的开发方式
dtsled.major = 0; // 设备号由内核分配
if(dtsled.major){ // 定义了设备号
dtsled.devid = MKDEV(dtsled.major, 0);
ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
}else{ // 没有给定设备号
ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
dtsled.major = MAJOR(dtsled.devid);
dtsled.minor = MINOR(dtsled.minor);
}
if(ret < 0){
printk("dtsled init error \r\n");
return -1;
}
// 添加字符设备
dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev, &dtsled_fops); // 初始化
ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT); // 添加到内核
if(ret < 0){
printk("dtsled add failed \r\n");
unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号
return -1;
}
// 自动创建设备节点
dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); // 创建类
if(IS_ERR(dtsled.class)){
ret= PTR_ERR(dtsled.class);
cdev_del(&dtsled.cdev); // 删除设备
unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号
return -1;
}
dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
if(IS_ERR(dtsled.device)){
ret = PTR_ERR(dtsled.device);
cdev_del(&dtsled.cdev); // 删除设备
unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 释放设备号
class_destroy(dtsled.class); // 删除类
return -1;
}
/*获取设备树属性函数*/
dtsled.nd = of_find_node_by_path("/alphaled");
if(dtsled.nd == NULL){
ret = -EINVAL;
printk("find node by path error\r\n");
return -1;
}
ret = of_property_read_string(dtsled.nd, "status", &str);
if(ret < 0){
printk("get status failed!\r\n");
}else{
printk("status= %s \r\n", str);
}
ret = of_property_read_string(dtsled.nd, "compatible", &str);
if(ret < 0){
printk("get compatible failed!\r\n");
}else{
printk("compatible= %s \r\n", str);
}
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 16);
if(ret < 0){
printk("read reg error \r\n");
cdev_del(&dtsled.cdev); // 删除字符设备
unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 删除设备号
device_destroy(dtsled.class, dtsled.devid); // 删除设备 (设备用到类)
class_destroy(dtsled.class); // 删除类
return -1;
}else{
printk("reg data: \r\n");
for(i = 0; i < 16; i ++){
printk("%#X ", regdata[i]);
}
printk("\r\n");
}
/* LED灯初始化 */
// 内存映射
PMU_GRF_GPIO0C_IOMUX_L_PI = of_iomap(dtsled.nd, 0);
PMU_GRF_GPIO0C_DS_0_PI = of_iomap(dtsled.nd, 1);
GPIO0_SWPORT_DR_H_PI = of_iomap(dtsled.nd, 2);
GPIO0_SWPORT_DDR_H_PI = of_iomap(dtsled.nd, 3);
// 初始化io
/* 2、设置GPIO0_C0为GPIO功能。*/
val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);
val &= ~(0X7 << 0); /* bit2:0,清零 */
val |= ((0X7 << 16) | (0X0 << 0)); /* bit18:16 置1,允许写bit2:0,
bit2:0:0,用作GPIO0_C0 */
writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);
/* 3、设置GPIO0_C0驱动能力为level5 */
val = readl(PMU_GRF_GPIO0C_DS_0_PI);
val &= ~(0X3F << 0); /* bit5:0清零*/
val |= ((0X3F << 16) | (0X3F << 0)); /* bit21:16 置1,允许写bit5:0,
bit5:0:0,用作GPIO0_C0 */
writel(val, PMU_GRF_GPIO0C_DS_0_PI);
/* 4、设置GPIO0_C0为输出 */
val = readl(GPIO0_SWPORT_DDR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,
bit0,高电平 */
writel(val, GPIO0_SWPORT_DDR_H_PI);
/* 5、设置GPIO0_C0为低电平,关闭LED灯。*/
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,
bit0,低电平 */
writel(val, GPIO0_SWPORT_DR_H_PI);
return 0;
}
/*出口*/
static void __exit dtsled_exit(void){
led_switch(LEDOFF);
led_unmap(); // 取消地址映射
cdev_del(&dtsled.cdev); // 删除字符设备
unregister_chrdev_region(dtsled.devid, DTSLED_CNT); // 删除设备号
device_destroy(dtsled.class, dtsled.devid); // 删除设备 (设备用到类)
class_destroy(dtsled.class); // 删除类
}
/* 注册驱动和卸载驱动 */
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Narnat");
代码中通过读取alphaled节点数据,alphaled中存放的是led灯引脚地址,这样led灯引脚发生变化,只需要更改设备树中地址的具体值,无需更改代码
结果: