(Linux驱动学习 - 9).设备树下platform的LED驱动

news2024/11/17 9:42:40

一.platform相关结构体与函数

1.匹配列表 - struct of_device_id

struct of_device_id
{
    char name[32];
    char type[32];

    /* compatible 很重要,需要与设备树节点的 compatible 属性一致,才能匹配 */
    char compatible[128];        

    const void *data;
};

2.device_driver

struct device_driver 
{
    /* 设备名字,在不用设备树的情况下可以使用 name 匹配 */
    const char *name;            
    struct bus_type *bus;

    struct module *owner;
    const char *mod_name;         /* used for built-in modules */

    bool suppress_bind_attrs;     /* disables bind/unbind via sysfs */


    /* 设备树下的匹配列表结构体 */
    const struct of_device_id *of_match_table;


    const struct acpi_device_id *acpi_match_table;

    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    const struct attribute_group **groups;

    const struct dev_pm_ops *pm;

    struct driver_private *p;
};

3.平台驱动结构体 - struct platform_driver

struct platform_driver 
{
    /* 匹配成功后此函数会执行,将注册字符设备等内容放入这个函数,例如注册字符设备驱动、添加cdev、创建类等 */
    int (*probe)(struct platform_device *);

    /* 当关闭 platform 设备驱动的时候此函数会执行,把以前在 exit 中要做的事情放到这个里面,如删除cdev、注销设备号等 */
    int (*remove)(struct platform_device *);
    

    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);


    /* 用于匹配设备和 platform 驱动 */
    struct device_driver driver;


    const struct platform_device_id *id_table;
    bool prevent_deferred_probe;
};

4.声明设备匹配列表 - MODULE_DEVICE_TABLE

函数原型

/*
*    @description:         : 声明匹配列表
*    @param - type         : 设备类型,设备树下的设备就传入 of
*    @param - xxx_of_match : 匹配列表结构体
*/
MODULE_DEVICE_TABLE(type,struct of_device_id xxx_of_match);

5.向Linux内核注册一个 platform 驱动 - platform_driver_register

函数原型:

/**
 * @description:            向Linux内核注册一个 platform 驱动
 * @param - driver  :       要注册的 platform 驱动
 * @return          :       成功时返回(0),失败则返回(负数)
 */
int platform_driver_register(struct platform_driver *driver)

6.卸载 platform 驱动 - platform_driver_unregister

函数原型

/**
 * @description:            卸载 platform 驱动
 * @param - drv     :       要卸载的 platform 驱动
 * @return          :       无
 */
void platform_driver_unregister(struct platform_driver *drv)

二.platform下的LED实验

1.设备树

(1).流程图

        注意:compatible 属性部分要与匹配列表中的 compatible 部分一致

(2).设备树代码

2.驱动部分

(1).流程图

(2).驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/fs.h>


#define LEDDEV_CNT  1               /* 设备号个数 */
#define LEDDEV_NAME "dtsplatled"    /* 设备名字 */
#define LEDOFF      0               /* 关灯 */
#define LEDON       1               /* 开灯 */


/* leddev 设备结构体 */
struct leddev_dev
{
    dev_t devid;                /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    int major;                  /* 主设备号 */
    int minor;                  /* 次设备号 */
    struct device_node *node;   /* LED设备结点 */
    int led0;                   /* LED 灯的 GPIO 编号 */
};


/* led 设备 */
struct leddev_dev leddev;


/*
* @description : LED 打开/关闭
* @param - sta : LEDON(0) 打开 LED, LEDOFF(1) 关闭 LED
* @return : 无
*/
void led0_switch(u8 sta)
{
    if (sta == LEDON )
        gpio_set_value(leddev.led0, 0);
    else if (sta == LEDOFF)
        gpio_set_value(leddev.led0, 1);
}



/**
 * @description:            打开设备
 * @param - inode   :       传递给驱动的 inode
 * @param - filp    :       要打开的文件,file结构体有个private_data的成员变量,一般在open的时候将private_data指向设备结构体
 * @return          :       成功时返回(0),失败时返回(其他值)
 */
