在Linux操作系统中,驱动程序占用了Linux内核代码量的大头,如果不进行管理,将会造成数量庞大的结果。因此引入了驱动的分离

上图所示就是驱动的分离,相当于驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息),然后根据获取到的设备信息来初始化设备。 这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux 中的总线(bus)驱动(driver)设备(device)模型,也就是常说的驱动分离。总线就是驱动和设备信息的月老,负责给两者牵线搭桥。

一、platform 平台驱动

为了允许没有总线模型概念的外设使用总线(bus)、驱动(driver)和设备(device)模型。Linux系统提出了platform 这个虚拟总线。

struct bus_type platform_bus_type = {.name = "platform",.dev_groups = platform_dev_groups,.match = platform_match,.uevent = platform_uevent,.pm = &platform_dev_pm_ops,};

  platform_bus_type就是platform平台总线,其中最关键的就是.match = platform_match,总线就是通过platform_match函数完成设备与驱动之间的匹配的!
        platform_match具体定义如下:

static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* When driver_override is set, only bind to the matching driver */if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}

platform_match函数中包含了4中驱动和设备匹配的方法。分别为:

①设备树匹配的方法
②ACPI匹配方式
③id_table匹配方式
④通过驱动和设备.name匹配

并且四种方式的优先级为:①>②>③>④。主要学习方式①和④。

二、通过驱动和设备.name匹配

platform平台设备驱动,它只不过是将 device 进一步封装成为 platform_device,将 device_driver 进一步封装成为 platform_device_driver
        要想使用platform平台驱动,需要定义在设备中定义 platform_device结构体(如果采用设备数匹配方式的话则不用定义设备结构体),在驱动中定义platform_driver结构体。

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 (*resume)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;bool prevent_deferred_probe;
};

        int (*probe)(struct platform_device *)表示probe函数指针,当驱动与设备匹配成功以后 probe 函数就会执行。
        struct device_driver driver表示device_driver结构体,在此处定义类似于C++中的继承特性,即platform_driver结构体继承了device_driver结构体的成员变量。device_driver结构体具体定义如下:

struct device_driver {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;
};

        name表示设备的名字,需要和设备中使用的name相匹配。
        probe 函数,当设备和驱动匹配以后此函数就会执行。
        remove 函数,当卸载platform驱动的时候此函数就会执行。
        of_match_table 就是采用设备树的时候驱动使用的匹配表,同样是数组,每个匹
配项都为 of_device_id 结构体类型。定义如下:

struct of_device_id {char name[32];char type[32];char compatible[128];const void *data;
};

        compatible,对于设备树而言,就是通过设备节点的 compatible 属性值和 of_match_table 中每个项目的 compatible 成员变量进行比较,如果有相等的就表示设备和此驱动匹配成功。

注册platform_driver设备

使用platform 驱动的时候,首先定义一个 platform_driver 结构体变量,然后实现结构体
中的各个成员变量,重点是实现匹配方法以及 probe 函数。当驱动和设备匹配成功以后 probe
函数就会执行。
        当我们定义并初始化好 platform_driver 结构体变量以后,需要在驱动入口函数里面调用
platform_driver_register 函数向 Linux 内核注册一个 platform 驱动。

int platform_driver_register (struct platform_driver *driver)

函数参数和返回值含义如下:
driver:要注册的 platform 驱动。
返回值: 负数,失败; 0,成功

驱动卸载函数中通过platform_driver_unregister函数卸载 platform 驱动。

void platform_driver_unregister(struct platform_driver *drv)

函数参数和返回值含义如下:
drv:要卸载的 platform 驱动。
返回值: 无

platform_device结构体

