STM32MP157驱动开发——platform设备驱动(中)

  • 0.前言
  • 一、platform设备模块——设备信息解析
  • 二、platform驱动模块——加载设备
  • 三、测试App
  • 四、编译及运行

相关文章:STM32MP157驱动开发——platform设备驱动(上)

0.前言

  在上一节中,对platform平台设备驱动的原理以及相关知识进行了一些了解。本节就使用 platform 驱动框架来编写一个 LED 设备驱动,其中包括一个驱动模块和一个设备模块,驱动模块是 platform 驱动程序,设备模块是 platform 的设备信息,当这两个模块都加载成功以后就会匹配成功,然后 platform驱动模块中的 probe 函数就会执行,probe 函数中就是传统的字符设备驱动。注:这种驱动开发方式是无设备树的Linux内核所使用的,在使用设备树模式的新版本Linux内核中不再使用,在此只是为了理解以及兼顾旧版本的项目使用,另外,一些RTOS中的驱动开发也会用这种方式。

一、platform设备模块——设备信息解析

leddevice.c:

#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_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <inux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>/* 寄存器物理地址 */
#define PERIPH_BASE (0x40000000)
#define MPU_AHB4_PERIPH_BASE (PERIPH_BASE + 0x10000000)
#define RCC_BASE (MPU_AHB4_PERIPH_BASE + 0x0000)
#define RCC_MP_AHB4ENSETR (RCC_BASE + 0XA28)
#define GPIOI_BASE (MPU_AHB4_PERIPH_BASE + 0xA000)
#define GPIOI_MODER (GPIOI_BASE + 0x0000)
#define GPIOI_OTYPER (GPIOI_BASE + 0x0004)
#define GPIOI_OSPEEDR (GPIOI_BASE + 0x0008)
#define GPIOI_PUPDR (GPIOI_BASE + 0x000C)
#define GPIOI_BSRR (GPIOI_BASE + 0x0018)
#define REGISTER_LENGTH 4/*设备资源信息,也就是LED0所使用的寄存器*/
static struct resource led_resources[] = {[0] = {.start = RCC_MP_AHB4ENSETR,.end = (RCC_MP_AHB4ENSETR + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},[1] = {.start = GPIOI_MODER,.end = (GPIOI_MODER + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},[2] = {.start = GPIOI_OTYPER,.end = (GPIOI_OTYPER + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},[3] = {.start = GPIOI_OSPEEDR,.end = (GPIOI_OSPEEDR + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},[4] = {.start = GPIOI_PUPDR,.end = (GPIOI_PUPDR + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},[5] = {.start = GPIOI_BSRR,.end = (GPIOI_BSRR + REGISTER_LENGTH - 1),.flags = IORESOURCE_MEM,},
};/*release程序,释放 flatform 设备模块的时候此函数会执行*/
static void led_release(struct device *dev)
{printk("led device released!\r\n");
}/*platform 设备结构体*/
static struct platform_device leddevice = {.name = "stm32mp1-led",.id = -1,.dev = {.release = &led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};/*设备模块加载*/
static int __init leddevice_init(void)
{return platform_device_register(&leddevice);
}/*设备模块注销*/
static void __exit leddevice_exit(void)
{platform_device_unregister(&leddevice);
}module_init(leddevice_init);
module_exit(leddevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("amonter");
MODULE_INFO(intree, "Y");

① led_resources 数组,也就是设备资源,描述了 LED 所要使用到的寄存器信息,也就是 IORESOURCE_MEM 资源
②platform 设备结构体变量 leddevice,这里要注意 name 字段为“stm32mp1-led”,所以稍后编写 platform 驱动中的 name 字段也要为“stm32mp1-led”,否则设备和驱动会匹配失败
③设备模块加载函数 leddevice_init,在此函数里面通过 platform_device_register 向 Linux 内核注册 leddevice 这个 platform 设备
④设备模块卸载函数 leddevice_exit,在此函数里面通过 platform_device_unregister 从 Linux内核中删除掉 leddevice 这个 platform 设备

二、platform驱动模块——加载设备

leddriver.c:

#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_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define LEDDEV_CNT 1          /* 设备号长度 */
#define LEDDEV_NAME "platled" /* 设备名字 */
#define LEDOFF 0
#define LEDON 1/* 映射后的寄存器虚拟地址指针 */
static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;/* leddev 设备结构体 */
struct leddev_dev
{dev_t devid;           /* 设备号 */struct cdev cdev;      /* cdev */struct class *class;   /* 类 */struct device *device; /* 设备 */
};struct leddev_dev leddev;/*LED打开、关闭*/
void led_switch(u8 sta)
{u32 val = 0;if (sta == LEDON){val = readl(GPIOI_BSRR_PI);val |= (1 << 16);writel(val, GPIOI_BSRR_PI);}else if (sta == LEDOFF){val = readl(GPIOI_BSRR_PI);val |= (1 << 0);writel(val, GPIOI_BSRR_PI);}
}/*取消映射*/
void led_unmap(void)
{/* 取消映射 */iounmap(MPU_AHB4_PERIPH_RCC_PI);iounmap(GPIOI_MODER_PI);iounmap(GPIOI_OTYPER_PI);iounmap(GPIOI_OSPEEDR_PI);iounmap(GPIOI_PUPDR_PI);iounmap(GPIOI_BSRR_PI);
}/*打开设备*/
static int led_open(struct inode *inode, struct file *filp)
{return 0;
}/*向设备写数据*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);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;
}/* 设备操作函数 */
static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write,
};/*probe函数,即挂载设备函数*/
static int led_probe(struct platform_device *dev)
{int i = 0, ret;int ressize[6];u32 val = 0;struct resource *ledsource[6];printk("led driver and device has matched!\r\n");/* 1、获取资源 */for (i = 0; i < 6; i++){ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i);if (!ledsource[i]){dev_err(&dev->dev, "No MEM resource for always on\n");return -ENXIO;}ressize[i] = resource_size(ledsource[i]);}/* 2、初始化 LED *//* 寄存器地址映射 */MPU_AHB4_PERIPH_RCC_PI = ioremap(ledsource[0]->start, ressize[0]);GPIOI_MODER_PI = ioremap(ledsource[1]->start, ressize[1]);GPIOI_OTYPER_PI = ioremap(ledsource[2]->start, ressize[2]);GPIOI_OSPEEDR_PI = ioremap(ledsource[3]->start, ressize[3]);GPIOI_PUPDR_PI = ioremap(ledsource[4]->start, ressize[4]);GPIOI_BSRR_PI = ioremap(ledsource[5]->start, ressize[5]);/* 3、使能 PI 时钟 */val = readl(MPU_AHB4_PERIPH_RCC_PI);val &= ~(0X1 << 8); /* 清除以前的设置 */val |= (0X1 << 8);  /* 设置新值 */writel(val, MPU_AHB4_PERIPH_RCC_PI);/* 4、设置 PI0 通用的输出模式。 */val = readl(GPIOI_MODER_PI);val &= ~(0X3 << 0); /* bit0:1 清零 */val |= (0X1 << 0);  /* bit0:1 设置 01 */writel(val, GPIOI_MODER_PI);/* 5、设置 PI0 为推挽模式。 */val = readl(GPIOI_OTYPER_PI);val &= ~(0X1 << 0); /* bit0 清零,设置为上拉*/writel(val, GPIOI_OTYPER_PI);/* 6、设置 PI0 为高速。 */val = readl(GPIOI_OSPEEDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零 */val |= (0x2 << 0);  /* bit0:1 设置为 10 */writel(val, GPIOI_OSPEEDR_PI);/* 7、设置 PI0 为上拉。 */val = readl(GPIOI_PUPDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零 */val |= (0x1 << 0);  /*bit0:1 设置为 01 */writel(val, GPIOI_PUPDR_PI);/* 8、默认关闭 LED */val = readl(GPIOI_BSRR_PI);val |= (0x1 << 0);writel(val, GPIOI_BSRR_PI);/* 注册字符设备驱动 *//* 1、申请设备号 */ret = alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);if (ret < 0)goto fail_map;/* 2、初始化 cdev */leddev.cdev.owner = THIS_MODULE;cdev_init(&leddev.cdev, &led_fops);/* 3、添加一个 cdev */ret = cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);if (ret < 0)goto del_unregister;/* 4、创建类 */leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)){goto del_cdev;}/* 5、创建设备 */leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)){goto destroy_class;}return 0;destroy_class:class_destroy(leddev.class);
del_cdev:cdev_del(&leddev.cdev);
del_unregister:unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
fail_map:led_unmap();return -EIO;
}/*remove函數*/
static int led_remove(struct platform_device *dev)
{led_unmap();                                        /* 取消映射 */cdev_del(&leddev.cdev);                             /* 删除 cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号*/device_destroy(leddev.class, leddev.devid);         /* 注销设备 */class_destroy(leddev.class);                        /* 注销类 */return 0;
}/*platform驱动结构体*/
static struct platform_driver led_driver = {.driver = {.name = "stm32mp1-led", /* 驱动名字,用于和设备匹配 */},.probe = led_probe,.remove = led_remove,
};/*驱动模块加载函数*/
static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}/*驱动模块卸载函数*/
static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("amonter");
MODULE_INFO(intree, "Y");