static int led_open(struct inode *inode,struct file *filp)
{
    /* 设置私有数据 */
    filp->private_data = &leddev;

    return 0;
}


/**
 * @description:            向设备写数据
 * @param - filp    :       设备文件,表示打开的文件描述符
 * @param - buf     :       要写入设备的数据
 * @param - cnt     :       要写入的字节数
 * @param - offt    :       相对于文件首地址的偏移量
 * @return          :       成功时返回(成功写入的字节数),失败时返回(负值)
 */
static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
    int retvalue;
    unsigned char databuf[0];
    unsigned char ledstat;

    retvalue = copy_from_user(databuf,buf,cnt);
    if(0 > retvalue)
    {
        printk("kernel write falied!\r\n");
        return -EFAULT;
    }

    ledstat = databuf[0];
    if(LEDON == ledstat)
    {
        led0_switch(LEDON);
    }
    else if(LEDOFF == ledstat)
    {
        led0_switch(LEDOFF);
    }

    return 0;
}


/* 绑定设备操作函数 */
static struct file_operations led_fops = 
{
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_write,
};



/**
 * @description:            platform 驱动的probe函数,当驱动与设备匹配以后此函数会执行
 * @param - dev     :       platform 设备
 * @return          :       成功时返回(0),失败时返回(负值) 
 */
static int led_probe(struct platform_device *dev)
{
    printk("led driver and device was matched!\r\n");

    /* 一.设置 LED 所使用的 GPIO */
    /* 1.获取设备结点 */
    leddev.node = of_find_node_by_path("/gpioled");

    /* 2.得到 GPIO 编号 */
    leddev.led0 = of_get_named_gpio(leddev.node,"led-gpio",0);

    /* 3.申请 gpio */
    gpio_request(leddev.led0,"led0");

    /* 4.设置gpio默认输出为高电平,关闭 LED 灯 */
    gpio_direction_output(leddev.led0,1);


    /* 二.注册字符设备驱动 */
    /* 1.创建设备号 */
    if(leddev.major)
    {
        leddev.devid = MKDEV(leddev.major,0);
        register_chrdev_region(leddev.devid,LEDDEV_CNT,LEDDEV_NAME);
    }
    else
    {
        alloc_chrdev_region(&leddev.devid,0,LEDDEV_CNT,LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
        printk("leddev major : %d\r\n",leddev.major);
    }

    /* 2.初始化 cdev */
    cdev_init(&leddev.cdev,&led_fops);

    /* 3.添加一个 cdev */
    cdev_add(&leddev.cdev,leddev.devid,LEDDEV_CNT);

    /* 4.创建类 */
    leddev.class = class_create(THIS_MODULE,LEDDEV_NAME);
    if(IS_ERR(leddev.class))
    {
        return PTR_ERR(leddev.class);
    }

    /* 5.创建设备 */
    leddev.device = device_create(leddev.class,NULL,leddev.devid,NULL,LEDDEV_NAME);
    if(IS_ERR(leddev.device))
    {
        return PTR_ERR(leddev.device);
    }

    return 0;
}


/**
 * @description:            platform驱动的remove函数,移除platform驱动的时候会执行此函数
 * @param - dev     :       platform 设备
 * @return          :       成功时返回(0),失败时返回(负值)
 */
static int led_remove(struct platform_device *dev)
{
    gpio_set_value(leddev.led0,1);              //卸载驱动的时候关闭 LED 灯

    /* 注销字符设备驱动 */
    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid,LEDDEV_CNT);

    device_destroy(leddev.class,leddev.devid);
    class_destroy(leddev.class);

    return 0;
}


/* 匹配列表 */
static struct of_device_id led_of_match[] =
{
    {.compatible = "atkalpha-gpioled"},
    {}
};