struct platform_device {const char   *name;int       id;bool     id_auto;struct device   dev;u32     num_resources;struct resource   *resource;const struct platform_device_id   *id_entry;char *driver_override; /* Driver name to force a match *//* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata    archdata;
};

name表示设备的名字,需要和驱动中使用的name相匹配,方式④就是通过name来完成驱动和设备的匹配的。
        num_resources表示struct resource *resource结构体指针指向的资源的大小。
        struct resource *resource为结构体指针,指向设备信息。具体定义如下:

struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;
};

startend 分别表示资源的起始和终止信息,对于内存类的资源,就表示内存起始和终止
地址,name 表示资源名字,flags表示资源类型。

注册platform_device设备

在不使用方式①设备树,利用方式④匹配方法中,需要使用两个函数完成platform_device设备的注册和注销。

int platform_device_register(struct platform_device *pdev)

函数参数和返回值含义如下:
pdev:要注册的 platform 设备。
返回值: 负数,失败; 0,成功。

void platform_device_unregister(struct platform_device *pdev)

函数参数和返回值含义如下:
pdev:要注销的 platform 设备。
返回值: 无

实验代码:

platform_driver驱动代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.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>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>#define LEDON 1         /*开灯*/
#define LEDOFF 0        /*关灯*/#define PLATFORMLED_NAME "platformled"
#define PLATFORMLED_COUNT 1struct newchrled_dev newchrled; /*led设备*//*映射后虚拟地址指针*/
static void __iomem *IMX6ULL_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;/*LED设备结构体*/
struct newchrled_dev{struct cdev cdev;   /*字符设备结构体*/dev_t devid;    /*定义设备号*/struct class *class;    /*类*/struct device *device;  /*创建设备*/int major;  /*定义主设备号*/int minor;  /*定义次设备号*/
};static void LED_switch(u8 sta){u32 val = 0;if(sta == LEDON){val = readl(GPIO1_DR);val &= ~(1 << 3);writel(val, GPIO1_DR);}else if(sta == LEDOFF){val = readl(GPIO1_DR);val |= (1 << 3);writel(val, GPIO1_DR);}
}
static int newchrled_open(struct inode *inode, struct file *filp){filp->private_data = &newchrled;return 0;
}static ssize_t newchrled_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos){int retvalue;unsigned char databuf[1];retvalue = copy_from_user(databuf, buf, count);if(retvalue < 0){printk("kernel write error \r\n");return -EFAULT;}/*开灯还是关灯*/LED_switch(databuf[0]);return 0;
}static int newchrled_release(struct inode *inode, struct file *filp){return 0;
}static const struct file_operations newchrled_fops = {.owner = THIS_MODULE,.write = newchrled_write,.open = newchrled_open,.release = newchrled_release,
};static int led_probe(struct platform_device *dev){int ret = 0;unsigned int val = 0;int i;struct resource *ledsource[5];/*初始化led*//*1、从设备中获取资源*/for(i = 0;i < 5;i++){ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i);if(ledsource[i] == NULL)return -EINVAL;}/*led初始化*//*2、地址映射*/IMX6ULL_CCM_CCGR1 = ioremap(ledsource[0]->start, resource_size(ledsource[0]));SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, resource_size(ledsource[1]));SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, resource_size(ledsource[2]));GPIO1_DR = ioremap(ledsource[3]->start, resource_size(ledsource[3]));GPIO1_GDIR = ioremap(ledsource[4]->start, resource_size(ledsource[4]));/*3、初始化*//*开启时钟*/val = readl(IMX6ULL_CCM_CCGR1);val &= ~(3 << 26);  //清除先前的26,27位val |= (3 << 26);   //26,27位置1writel(val, IMX6ULL_CCM_CCGR1);/*配置GPIO1_IO03*/writel(0x5, SW_MUX_GPIO1_IO03); //设置复用writel(0x10B0, SW_PAD_GPIO1_IO03);  //设置电气属性val = readl(GPIO1_GDIR);val |= (1 << 3);writel(val, GPIO1_GDIR);/*注册字符设备*/if(newchrled.major){    /*给定主设备号*/newchrled.devid = MKDEV(newchrled.major, 0);    /*获取设备号*/ret = register_chrdev_region(newchrled.devid, PLATFORMLED_COUNT, PLATFORMLED_NAME);}else{   /*s否则自动申请设备号*/ret = alloc_chrdev_region(&newchrled.devid, 0, PLATFORMLED_COUNT, PLATFORMLED_NAME);newchrled.major = MAJOR(newchrled.devid);newchrled.minor = MINOR(newchrled.devid);}if(ret < 0){printk("newchrled register error!\r\n");goto fail_devid;}printk("newchrled major = %d, minor = %d \r\n", newchrled.major, newchrled.minor);//打印主次设备号newchrled.cdev.owner = THIS_MODULE;cdev_init(&newchrled.cdev, &newchrled_fops);ret = cdev_add(&newchrled.cdev, newchrled.devid, PLATFORMLED_COUNT);if(ret < 0){goto fail_cdev;}/*自动创建设备节点*/newchrled.class = class_create(THIS_MODULE, PLATFORMLED_NAME);if(IS_ERR(newchrled.class)){ret = PTR_ERR(newchrled.class);goto fail_class;}newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, PLATFORMLED_NAME);if(IS_ERR(newchrled.device)){ret = PTR_ERR(newchrled.device);goto fail_device;}return 0;fail_device:class_destroy(newchrled.class);
fail_class:cdev_del(&newchrled.cdev);
fail_cdev:unregister_chrdev_region(newchrled.devid, PLATFORMLED_COUNT);
fail_devid:return ret;
}static int led_remove(struct platform_device *dev){unsigned int val = 0;/*关闭LED*/val = readl(GPIO1_DR);val |= (1 << 3);writel(val, GPIO1_DR);/*取消地址映射*/iounmap(IMX6ULL_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/*删除字符设备*/cdev_del(&newchrled.cdev);/*注销设备号*/unregister_chrdev_region(newchrled.devid, PLATFORMLED_COUNT);/*销毁设备*/device_destroy(newchrled.class, newchrled.devid);/*销毁类*/class_destroy(newchrled.class);return 0;
}/*platform结构体*/
static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",},.probe = led_probe,.remove = led_remove,
};/*驱动加载函数*/
static int __init leddriver_init(void){/*注册platform驱动*/return platform_driver_register(&led_driver);
}/*驱动卸载函数*/
static void __exit leddriver_exit(void){/*卸载platform驱动*/platform_driver_unregister(&led_driver);
}/*驱动入口和出口*/
module_init(leddriver_init);
module_exit(leddriver_exit);/*协议*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

platform_device驱动代码: 

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.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>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>/*寄存器基地址*/
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)/*寄存器地址长度*/
#define REGISTER_LENGTH 4void leddevice_release(struct device *dev){printk("leddevice release\r\n");
}static struct resource led_resources[] = {[0] = {.start = CCM_CCGR1_BASE,.end = CCM_CCGR1_BASE + REGISTER_LENGTH - 1,.flags =  IORESOURCE_MEM,},[1] = {.start = SW_MUX_GPIO1_IO03_BASE,.end = SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1,.flags =  IORESOURCE_MEM,},[2] = {.start = SW_PAD_GPIO1_IO03_BASE,.end = SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1,.flags =  IORESOURCE_MEM,},[3] = {.start = GPIO1_DR_BASE,.end = GPIO1_DR_BASE + REGISTER_LENGTH - 1,.flags =  IORESOURCE_MEM,},[4] = {.start = GPIO1_GDIR_BASE,.end = GPIO1_GDIR_BASE + REGISTER_LENGTH - 1,.flags =  IORESOURCE_MEM,},
};static struct platform_device leddevice = {.name = "imx6ul-led",.id = -1,.dev = {.release = &leddevice_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};static int __init leddevice_init(void){/*注册platform设备*/return platform_device_register(&leddevice);
}static void __exit leddevice_exit(void){/*卸载platform设备*/platform_device_unregister(&leddevice);
}/*驱动入口和出口*/
module_init(leddevice_init);
module_exit(leddevice_exit);/*协议*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

代码流程:①注册platform_driver②注册platform_device(①和②顺序可以互换)
③.name是否匹配?
执行.probe函数进行初始化:选择其他匹配方法;
④卸载驱动执行.remove函数。

三、设备树匹配方法

        在使用设备树的时候,只需要配置platform_driver驱动,不用配置platform_device。platform 驱动会通过of_match_table来保存兼容性值,也就是表明此驱动兼容哪些设备。

实验代码如下:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.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>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>#define GPIOLED_COUNT 1
#define GPIOLED_NAME "dtsplatformled"#define LEDOFF 0
#define LEDON 1/*gpio设备结构体*/
struct gpioled_dev{dev_t devid; /*设备号*/int major; /*主设备号*/int minor; /*次设备号*/struct cdev cdev;struct class *class;    /*创建类*/struct device *device;  /*创建设备*/struct device_node *node; /*设备节点*/int led_gpio;
};struct gpioled_dev gpioled;static int gpioled_open(struct inode *inode, struct file *filp){filp->private_data = &gpioled; /* 设置私有数据 */return 0;
}static ssize_t gpioled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{int ret = 0;unsigned char databuf[1];ret = copy_from_user(databuf, buf, cnt);if(ret < 0){return -EINVAL;}if(databuf[0] == LEDON){gpio_set_value(gpioled.led_gpio, 0);}else if(databuf[0] == LEDOFF){gpio_set_value(gpioled.led_gpio, 1);}return 0;
}static int gpioled_release(struct inode *inode, struct file *filp){return 0;
}/*定义字符操作集*/
static const struct file_operations gpioled_fops = {.owner = THIS_MODULE,.write = gpioled_write,.open = gpioled_open,.release = gpioled_release,
};static int led_probe(struct platform_device *dev){int ret = 0;printk("led_probe! \r\n");gpioled.major = 0;  /*linux内核自动申请设备号*//*注册字符设备驱动*/if(gpioled.major){  /*给定主设备号*/gpioled.devid = MKDEV(gpioled.major, 0);ret = register_chrdev_region(gpioled.devid, GPIOLED_COUNT, GPIOLED_NAME);}else{   /*没给定设备号*/ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_COUNT, GPIOLED_NAME);gpioled.major = MAJOR(gpioled.devid);   /*保存主设备号*/gpioled.minor = MINOR(gpioled.devid);   /*保存次设备号*/}if(ret < 0){goto failed_devid;}printk("gpioled major = %d ,minor = %d \r\n",gpioled.major,gpioled.minor);/*初始化cdev*/gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/*添加cdev*/ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_COUNT);if(ret < 0){goto failed_cdev;}/*自动创建设备节点*//*创建类*/gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if(IS_ERR(gpioled.class)){   /*判断是否创建类成功*/ret = PTR_ERR(gpioled.class);goto failed_class;}/*创建设备*/gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if(IS_ERR(gpioled.device)){   /*判断是否创建类成功*/ret = PTR_ERR(gpioled.device);goto failed_device;}/*获取设备节点*/gpioled.node = of_find_node_by_path("/gpioled");if(gpioled.node == NULL){    /*寻找节点失败*/ret = -EINVAL;goto failed_findnode;}/*获取led所对应的gpio*/gpioled.led_gpio = of_get_named_gpio(gpioled.node, "led-gpios", 0);if(gpioled.led_gpio < 0){printk("can't find led gpio \r\n");ret = -EINVAL;goto failed_findnode;}printk("led gpio num = %d \r\n",gpioled.led_gpio);/*申请gpio*/ret = gpio_request(gpioled.led_gpio, "led-gpios");if(ret){printk("Failed to request gpio \r\n");ret = -EINVAL;goto failed_findnode;}/*使用IO,申请为输出*/ret = gpio_direction_output(gpioled.led_gpio, 1); /*设置为输出,高电平不点亮*/if(ret < 0){goto failed_setoutput;}/*输出低电平,点亮gpio*/gpio_set_value(gpioled.led_gpio, 0);return 0;failed_setoutput:gpio_free(gpioled.led_gpio);
failed_findnode:device_destroy(gpioled.class, gpioled.devid);
failed_device:class_destroy(gpioled.class);
failed_class:cdev_del(&gpioled.cdev);
failed_cdev:unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);
failed_devid:return ret;return 0;
}static int led_remove(struct platform_device *dev){printk("led_removes! \r\n");/*关灯*//*输出高电平,关闭gpio*/gpio_set_value(gpioled.led_gpio, 1);/*注销字符设备驱动*/cdev_del(&gpioled.cdev);unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class);/*释放IO*/gpio_free(gpioled.led_gpio);return 0;
}struct of_device_id led_of_match[] = {{.compatible = "alientek,gpioled"},{/* Sentinel */},};struct platform_driver led_driver = {.driver ={.name = "imx6ul-led",   /*无设备树的时候,会和设备里面platform_device->driver->name匹配*/.of_match_table = led_of_match, /*设备树匹配表*/},.probe = led_probe,.remove = led_remove,
};/*驱动加载函数*/
static int __init leddriver_init(void){/*注册platform驱动*/platform_driver_register(&led_driver);return 0;
}/*驱动卸载函数*/
static void __exit leddriver_exit(void){platform_driver_unregister(&led_driver);
}/*驱动入口和出口*/
module_init(leddriver_init);
module_exit(leddriver_exit);/*协议*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

代码流程:①注册platform_driver        ②.compatible是否匹配?执行.probe函数进行初始化:选择其他匹配方法;③卸载驱动执行.remove函数。

总结:

platform平台设备驱动是基于设备总线驱动模型的,它只不过是将 device 进一步封装成为 platform_device,将 device_driver 进一步封装成为 platform_device_driver,将我们之前学过的驱动代码套了一层壳。

Linux驱动_Platform平台驱动相关推荐

  1. Linux驱动之平台设备

    <平台设备设备驱动> a:背景: 平台总线是Linux2.6的设备驱动模型中,关心总线,设备和驱动这3个实体.一个现实的Linux设备和驱动通常需要挂接在一种总线上(比如本身依附于PCI, ...

  2. linux设备驱动——andriod平台wlan驱动

    转自 :http://blog.chinaunix.net/space.php?uid=22278460&do=blog&cuid=2186191 linux设备驱动--andriod ...

  3. Linux kernel 中模块化的平台驱动代码介绍

    介绍 在linux kernel中通过module_platform_driver来实现模块化平台驱动.大量的设备驱动程序都基于该种方式来实现,使用频次非常的高,在linux kernel 5.4.1 ...

  4. linux红外遥控进程,46.Linux-分析rc红外遥控平台驱动框架,修改内核的NEC解码函数BUG(1)...

    内核版本         :  Linux 3.10.14 rc红外接收类型:  GPIO 类型的NEC红外编码 本章内容 1)rc体系结构分析 2) 分析红外platform_driver平台驱动框 ...

  5. Linux驱动学习-平台设备驱动probe函数-20220410

    1.最简单的杂项设备 /* * @Descripttion: 最简单的杂项设备驱动 * @version: * @Author: topeet */ #include <linux/init.h ...

  6. platform平台驱动模型简述(linux驱动开发篇)

    此篇是驱动分离(总线.驱动和设备模型)的应用扩展,主要简述platform虚拟总线平台 一个现实的Linux设备和驱动通常挂接在一种总线上,对于本身依附于PCI.USB.I2C.SPI等的设备而言,这 ...

  7. linux驱动开发 - 12_platform 平台驱动模型

    文章目录 platform 平台驱动模型 1 platform 总线 platform匹配过程 2 platform 驱动 platform 驱动框架如下所示: 3 platform 设备 platf ...

  8. 英特尔linux手机,英特尔的手机梦破碎:Linux 5.12将移除Moorestown和Medfield平台驱动...

    原标题:英特尔的手机梦破碎:Linux 5.12将移除Moorestown和Medfield平台驱动 来源:cnBeta.COM 除了一些比较老的 ARM 平台以及一些过时.不知名的 CPU 架构被移 ...

  9. linux注册平台驱动,关于Linux驱动的平台注册方式-- platform_driver_register

    http://hi.baidu.com/deep_pro/blog/item/754f7764b73099fbf636544a.html cdev_add .register_blkdev 这样的驱动 ...