①在设备操作函数集 led_fops 之前,为传统的字符设备驱动
②probe 函数,当设备和驱动匹配以后此函数就会执行,当匹配成功以后会在终端上输出“led driver and device has matched!”。在 probe 函数里面初始化 LED、注册字符设备驱动。也就是将原来在驱动加载函数里面做的工作全部放到 probe 函数里面完成
③remove 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等。也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中完成
④platform_driver 驱动结构体,注意 name 字段为"stm32mp1-led",和 leddevice.c 文件里面设置的设备 name 字段一致
⑤驱动模块加载函数,在此函数里面通过 platform_driver_register 向 Linux 内核注册 led_driver 驱动
⑥驱动模块卸载函数,在此函数里面通过 platform_driver_unregister 从 Linux内核卸载 led_driver 驱动

三、测试App

测试程序的功能比较简单,只需要操作打开和关闭LED。
led_App.c:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF   0
#define LEDON    1int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if (argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开 led 驱动 */fd = open(filename, O_RDWR);if (fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if (retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if (retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

四、编译及运行

与前面章节的Makefile不同,本节使用的驱动,Makefile文件需要添加两个目标输出文件,所以将obj-m 变量的值改为“leddevice.o leddriver.o”:

编译后会生成两个名为“leddevice.ko leddriver.ko”的驱动模块文件,另外再使用交叉编译工具链编译出测试App文件,即可用于驱动的运行及测试。