/* platform 驱动结构体 */
static struct platform_driver led_driver = 
{
    .driver = 
    {
        .name = "imx6ul-led",               //驱动名字,用于无设备树时的匹配
        .of_match_table = led_of_match,     //设备树匹配列表
    },

    .probe = led_probe,
    .remove = led_remove,
};



/**
 * @description:            驱动入口函数
 * @param -         :       无
 * @return          :       
 */
static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}


/**
 * @description:            驱动出口函数
 * @param           :       无
 */
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}



module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2200841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dfs 判重Sequence one——hdu 2610

目录 前言 搜索算法判重 map判重 set判重 Sequence one 问题描述 输入 输出 数据范围 样例 问题分析 重构dfs参数 递减&#xff0c;不重复 去重的优化 最终代码 前言 搜索算法判重 搜索算法判重有很多种方法&#xff0c;常见的有两种&#xff0c;map判重和set判重…

模运算和快速幂

文章目录 模运算快速幂 模运算 模运算是大数运算中的常用操作。如果一个数太大&#xff0c;无法直接输出&#xff0c;或者不需要直接输出&#xff0c;则可以对它取模&#xff0c;缩小数值再输出。取模可以防止溢出&#xff0c;这是常见的操作。 取模运算一般要求a和m的符号一…

VCI_VBDSP使用教程-服务站

VCI_VBDSP使用教程-服务站 VBDSP软件压缩包请点击下载&#xff1a;(备注&#xff1a;将VBDSP软件压缩包做一个下载连接&#xff0c;放到此处) 教程视频&#xff1a;https://www.bilibili.com/video/BV19eHpeeEiz/?spm_id_from333.999.0.0&vd_source224b4434f72960113bc97…

数组的定义与使用(二)

2. 数组是引用类型 2.1初识JVM的内存分布 内存是一段连续的存储空间&#xff0c;主要用来存储程序运行时数据的。比如&#xff1a; 程序运行时代码需要加载到内存程序运行产生的中间数据要存放在内存程序中的常量也要保存有些数据可能需要长时间储存&#xff0c;有些数据当方…

PCL 平面点云边界特征提取(alpha shapes)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 点云边界提取 2.1.2 可视化点云与边界 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&a…

07:(寄存器开发)串口通信

串口通信 1、串口简介2、串口通讯协议3、硬件外设4、发送数据5、使用轮询的方式接收数据&#xff08;USART1&#xff09;6、使用中断的方式接收数据7、串口进行printf重定向 1、串口简介 串口通讯&#xff08;Serial Communication&#xff09;是一种设备间非常常用的串行通讯方…

后端增删改查的基本应用——一个简单的货物管理系统

最终效果&#xff0c;如图所示&#xff1a; 如果想要进行修改操作&#xff0c;可点击某栏修改选项&#xff0c;会在本表格下方弹出修改的具体操作界面&#xff08;点击前隐藏&#xff09;&#xff0c;并且目前的信息可复现在修改框内。 本篇文章通过该项目将后端和前端结合起来…

java内存控制

Java 内存控制是一个相对复杂但至关重要的主题&#xff0c;它涉及到如何高效地管理Java应用程序中的内存资源。在Java中&#xff0c;内存管理主要由Java虚拟机&#xff08;JVM&#xff09;负责&#xff0c;包括内存的分配和回收。尽管如此&#xff0c;作为开发者&#xff0c;我…

2025年5月高项,从0备考信息系统项目管理师 | 备考经验全攻略分享

在逐步摸索备考信息系统项目管理师的过程中&#xff0c;我总结了很多关于班课资料和学习经验&#xff0c;现在与大家分享。&#xff08;全文约3k字&#xff0c;阅读用时约5min&#xff09; 这篇分享帖不仅告诉你关于备考信息系统项目管理师实用的班课资料&#xff0c;还有学习…

Win11 23H2 10月正式版:22631.4317 镜像免费下载!

