首页 > 其他分享 >分离分层的 platform驱动

分离分层的 platform驱动

时间:2023-02-03 10:35:57浏览次数:46  
标签:led struct dev platform 分层 device 驱动 include

一、分离分层

输入子系统、usb设备比驱动以及platform类型的驱动等都体现出分离分层机制;如下图所示,一种典型的分离分层框架:

二、platform机制下的分离

分离就是在驱动层中使用platform机制把硬件相关的代码(固定的,如板子的网卡、中断地址)和驱动(会根据程序作变动,如点哪一个灯)分离开来,即要编写两个文件:dev.c和drv.c(platform设备和platform驱动)。

会在开发板/sys/bus/platform目录下出现这两个文件,并且在两个文件目录下存在命名相同的设备和驱动程序。

同样的,在/sys/bus/i2c等其它的总线类型目录下,也存在设备和驱动各自的目录。

platform总线:为platform_bus_type,是个全局变量,属于虚拟设备总线, 属于Linux中bus的一种:

复制代码
 1 struct bus_type platform_bus_type = {
 2     .name            = "platform",
 3     .dev_attrs       = platform_dev_attrs,
 4     .match           = platform_match,
 5     .uevent          = platform_uevent,
 6     .suspend         = platform_suspend,
 7     .suspend_late    = platform_suspend_late,
 8     .resume_early    = platform_resume_early,
 9     .resume          = platform_resume,
10 };
复制代码

利用该设备总线,一旦一方注册就会调用.match函数进行匹配,将driver和device连接在一起,匹配成功后会调用driver程序里的.probe函数:

 

其中,device设备为挂接在platform总线下的设备, platform_device结构体类型,driver驱动为挂接在platform总线下,与某种设备相关的驱动程序, platform_driver结构体类型。

三、例程--基于platform机制,编写led设备和驱动程序

需要分别编写设备代码和驱动代码: led_dev.c 、led_drv.c。其中,

led_dev.c:指定设备硬件/寄存器资源。

led_drv.c:获取设备资源,初始化并操作led。

 3.1 led_drv.c

复制代码
  1 /* 分配/设置/注册一个platform_driver */
  3 #include <linux/module.h>
  4 #include <linux/version.h>
  6 #include <linux/init.h>
  7 #include <linux/fs.h>
  8 #include <linux/interrupt.h>
  9 #include <linux/irq.h>
 10 #include <linux/sched.h>
 11 #include <linux/pm.h>
 12 #include <linux/sysctl.h>
 13 #include <linux/proc_fs.h>
 14 #include <linux/delay.h>
 15 #include <linux/platform_device.h>
 16 #include <linux/input.h>
 17 #include <linux/irq.h>
 18 #include <asm/uaccess.h>
 19 #include <asm/io.h>
 20 
 21 static int major;
 24 static struct class *cls;
 25 static volatile unsigned long *gpio_con;
 26 static volatile unsigned long *gpio_dat;
 27 static int pin;
 28 
 29 static int led_open(struct inode *inode, struct file *file)
 30 {
 31     /* 配置为输出 */
 32     *gpio_con &= ~(0x3<<(pin*2));
 33     *gpio_con |= (0x1<<(pin*2));
 34      return 0;    
 35 }
 37 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
 38 {
 39     int val;
 41     copy_from_user(&val, buf, count); //    copy_to_user();
 42 
 43     if (val == 1)
 44     {
 45         // 点灯
 46         *gpio_dat &= ~(1<<pin);
 47     }
 48     else
 49     {
 50         // 灭灯
 51         *gpio_dat |= (1<<pin);
 52     }
 54     return 0;
 55 }
 58 static struct file_operations led_fops = {
 59     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
 60     .open   =   led_open,     
 61     .write    =    led_write,       
 62 };
 63 
 64 /* 2.1 当驱动和设备都insmod加载后,然后bus总线会匹配成功,就进入.probe函数,
 65    2.2 在.probe函数中便使用platform_get_resource()函数获取LED的地址和引脚,然后初始化LED,并注册字符设备,和设备节点"led",
 66    2.3 编写注册时的file_operations成员函数
 67 */
 68 static int led_probe(struct platform_device *pdev)
 69 {
 70     struct resource        *res;
 71 
 72     /* 根据platform_device的资源进行ioremap */
 73     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //IORESOURCE_MEM资源中的第0个
 74     gpio_con = ioremap(res->start, res->end - res->start + 1);
 75     gpio_dat = gpio_con + 1;                //指针加1==》相当于+4
 76 
 77     res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  //IORESOURCE_IRQ资源中的第0个
 78     pin = res->start;
 79 
 80     /* 注册字符设备驱动程序 */
 82     printk("led_probe, found led\n");
 83 
 84     major = register_chrdev(0, "myled", &led_fops);
 85 
 86     cls = class_create(THIS_MODULE, "myled");
 87 
 88     class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
 89     
 90     return 0;
 91 }
 93 static int led_remove(struct platform_device *pdev)
 94 {
 95     /* 卸载字符设备驱动程序 */
 96     /* iounmap */
 97     printk("led_remove, remove led\n");
 98 
 99     class_device_destroy(cls, MKDEV(major, 0));
100     class_destroy(cls);
101     unregister_chrdev(major, "myled");
102     iounmap(gpio_con);
103     
104     return 0;
105 }
108 struct platform_driver led_drv = {
109     .probe        = led_probe,
110     .remove        = led_remove,
111     .driver        = {
112         .name    = "myled",  
113     }
114 };
115 
116 /*1. 注册的led驱动*/
117 static int led_drv_init(void)
118 {
119     platform_driver_register(&led_drv);
120     return 0;
121 }
123 static void led_drv_exit(void)
124 {
125     platform_driver_unregister(&led_drv);
126 }
128 module_init(led_drv_init);
129 module_exit(led_drv_exit);
131 MODULE_LICENSE("GPL");
复制代码