最新文章

  1. 5.计算机发展个人理解-电路终究是电路 软件如何控制硬件 代码如何操作硬件 硬件是怎么执行代码 代码如何执行 软件与硬件如何交互 计算机思维 抽象 封装 规范 屏蔽 协议分层...
  2. TeamTalk源码分析之login_server
  3. xmlWriter以UTF-8格式写xml问题
  4. python进阶 多线程编程 —— threading和queue库实现多线程编程
  5. poj 3460 bookstore
  6. 根据ABAP BAdI definition名称找到SPRO里配置路径的办法
  7. 【POJ - 3281】Dining(拆点建图,网络流最大流)
  8. iphone短信尚未送达_iPhone开启这个功能,从此告别垃圾短信骚扰!
  9. docker搭建单节点mongodb
  10. python 读取命令行输入_python读取命令行参数的方法
  11. window.open在Safari浏览器出现的问题
  12. 质量数据分析工具软件的应用
  13. css子元素选择父元素的实现
  14. python枚举详解
  15. 码农到架构师视频学习笔记
  16. 三分钟了解区块链AR游戏Triffic2.0版本
  17. Cesium|xt3d 雷达追踪圆锥体
  18. 各版本Qt下载传送门
  19. 从技术角度谈如何开发一款微信联网小游戏
  20. 异地二维码收款被风控的问题解决方法

热门文章

  1. python基础100道例题
  2. 前端架构演进及主流UI
  3. NestJs学习总结篇
  4. vert.x笔记:3.使用vert.x发布restful接口
  5. 止不住的砸钱emmm
  6. Android Studio 技巧之【Move Lines Up Down】
  7. Hadoop HA——hdfs haadmin 详解
  8. springcloud gateway网关直接给前端返回json数据
  9. 刀锋之影-EA【微马丁策略】
  10. 数据库自动收缩造成的阻塞