今日&#xff0c;系统之家小编给您带来2024年10月最新更新的Windows11 23H2正式版系统下载&#xff0c;该版本系统基于微软官方最新Windows11 23H2 22631.4317专业版展开离线制作&#xff0c;没有病毒残留&#xff0c;且能完美支持新老机型&#xff0c;安装后&#xff0c;系统版…

【概率论】泊松分布

泊松分布 若 &#xff0c;则 归一性 例子 泊松分布多出现在当X表示一定时间或一定空间内出现的事件的个数这种场合&#xff0c;如在一定时间内某交通路口所发生的事故的个数。 将泊松分布假设为二项分布 假设条件: &#xff08;1&#xff09;泊松分布一般为一段时间或一…

★ 算法OJ题 ★ 二分查找算法

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;塞尔达将和大家一起做几道二分查找算法算法题 ~ ❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️ 澄岚主页&#xff1a;椎名澄嵐-CSDN博客 算法专栏&#xff1a;★ 优选算法100天 ★_椎名澄嵐的博客-CSDN博客…

STM32 SPI串行总线

目录 STM32的SPI通信原理 SPI串行总线概述 SPI串行总线互连方式 STM32F1 SPI串行总线的工作原理 SPI串行总线的特征 SPI串行总线的内部结构 SPI串行总线时钟信号的相位和极性 STM32的SPI接口配置 STM32的SPI接口数据发送与接收过程 SPI的HAL 驱动函数 STM32的SPI通信…

靶标弹孔检测系统源码分享

靶标弹孔检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

apt update报错:ModuleNotFoundError: No module named ‘apt_pkg‘(可能是默认python版本被改坏了)

文章目录 错误信息分析1. 确保 apt_pkg 模块已安装2. 检查 Python 版本3. 重新配置 Python4. 修复损坏的依赖5. 检查环境变量 尝试 错误信息 (base) rootkyai:/ky/tml/ky_ai_get_server_info# apt update 获取:1 file:/var/cuda-repo-cross-aarch64-ubuntu2004-11-4-local InR…

【Python】如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志)

如何让SQL Server像MySQL一样拥有慢查询日志&#xff08;Slow Query Log慢日志&#xff09; SQL Server一直以来被人诟病的一个问题是缺少了像MySQL的慢日志功能&#xff0c;程序员和运维无法知道数据库过去历史的慢查询语句。 因为SQLServer默认是不捕获过去历史的长时间阻塞…

inBuilder低代码平台新特性推荐-第二十五期

今天来给大家带来的是inBuilder低代码平台社区版中的特性推荐系列第二十五期——选人组件扩展&#xff01; 一、概述 inBuilder低代码平台社区版的开发过程中&#xff0c;选人组件支持tab页中增加扩展页面&#xff0c;由二开人员根据业务场景实现自定义取数接口和页面展示形式…

【笔记】济南,天命人,春秋

孤独而高傲的济南人 浩克山东知天命热爱的sensei 浩克山东 哦哦&#xff0c;最高的大葱也是济南的了&#xff0c;这大葱&#xff0c;比一般人要高呢&#xff0c;尽管济南的朋友们也都个子不矮。。能想像的到两米高的米库。。。。 然而在这块地界&#xff0c;遇到个人&#xf…

基于STM32的简易交通灯proteus仿真设计(仿真+程序+设计报告+讲解视频)

基于STM32的简易交通灯proteus仿真设计(仿真程序设计报告讲解视频&#xff09; 仿真图proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;C0091 **1.**主要功能 功能说明&#xff1a; 以STM32单片机和数码管、LED灯设计简易交通…

版本控制系统Helix Core的常见使用误区及解决办法、实用工具及新功能介绍

日前&#xff0c;Perforce携手合作伙伴龙智一同亮相Unreal Fest 2024上海站&#xff0c;分享Helix Core版本控制系统及其协作套件的强大功能与最新动态&#xff0c;助力游戏创意产业加速前行。 Perforce解决方案工程师Kory Luo在活动主会场&#xff0c;带来《Perforce Helix C…