当前位置: 首页 > news >正文

i.MAX6ULL Linux LED 字符设备驱动代码分析

概述


        这是一个简单的针对特定硬件平台的LED字符设备驱动,通过文件操作接口控制GPIO来控制LED状态,仅供参考。

头文件包含


#include <linux/init.h>        // 模块初始化和退出
#include <linux/printk.h>      // 内核打印
#include <linux/types.h>       // 数据类型
#include <linux/fs.h>          // 文件系统
#include <linux/cdev.h>        // 字符设备
#include <linux/export.h>      // 模块导出
#include <linux/kdev_t.h>      // 设备号
#include <linux/device.h>      // 设备类
#include <asm/io.h>            // IO操作
#include <asm/string.h>        // 字符串操作
#include <asm/uaccess.h>       // 用户空间数据访问

宏定义和设备参数


#define MAJOR_NUM 248         // 主设备号
#define MINOR_NUM 0           // 次设备号
#define DEV_NAME "led"        // 设备名称
#define CLASS_NAME "led_class" // 设备类名称

// GPIO寄存器物理地址
#define GPIO1_SW_MUX_CTL 0x20e0068  // 复用控制寄存器
#define GPIO1_SW_PAD_CTL 0x20e02f4  // 引脚控制寄存器  
#define GPIO1_DIR 0x209c004         // 方向控制寄存器
#define GPIO1_DATA 0x209c000        // 数据寄存器

全局变量


static volatile unsigned long * sw_mux;    // 复用控制寄存器虚拟地址
static volatile unsigned long * sw_pad;    // 引脚控制寄存器虚拟地址
static volatile unsigned long * gpio1_dir; // 方向控制寄存器虚拟地址
static volatile unsigned long * gpio1_data;// 数据寄存器虚拟地址

static dev_t devno;            // 设备号
static struct cdev cdev;       // 字符设备结构体
static struct class *led_class;// 设备类指针

硬件控制函数

LED硬件初始化


static void led_hardware_init(void)
{
    *sw_mux &= ~(0xf << 0);     // 清除低4位
    *sw_mux |= (0x5 << 0);      // 设置为GPIO功能(0x5)
    *sw_pad = 0x10B0;           // 设置引脚电气特性
    *gpio1_dir |= (1 << 3);     // 设置GPIO1_3为输出模式
    *gpio1_data |= (1 << 3);    // 初始化为高电平(LED灭)
}

LED控制函数


// 打开LED(低电平点亮)
static void led_on(void)
{
    *gpio1_data &= ~(1 << 3);   // 清除第3位,输出低电平
}

// 关闭LED(高电平熄灭)  
static void led_off(void)
{
    *gpio1_data |= (1 << 3);    // 设置第3位,输出高电平
}

文件操作接口

打开设备


static int open(struct inode * node, struct file * file)
{
    led_hardware_init();        // 初始化LED硬件
    printk("kernel led open   ...\n");
    return 0;
}

读取设备


static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * loff)
{
    printk("kernel led read   ...\n");
    return 0;  // 未实现实际读取功能
}

写入设备