根文件系统中/sys/bus/platform/目录下保存着当前板子 platform 总线下的设备和驱动,其中 devices 子目录为 platform 设备,drivers 子目录为 plartofm 驱动。进入 devices 目录,就可以找到名为“stm32mp1-led”的设备,同样的,在 drivers 目录下,一定也会有一个“stm32mp1-led”的驱动文件,否则就会匹配失败。
  使用测试 App 对 /dev/platled 设备操作就可以控制LED的亮灭:./led_App /dev/platled 1,卸载驱动时需要分别对驱动和设备进行卸载。

STM32MP157驱动开发——platform设备驱动(中)相关推荐

  1. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

  2. Linux 设备驱动开发 —— platform 设备驱动

    一.platform总线.设备与驱动         在Linux 2.6 的设备驱动模型中,关心总线.设备和驱动3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反 ...

  3. STM32MP157驱动开发——蜂鸣器设备驱动

    STM32MP157驱动开发--蜂鸣器设备驱动 0.相关知识 一.驱动程序开发 1.设备树修改 2.启动程序编写 3.测试程序编写 二.编译及运行测试 0.相关知识   蜂鸣器常用于计算机.打印机.报 ...

  4. STM32MP157驱动开发——USB设备驱动

    STM32MP157驱动开发--USB设备驱动 一.简介 1.电气属性 2.USB OTG 3.STM32MP1 USB 接口简介 4.Type-C 电气属性 二.USB HOST 驱动开发 1.US ...

  5. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

  6. Linux驱动之platform设备驱动

    当我们在一块开发板上写好了驱动,但换一块不同芯片的开发板,我们就需要重新写一个驱动.其中主要是硬件连接也就是接口发生了改变,而软件框架几乎不用通用的.所以为了更加方便地移植,能够仅修改很小的内容就达到 ...

  7. I.MX6ULL ARM驱动开发---platfrom设备驱动

    引言   对IO进行最简单的读写操作,设备驱动都非常的简单.像I2C.SPI.LCD 等这些复杂外设的驱动就不能这么去写了,Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件 ...

  8. Linux驱动开发|块设备驱动

    块设备驱动 块设备驱动是 Linux 三大驱动类型之一,块设备驱动比字符设备驱动复杂得多,不同类型的存储设备又对应不同的驱动子系统,下面介绍块设备驱动框架及使用 一.块设备介绍 块设备是针对存储设备的 ...

  9. linux的驱动开发——字符设备驱动

    1.字符设备驱动 \qquad字符设备驱动是最基本,最常用的设备.它将千差万别的硬件设备采用统一的接口封装起来,屏蔽了硬件的差异,简化了应用层的操作. 2.描述所有字符设备的结构体 \qquad描述所 ...