其中,  platform_get_resource函数为:

复制代码
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num);
//获取设备的某个资源,获取成功,则返回一个resource资源结构体
//参数:
// *dev: 指向某个platform device设备
// type: 获取的资源类型
// num: type资源下的第几个数组
复制代码

platform_driver结构体:

复制代码
struct platform_driver {
       int (*probe)(struct platform_device *);       
       int (*remove)(struct platform_device *);             
       void (*shutdown)(struct platform_device *);         //断电
       int (*suspend)(struct platform_device *, pm_message_t state);  //休眠
       int (*suspend_late)(struct platform_device *, pm_message_t state);
       int (*resume_early)(struct platform_device *);
       int (*resume)(struct platform_device *);           //唤醒
       struct device_driver driver;       //内嵌的driver, 主要是name成员:设备的名称
};
复制代码

3.2 led_dev.c

复制代码
 1 #include <linux/module.h>
 2 #include <linux/version.h>
 3 #include <linux/init.h>
 4 #include <linux/kernel.h>
 5 #include <linux/types.h>
 6 /* 分配/设置/注册一个platform_device */
 7 #include <linux/interrupt.h>
 8 #include <linux/list.h>
 9 #include <linux/timer.h>
10 #include <linux/init.h>
11 #include <linux/serial_core.h>
12 #include <linux/platform_device.h>
13 
14 static struct resource led_resource[] = {
15     [0] = {
16         .start = 0x56000050,               //寄存器的起始物理地址  GPFCON 0x56000050
17         .end   = 0x56000050 + 8 - 1,      
18         .flags = IORESOURCE_MEM,           //哪一类资源  
19     },
20     [1] = {
21         .start = 5,                        //哪(位)个引脚
22         .end   = 5,
23         .flags = IORESOURCE_IRQ,
24     }
25 
26 };
27 
28 static void led_release(struct device * dev)
29 {
30 }
32 static struct platform_device led_dev = {
33     .name         = "myled",
34     .id       = -1,        //表示只有一个设备
35     .num_resources    = ARRAY_SIZE(led_resource),//资源数量,ARRAY_SIZE()函数:获取数量
36     .resource     = led_resource,  
37     .dev = { 
38         .release = led_release, 
39     },
40 };
41 
42 static int led_dev_init(void)
43 {
44     platform_device_register(&led_dev);
45     return 0;
46 }
47 
48 static void led_dev_exit(void)
49 {
50     platform_device_unregister(&led_dev);
51 }
52 module_init(led_dev_init);
53 module_exit(led_dev_exit);
54 MODULE_LICENSE("GPL");
复制代码

其中,platform_driver结构体:

复制代码
struct platform_device {
  const char       * name; //设备名称,要与platform_driver的name一样,这样总线才能匹配成功
  u32          id;         //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1
  struct  device  dev;     //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)
  u32 num_resources;                 //资源数量,
  struct resource         * resource;    //资源结构体,保存设备的信息
};
复制代码 复制代码
struct resource {
         resource_size_t start;                    //起始资源,如果是地址的话,必须是物理地址
         resource_size_t end;                      //结束资源,如果是地址的话,必须是物理地址
         const char *name;                         //资源名
         unsigned long flags;                      //资源的标志
         //比如IORESOURCE_MEM,表示地址资源, IORESOURCE_IRQ表示中断引脚... ...
         struct resource *parent, *sibling, *child;   //资源拓扑指针父、兄、子,可以构成链表
};
复制代码

3.3 测试程序

复制代码
 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5 
 6 /* led_test on
 7  * led_test off
 8  */
 9 int main(int argc, char **argv)
10 {
11     int fd;
12     int val = 1;
13     fd = open("/dev/led", O_RDWR);
14     if (fd < 0)
15     {
16         printf("can't open!\n");
17     }
18     if (argc != 2)
19     {
20         printf("Usage :\n");
21         printf("%s <on|off>\n", argv[0]);
22         return 0;
23     }
24 
25     if (strcmp(argv[1], "on") == 0)
26     {
27         val  = 1;
28     }
29     else
30     {
31         val = 0;
32     }
34     write(fd, &val, 4);
35     return 0;
36 }
复制代码

1)将led_dev和led_drv编译为模块,加载。会在sys/bus/platform/devices目录下分别生成一个"myled"。

2)最后,编译应用程序,并在开发板运行可执行程序,控制led:

  # led_test on

  # led_test off 

标签:led,struct,dev,platform,分层,device,驱动,include
From: https://www.cnblogs.com/kn-zheng/p/17088306.html

相关文章

  • 《领域驱动设计》:从领域视角深入仓储(Repository)的设计和实现
    一、前言“DDD设计的目标是关注领域模型而并非技术来创建更好的软件,假设开发人员构建了一个SQL,并将它传递给基础设施层中的某个查询服务然后根据表数据的结构集取出所......
  • linux驱动移植-DM9000网卡驱动
    在学习Mini2440裸机程序时,我们介绍过关于DM9000网卡的相关知识,包括电路图、以及DM9000寄存器等信息。具体可以参考Mini2440裸机开发之DM9000。本节对之前已经介绍过的知识......
  • debian 显卡驱动
    https://wiki.debian.org/AtiHowTo 01:00.0VGAcompatiblecontroller[0300]:AdvancedMicroDevices,Inc.[AMD/ATI]BartsPRO[RadeonHD6850][1002:6739]amd......
  • Linux ALSA驱动之二:声卡的创建流程
    1、structsnd_card1.1、snd_card是啥snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card......
  • MT8788 android 9.0 sensor 驱动移植
    1.硬件配置打开 MT8788 核心板原理图,配置GYRO(陀螺仪),ALSPS(光感),G-sensor(加速度)的ENIT和GPIO以及I2C,打开vendor\mediatek\proprietary\scripts\dct\DrvGen.......
  • USB键盘驱动编写和测试
    一、原理分析1.首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么。1)通过按完所有键盘按键打印的结果可知,有8个按键会打印在usb_buf[0]里,即:ctrl左键......
  • VK1S68C/VK1640B是血氧仪LED数码管显示驱动芯片/LED数显驱动控制电路(IC),SSOP24小体积封
    产品品牌:永嘉微电/VINKA产品型号:VK1S68C封装形式:SSOP24概述:VK1S68C是一种带键盘扫描接口的数码管或点阵LED驱动控制专用芯片,内部集成有3线串行接口、数据锁存器、LED......
  • 血氧仪/胎心仪等低功耗小体积数码管显示屏LED数显驱动IC-VK1S68C【FAE技术支持】
    VK1S68C是1/5~1/8占空比的LED显示控制驱动电路。由10根段输出、4根栅输出、3根段/栅输出,1个显示存储器、控制电路、键扫描电路组成了一个高可靠性的单片机外围LE......
  • Linux下的硬件驱动——USB设备
    USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题。本文着力从Linux系统下设备驱动的架构,去阐述......
  • 使用EB配置PWM驱动
    概述脉宽调制(PWM)驱动器负责提供与AUTOSAR指定的PWM信号生成相关的标准服务。PWM通道的底层定时器引擎是一个GTM (TOM或ATOM片)或CCU6 (T12或T13片)定时器通道。......