static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * loff)
{
    int ret = 0;
    unsigned char data[10] = {0};
    // 安全拷贝,防止缓冲区溢出
    unsigned int len_cp = len < sizeof(data) ? len : sizeof(data);
    
    ret = copy_from_user(data, buf, len_cp);  // 从用户空间拷贝数据
    
    // 根据命令控制LED
    if(!strcmp(data, "ledon"))
        led_on();
    else if(!strcmp(data, "ledoff"))
        led_off();
    else
        ret = -EINVAL;  // 无效命令

    printk("kernel led write   ...\n");
    return len_cp;

关闭设备


static int close(struct inode * node, struct file * file)
{
    printk("kernel led close   ...\n");
    return 0;
}

文件操作结构体


static struct file_operations fops = 
{
    .owner = THIS_MODULE,
    .open = open,
    .read = read,
    .write = write,
    .release = close
};

模块初始化函数


static int __init led_init(void)
{
    int ret = 0;
    
    // 1. 设备号分配和注册
    devno = MKDEV(MAJOR_NUM, MINOR_NUM);
    ret = register_chrdev_region(devno, 1, DEV_NAME);
    if(ret < 0) {
        // 静态分配失败则动态分配
        ret = alloc_chrdev_region(&devno, MINOR_NUM, 1, DEV_NAME);
        if(ret < 0)
            goto err_register_chrdev;
    }
    
    // 2. 字符设备初始化
    cdev_init(&cdev, &fops);
    ret = cdev_add(&cdev, devno, 1);
    if(ret < 0)
        goto err_cdev_add;
        
    // 3. 创建设备类和设备节点
    led_class = class_create(THIS_MODULE, CLASS_NAME);
    if(IS_ERR(led_class)) {
        ret = PTR_ERR(led_class);
        led_class = NULL;
        goto err_class;
    }
    
    if(IS_ERR(device_create(led_class, NULL, devno, NULL, DEV_NAME))) {
        ret = PTR_ERR(device_create(led_class, NULL, devno, NULL, DEV_NAME));
        goto err_device;
    }
    
    // 4. 内存映射GPIO寄存器
    sw_mux = ioremap(GPIO1_SW_MUX_CTL, 4);
    sw_pad = ioremap(GPIO1_SW_PAD_CTL, 4);
    gpio1_dir = ioremap(GPIO1_DIR, 4);
    gpio1_data = ioremap(GPIO1_DATA, 4);
    
    printk("###########################################   led_init!\n");
    printk("Auto create device node: /dev/%s\n", DEV_NAME);
    return 0;

// 错误处理
err_device:
    class_destroy(led_class);
    led_class = NULL;
err_class:
    cdev_del(&cdev);
err_cdev_add:
    unregister_chrdev_region(devno, 1);
err_register_chrdev:
    printk("led_init failed ret = %d\n", ret);
    return ret;
}

模块退出函数


static void __exit led_exit(void)
{
    // 1. 销毁设备节点和类
    device_destroy(led_class, devno);
    class_destroy(led_class);
    led_class = NULL;
    
    // 2. 释放内存映射
    iounmap(gpio1_dir);
    iounmap(gpio1_data);
    iounmap(sw_pad);
    iounmap(sw_mux);
    
    // 3. 注销字符设备和设备号
    cdev_del(&cdev);
    unregister_chrdev_region(devno, 1);
    
    printk("###########################################   led_exit!\n");
}

模块声明


module_init(led_init);  // 指定模块初始化函数
module_exit(led_exit);  // 指定模块退出函数

关键特性总结

1. “字符设备驱动”:实现了标准的文件操作接口
2. “GPIO控制”:通过内存映射访问硬件寄存器
3. “用户空间接口”:支持通过write系统调用控制LED
4. “命令格式”:
   - `"ledon"` - 打开LED
   - `"ledoff"` - 关闭LED
5. “自动创建设备节点”:在`/dev/led`创建设备文件
6. “错误处理”:完整的错误处理机制和资源清理

使用方式


# 加载模块
insmod led_driver.ko

# 控制LED
echo "ledon" > /dev/led    # 打开LED
echo "ledoff" > /dev/led   # 关闭LED

# 卸载模块  
rmmod led_driver

http://www.dtcms.com/a/511732.html

相关文章:

  • Linux中基数树的初始化
  • 4.3 二维数组
  • 【C语言实战(40)】C语言查找算法:从基础到实战的效率进阶
  • 洛谷 P2949 [USACO09OPEN] Work Scheduling G
  • 建站公司杭州南宁制作网站服务商
  • Deepseek-ocr论文精读
  • 【完整源码+数据集+部署教程】【文件&发票】发票信息提取系统源码&数据集全套:改进yolo11-ContextGuided
  • SpringBoot+Shiro+mybatis教务管理系统源码
  • 佛山个人制作网站公司手机百度下载免费安装
  • Git 项目开发核心指南:聚焦常用语法与完整流程
  • 【图像处理基石】遥感多光谱图像处理入门:从概念到实战(附Python代码)
  • Spring Boot项目中使用线程池并发插入6万条数据的线程池参数设置指南
  • 网站建设网站设计哪家专业东莞展馆设计公司
  • Docker Swarm:打造高效、可扩展的容器编排引擎,引领微服务新纪元(上)
  • 第15章:Spring AI Alibaba — 认识Graph框架
  • [Dify 实战] 构建一个自动发送邮件的插件:从 OpenAPI 到自动化通知
  • 基于Chrome140的FB账号自动化(关键词浏览)——脚本撰写(二)
  • CICD实战(8) - 使用Arbess+GitLab实现React.js项目自动化部署
  • 小程序uview actionSheet 内容过多高度设置
  • 基于.net的个人网站开发实录哪个网站建站比较好
  • 徐州做网站公司哪家好湘建网
  • 做头发个人网站制作素材专业网站设计制作服务
  • Linux初识进程
  • c#using Oracle.ManagedDataAccess.Client 批量保存数据
  • 人大金仓数据库kingbase8创建表示例
  • oracle包编译错误
  • 函数指针 指针函数 数组指针 指针数组 常量指针 指针常量
  • sqoop采集完成后导致hdfs数据与Oracle数据量不符的问题。怎么解决?
  • 洛阳有做网站开发的吗平台网站建设源码
  • 从零开始的C++学习生活 12:AVL树全面解析