最新文章

  1. 深度学习时间序列预测:GRU算法构建多变量时间序列预测模型+代码实战
  2. 关于前台调用后台事件__doPostBack函数
  3. Java 8:对速度3.0.1“森林”流ORM的更深入了解
  4. (原創) 如何使用C++/CLI读/写jpg檔? (.NET) (C++/CLI) (GDI+) (C/C++) (Image Processing)
  5. [Jobdu] 题目1527:首尾相连数组的最大子数组和
  6. Python入门(04) -- 函数
  7. FreeRTOS源码分析与应用开发06:软件定时器
  8. 日常工作常用的几款小工具
  9. python四分位数_四分位数计算过程
  10. 地产钢铁银行成低市盈率三剑客
  11. Creo AFX钢结构设计视频教程
  12. 洛谷P4735 最大异或和
  13. 归档数据1000份,筑智建助力融汇温泉城G1项目高效完成数字化落地
  14. 使用System Center DPM 2012 SP1保护企业关键数据(一)部署SCDPM
  15. Smart3d 近景摄影测量与航空摄影测量
  16. PLL为什么可以倍频
  17. 2021研究生入学考试总结以及一月计划
  18. PC微信逆向:使用HOOK获取好友列表和群列表
  19. 分布式和集群的架构套路总结
  20. 基于Vue+JavaScript实现的手机移动端宠物商城系统(附完整源码)

热门文章

  1. Java实现按键精灵(鼠标篇)
  2. xcms php,牛叉内容管理系统|牛叉内容管理系统(NiuXcms) v1.0.7 官方版 - 软件下载 - 绿茶软件园|33LC.com...
  3. [整理]PCB阻抗控制
  4. html 3d 图片轮播,长方体3D轮播图(CSS3实现)
  5. 荣誉系统排名是整个服务器,[翻译分享]荣誉军衔晋升系统初步解析(2006.4.27更新)...
  6. [SPSS] SPSS统计分析软件简介
  7. 《Improving Deep Neural Networks》的理论知识点
  8. FPGA 处理视频SDRAM带宽计算
  9. 网络综合布线线缆如何敷设布放_网络综合布线垂直子系统线缆敷设方式.PPT
  10. 网站漏洞检测服务 URL跳转漏洞的检测与修复