linux系列目录:
linux基础篇(一)——GCC和Makefile编译过程
linux基础篇(二)——静态和动态链接
ARM裸机篇(一)——i.MX6ULL介绍
ARM裸机篇(二)——i.MX6ULL启动过程
ARM裸机篇(三)——i.MX6ULL第一个裸机程序
ARM裸机篇(四)——重定位和地址无关码
ARM裸机篇(五)——异常和中断
linux系统移植篇(一)—— linux系统组成
linux系统移植篇(二)—— Uboot使用介绍
linux系统移植篇(三)—— Linux 内核使用介绍
linux系统移植篇(四)—— 根文件系统使用介绍
linux驱动开发篇(一)—— Linux 内核模块介绍
linux驱动开发篇(二)—— 字符设备驱动框架
linux驱动开发篇(三)—— 总线设备驱动模型
linux驱动开发篇(四)—— platform平台设备驱动
linux驱动开发篇(五)—— linux驱动面向对象的编程思想
linux驱动开发篇(六)—— 设备树的引入

文章目录

  • 一、平台设备驱动
    • 1、平台总线
    • 2、平台设备
    • 3、平台驱动
    • 4、平台驱动获取设备信息
  • 二、平台设备驱动实验
    • 1、编程思路
    • 2、定义平台设备
    • 3、定义平台驱动
    • 4、编译

一、平台设备驱动

在设备驱动模型中,引入总线的概念可以对驱动代码和设备信息进行分离。但是驱动中总线的概念是软件层面的一种抽象,与我们 SOC 中物理总线的概念并不严格相等:

  • 物理总线:芯片与各个功能外设之间传送信息的公共通信干线,其中又包括数据总线、地址总线和控制总线,以此来传输各种通信时序。
  • 驱动总线:负责管理设备和驱动。制定设备和驱动的匹配规则,一旦总线上注册了新的设备或者是新的驱动,总线将尝试为它们进行配对。

一般对于 I2C、 SPI、 USB 这些常见类型的物理总线来说, Linux 内核会自动创建与之相应的驱动总线,因此 I2C 设备、 SPI 设备、 USB 设备自然是注册挂载在相应的总线上。但是,实际项目开发中还有很多结构简单的设备,对它们进行控制并不需要特殊的时序。它们也就没有相应的物理总线,比如 led、 rtc 时钟、蜂鸣器、按键等等, Linux 内核将不会为它们创建相应的驱动总线。为了使这部分设备的驱动开发也能够遵循设备驱动模型, Linux 内核引入了一种虚拟的总线——平台总线(platform bus)

平台设备驱动的核心依然是 Linux 设备驱动模型,平台设备使用 platform_device 结构体来进行表示,其继承了设备驱动模型中的device 结构体。而平台驱动使用 platform_driver 结构体来进行表示,其则是继承了设备驱动模型中的 device_driver结构体。

1、平台总线

内核中使用 bus_type 来抽象描述系统中的总线,平台总线结构体原型如下所示:
platform_bus_type 结 构 体 (内 核 源码/driver/base/platform.c)

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

内核用 platform_bus_type 来描述平台总线,该总线在 linux 内核启动的时候自动进行注册。

这里重点是 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 总线提供了四种匹配方式,并且这四种方式存在着优先级:设备树机
制 >ACPI 匹配模式 >id_table 方式 > 字符串比较。

2、平台设备

内核使用 platform_device 结构体来描述平台设备,platform_device 结 构 体 (内 核 源码/include/linux/platform_device.h)结构体原型如下:

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: 设备名称,总线进行匹配时,会比较设备和驱动的名称是否一致;
  • id: 指定设备的编号, Linux 支持同名的设备,而同名设备之间则是通过该编号进行区分;
  • dev: Linux 设备模型中的 device 结构体, linux 内核大量使用了面向对象思想, platform_device通过继承该结构体可复用它的相关代码,方便内核管理平台设备;
  • num_resources: 记录资源的个数,当结构体成员 resource 存放的是数组时,需要记录 resource数组的个数,内核提供了宏定义 ARRAY_SIZE 用于计算数组的个数;
  • resource: 平台设备提供给驱动的资源,如 irq, dma,内存等等。该结构体会在接下来的内容进行讲解;
  • id_entry: 平台总线提供的另一种匹配方式,原理依然是通过比较字符串,这部分内容会在平台总线小节中讲,这里的 id_entry 用于保存匹配的结果;

平台设备的工作是为驱动程序提供设备信息, 设备信息包括硬件信息和软件信息两部分。

  • 硬件信息:驱动程序需要使用到什么寄存器,占用哪些中断号、内存资源、 IO 口等等
  • 软件信息:以太网卡设备中的 MAC 地址、 I2C 设备中的设备地址、 SPI 设备的片选信号线等等。

对于硬件信息,使用结构体 struct resource 来保存设备所提供的资源,比如设备使用的中断编号,寄存器物理地址等,结构体原型如下:

struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;
};
  • name: 指定资源的名字,可以设置为 NULL;
  • start、 end: 指定资源的起始地址以及结束地址
  • flags: 用于指定该资源的类型,在 Linux 中,资源包括 I/O、 Memory、 Register、 IRQ、 DMA、Bus 等多种类型,最常见的有以下几种:
资源宏定义 描述
IORESOURCE_IO 用于 IO 地址空间,对应于 IO 端口映射方式
IORESOURCE_MEM 用于外设的可直接寻址的地址空间
IORESOURCE_IRQ 用于指定该设备使用某个中断
IORESOURCE_DMA 用于指定使用的 DMA 通道

注册/注销平台设备API原型:

int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);

3、平台驱动

内核中使用 platform_driver 结构体来描述平台驱动,platform_driver 结 构 体 (内 核 源
码/include/platform_device.h)结构体原型如下所示:

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;
};
  • probe: 函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当总线为设备和驱动匹配上之后,会回调执行该函数。我们一般通过该函数,对设备进行一系列的初始化。
  • remove: 函数指针,驱动开发人员需要在驱动程序中初始化该函数指针,当我们移除某个平台设备时,会回调执行该函数指针,该函数实现的操作,通常是 probe 函数实现操作的逆过程。
  • driver: Linux 设备模型中用于抽象驱动的 device_driver 结构体, platform_driver 继承该结构体,也就获取了设备模型驱动对象的特性;
  • id_table: 表示该驱动能够兼容的设备类型。

platform_device_id 结构体原型如下所示:

struct platform_device_id {char name[PLATFORM_NAME_SIZE];kernel_ulong_t driver_data;
};

在 platform_device_id 这个结构体中,有两个成员,第一个是数组用于指定驱动的名称,总线进行匹配时,会依据该结构体的 name 成员与 platform_device 中的变量 name 进行比较匹配,另一个成员变量 driver_data,则是用于来保存设备的配置。

注册/注销平台驱动API

int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);

4、平台驱动获取设备信息

在学习平台设备的时候,我们知道平台设备使用结构体 resource 来抽象表示硬件信息,而软件信息则可以利用设备结构体 device 中的成员 platform_data 来保存。先看一下如何获取平台设备中结构体 resource 提供的资源。
platform_get_resource() 函数通常会在驱动的 probe 函数中执行,用于获取平台设备提供的资源结构体,最终会返回一个 struct resource 类型的指针,该函数原型如下:

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);
  • dev: 指定要获取哪个平台设备的资源;
  • type: 指定获取资源的类型,如 IORESOURCE_MEM、 IORESOURCE_IO 等;
  • num: 指定要获取的资源编号。每个设备所需要资源的个数是不一定的,为此内核对这些资源进行了编号,对于不同的资源,编号之间是相互独立的。

对于存放在 device 结构体中成员 platform_data 的软件信息,我们可以使用 dev_get_platdata 函数来获取,函数原型如下所示:

static inline void *dev_get_platdata(const struct device *dev)
{return dev->platform_data;
}

总结一下平台驱动需要实现 probe 函数,当平台总线成功匹配驱动和设备时,则会调用驱动的 probe 函数,在该函数中使用上述的函数接口来获取资源,以初始化设备,最后
填充结构体 platform_driver,调用 platform_driver_register 进行注册。

二、平台设备驱动实验

把平台设备驱动应用到 LED 字符设备驱动的代码中,实现硬件与软件代码相分离。

1、编程思路

  1. 编写第一个内核模块 led_pdev.c
  2. 在内核模块中定义一个平台设备,并填充 RGB 灯相关设备信息
  3. 在该模块入口函数,注册/挂载这个平台设备
  4. 编写第二个内核模块 led_pdrv.c
  5. 在内核模块中定义一个平台驱动,在 probe 函数中完成字符设备驱动的创建
  6. 在该模块入口函数,注册/挂载这个平台驱动

在平台设备总线上,注册/挂载平台设备和平台驱动时,会自动进行配对。配对成功后,回调执行平台驱动的 probe 函数,从而完成字符设备驱动的创建。

2、定义平台设备

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>#define CCM_CCGR1                                                            0x20C406C//时钟控制寄存器
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04                0x20E006C//GPIO1_04复用功能选择寄存器
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04                0x20E02F8//PAD属性设置寄存器
#define GPIO1_GDIR                                                          0x0209C004//GPIO方向设置寄存器(输入或输出)
#define GPIO1_DR                                                                0x0209C000//GPIO输出状态寄存器#define CCM_CCGR3                                                             0x020C4074
#define GPIO4_GDIR                                                          0x020A8004
#define GPIO4_DR                                                                0x020A8000#define IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO020          0x020E01E0
#define IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO020           0x020E046C#define IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO019          0x020E01DC
#define IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO019           0x020E0468static struct resource rled_resource[] = {[0] = DEFINE_RES_MEM(GPIO1_DR, 4),[1] = DEFINE_RES_MEM(GPIO1_GDIR, 4),[2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04, 4),[3] = DEFINE_RES_MEM(CCM_CCGR1, 4),[4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04, 4),
};static struct resource gled_resource[] = {[0] = DEFINE_RES_MEM(GPIO4_DR, 4),[1] = DEFINE_RES_MEM(GPIO4_GDIR, 4),[2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO020, 4),[3] = DEFINE_RES_MEM(CCM_CCGR3, 4),[4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO020, 4),
};static struct resource bled_resource[] = {[0] = DEFINE_RES_MEM(GPIO4_DR, 4),[1] = DEFINE_RES_MEM(GPIO4_GDIR, 4),[2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO019, 4),[3] = DEFINE_RES_MEM(CCM_CCGR3, 4),[4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO019, 4),
};
/* not used */
static void led_release(struct device *dev)
{}/* led hardware information */
unsigned int rled_hwinfo[2] = { 4, 26 };
unsigned int gled_hwinfo[2] = { 20, 12 };
unsigned int bled_hwinfo[2] = { 19, 12 };/* red led device */
static struct platform_device rled_pdev = {.name = "led_pdev",.id = 0,.num_resources = ARRAY_SIZE(rled_resource),.resource = rled_resource,.dev = {.release = led_release,.platform_data = rled_hwinfo,},
};
/* green led device */
static struct platform_device gled_pdev = {.name = "led_pdev",.id = 1,.num_resources = ARRAY_SIZE(gled_resource),.resource = gled_resource,.dev = {.release = led_release,.platform_data = gled_hwinfo,},
};
/* blue led device */
static struct platform_device bled_pdev = {.name = "led_pdev",.id = 2,.num_resources = ARRAY_SIZE(bled_resource),.resource = bled_resource,.dev = {.release = led_release,.platform_data = bled_hwinfo,},
};static __init int led_pdev_init(void)
{printk("pdev init\n");platform_device_register(&rled_pdev);platform_device_register(&gled_pdev);platform_device_register(&bled_pdev);return 0;
}module_init(led_pdev_init);static __exit void led_pdev_exit(void)
{printk("pdev exit\n");platform_device_unregister(&rled_pdev);platform_device_unregister(&gled_pdev);platform_device_unregister(&bled_pdev);
}module_exit(led_pdev_exit);MODULE_AUTHOR("embedfire");
MODULE_LICENSE("GPL");

3、定义平台驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/fs.h>#define DEV_MAJOR 243
#define DEV_NAME  "led"static struct class *my_led_class;//结构体led_data来管理我们LED灯的硬件信息
struct led_data {unsigned int led_pin;unsigned int clk_regshift;unsigned int __iomem *va_dr;unsigned int __iomem *va_gdir;unsigned int __iomem *va_iomuxc_mux;unsigned int __iomem *va_ccm_ccgrx;unsigned int __iomem *va_iomux_pad; struct cdev led_cdev;};static int led_cdev_open(struct inode *inode, struct file *filp)
{printk("%s\n", __func__);struct led_data *cur_led = container_of(inode->i_cdev, struct led_data, led_cdev);unsigned int val = 0;val = readl(cur_led->va_ccm_ccgrx);val &= ~(3 << cur_led->clk_regshift);val |= (3 << cur_led->clk_regshift);writel(val, cur_led->va_ccm_ccgrx);writel(5, cur_led->va_iomuxc_mux);writel(0x1F838, cur_led->va_iomux_pad);val = readl(cur_led->va_gdir);val &= ~(1 << cur_led->led_pin);val |= (1 << cur_led->led_pin);writel(val, cur_led->va_gdir);val = readl(cur_led->va_dr);val |= (0x01 << cur_led->led_pin);writel(val, cur_led->va_dr);filp->private_data = cur_led;return 0;
}static int led_cdev_release(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t led_cdev_write(struct file *filp, const char __user * buf,size_t count, loff_t * ppos)
{unsigned long val = 0;unsigned long ret = 0;int tmp = count;struct led_data *cur_led = (struct led_data *)filp->private_data;kstrtoul_from_user(buf, tmp, 10, &ret);val = readl(cur_led->va_dr);if (ret == 0)val &= ~(0x01 << cur_led->led_pin);elseval |= (0x01 << cur_led->led_pin);writel(val, cur_led->va_dr);*ppos += tmp;return tmp;
}static struct file_operations led_cdev_fops = {.open = led_cdev_open,.release = led_cdev_release,.write = led_cdev_write,
};//probe函数中,驱动需要去提取设备的资源,完成字符设备的注册等工作
static int led_pdrv_probe(struct platform_device *pdev)
{struct led_data *cur_led;unsigned int *led_hwinfo;struct resource *mem_dr;struct resource *mem_gdir;struct resource *mem_iomuxc_mux;struct resource *mem_ccm_ccgrx;struct resource *mem_iomux_pad;     dev_t cur_dev;int ret = 0;printk("led platform driver probe\n");//第一步:提取平台设备提供的资源//devm_kzalloc函数申请cur_led和led_hwinfo结构体内存大小cur_led = devm_kzalloc(&pdev->dev, sizeof(struct led_data), GFP_KERNEL);if(!cur_led)return -ENOMEM;led_hwinfo = devm_kzalloc(&pdev->dev, sizeof(unsigned int)*2, GFP_KERNEL);if(!led_hwinfo)return -ENOMEM;/* get the pin for led and the reg's shift *///dev_get_platdata函数获取私有数据,得到LED灯的寄存器偏移量,并赋值给cur_led->led_pin和cur_led->clk_regshiftled_hwinfo = dev_get_platdata(&pdev->dev);cur_led->led_pin = led_hwinfo[0];cur_led->clk_regshift = led_hwinfo[1];/* get platform resource *///利用函数platform_get_resource可以获取到各个寄存器的地址mem_dr = platform_get_resource(pdev, IORESOURCE_MEM, 0);mem_gdir = platform_get_resource(pdev, IORESOURCE_MEM, 1);mem_iomuxc_mux = platform_get_resource(pdev, IORESOURCE_MEM, 2);mem_ccm_ccgrx = platform_get_resource(pdev, IORESOURCE_MEM, 3);mem_iomux_pad = platform_get_resource(pdev, IORESOURCE_MEM, 4);//使用devm_ioremap将获取到的寄存器地址转化为虚拟地址cur_led->va_dr =devm_ioremap(&pdev->dev, mem_dr->start, resource_size(mem_dr));cur_led->va_gdir =devm_ioremap(&pdev->dev, mem_gdir->start, resource_size(mem_gdir));cur_led->va_iomuxc_mux =devm_ioremap(&pdev->dev, mem_iomuxc_mux->start,resource_size(mem_iomuxc_mux));cur_led->va_ccm_ccgrx =devm_ioremap(&pdev->dev, mem_ccm_ccgrx->start,resource_size(mem_ccm_ccgrx));cur_led->va_iomux_pad =devm_ioremap(&pdev->dev, mem_iomux_pad->start,resource_size(mem_iomux_pad));//第二步:注册字符设备cur_dev = MKDEV(DEV_MAJOR, pdev->id);register_chrdev_region(cur_dev, 1, "led_cdev");cdev_init(&cur_led->led_cdev, &led_cdev_fops);ret = cdev_add(&cur_led->led_cdev, cur_dev, 1);if(ret < 0){printk("fail to add cdev\n");goto add_err;}device_create(my_led_class, NULL, cur_dev, NULL, DEV_NAME "%d", pdev->id);/* save as drvdata */ //platform_set_drvdata函数,将LED数据信息存入在平台驱动结构体中pdev->dev->driver_data中platform_set_drvdata(pdev, cur_led);return 0;add_err:unregister_chrdev_region(cur_dev, 1);return ret;
}static int led_pdrv_remove(struct platform_device *pdev)
{dev_t cur_dev; //platform_get_drvdata,获取当前LED灯对应的结构体struct led_data *cur_data = platform_get_drvdata(pdev);printk("led platform driver remove\n");cur_dev = MKDEV(DEV_MAJOR, pdev->id);//cdev_del删除对应的字符设备cdev_del(&cur_data->led_cdev);//删除/dev目录下的设备device_destroy(my_led_class, cur_dev);//unregister_chrdev_region, 注销掉当前的字符设备编号unregister_chrdev_region(cur_dev, 1);return 0;
}static struct platform_device_id led_pdev_ids[] = {{.name = "led_pdev"},{}
};MODULE_DEVICE_TABLE(platform, led_pdev_ids);//led_pdrv中定义了两种匹配模式
//平台总线匹配过程中 ,只会根据id_table中的name值进行匹配,若和平台设备的name值相等,则表示匹配成功; 反之,则匹配不成功,表明当前内核没有该驱动能够支持的设备。
static struct platform_driver led_pdrv = {.probe = led_pdrv_probe,.remove = led_pdrv_remove,.driver.name = "led_pdev",.id_table = led_pdev_ids,
};static __init int led_pdrv_init(void)
{printk("led platform driver init\n");//class_create,来创建一个led类my_led_class = class_create(THIS_MODULE, "my_leds");//调用函数platform_driver_register,注册我们的平台驱动结构体,这样当加载该内核模块时, 就会有新的平台驱动加入到内核中。 第20-27行,注销platform_driver_register(&led_pdrv);return 0;
}
module_init(led_pdrv_init);static __exit void led_pdrv_exit(void)
{printk("led platform driver exit\n");    platform_driver_unregister(&led_pdrv);class_destroy(my_led_class);
}
module_exit(led_pdrv_exit);MODULE_AUTHOR("Embedfire");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("the example for platform driver");

4、编译

makefile文件

KERNEL_DIR=../ebf_linux_kernelARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILEobj-m := led_pdev.o led_pdrv.oall:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
modules clean:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean    

编译成功后,实验目录下会生成两个名为“led_pdev.ko”、” led_pdrv.ko”的驱动模块文件。

linux驱动开发篇(四)—— platform平台设备驱动相关推荐

  1. linux驱动的中断函数,嵌入式Linux驱动开发(四)——字符设备驱动之中断方式以及中断方式获取按键值...

    之前我们完成了关于通过查询的方式获取按键键值的驱动程序,可以参考:嵌入式Linux开发--裸板程序之中断控制器. 虽然读取键值没有什么问题,但是测试程序占用CPU过高,一直在不断的查询,资源消耗过大, ...

  2. Platform平台设备驱动框架

    Platform 平台设备驱动框架 platform平台设备驱动是基于设备驱动模型的,它将总线结构体struct bus_type封装为struct bus_type platform_bus_typ ...

  3. 嵌入式Linux设备驱动程序开发指南9(平台设备驱动)——读书笔记

    平台设备驱动 九.平台设备驱动 9.1 平台设备驱动概述 9.2 GPIO驱动 9.2.1 简介 9.2.2 硬件名称 9.2.3 引脚控制器 9.2.4 引脚控制子系统 9.2.5 GPIO控制器驱 ...

  4. Linux Platform平台设备驱动模型

    Linux总线设备驱动模型主要包含总线.设备.驱动三个部分. 现实总线:一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI.USB.I2C.SPI等的设备而言,这自然不是问 ...

  5. 正点原子linux阿尔法开发板使用——platform平台总线模型

    Linux驱动分离与分层 目的:为了提高软件的重用,跨平台性能!!! 控制器驱动和设备驱动分离!!! 将驱动分离:主机控制器驱动和设备驱动,主机控制器驱动一般是半导体厂家写的.在linux驱动框架下编 ...

  6. 基于Cortex-A7架构的嵌入式linux ARM驱动开发<1>——字符设备驱动开发

    一.什么是字符设备 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI, L ...

  7. 【Kernel】驱动开发学习之Platform平台总线模型

    Platform平台总线模型 一.前言 二.注册Device文件 三.注册Driver文件 四.编写Probe函数 五.完整代码示例 驱动部分 应用部分 一.前言 平台总线模型也交platform总线 ...

  8. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  9. Linux设备驱动开发---USB主机(控制器)与设备驱动(一)

    USB主机控制器与设备驱动---主机侧 一.Linux USB驱动层次 1.USB驱动(主机侧) 2.USB的逻辑组合(4个层次) 二.USB主机(控制器)驱动 1.主机控制器规格 2.主机控制器的相 ...

最新文章

  1. 零基础快速学习Java技术的方法整理
  2. Python 处理带中文 Excel 文件
  3. mysql停止主从_不停止mysql服务配置主从
  4. TCP协议属性设置之SO_LINGER属性
  5. LiveVideoStack线上交流分享 ( 二 ) —— 爱奇艺版权保护技术与维权实践
  6. linux 脚本批量删除,Linux Shell使用Bash脚本批量删除文件
  7. XReport报表工具技术白皮书
  8. CSS的Padding, Margin, Border 的区别
  9. Java知多少(9) import及Java类的搜索路径
  10. 操作系统学习思维导图——2处理器管理
  11. FFmpeg下载地址
  12. 程序员用C/C++打造车牌识别系统!同学眼中只有膜拜!
  13. 传输速率和传播速率的理解
  14. 执念斩长河专栏线代概率--目录
  15. unity 图文混排方案
  16. 微型计算机的ALU部件包括在( )之中,微机原理第二章复习题(附答案)期末考试题...
  17. 国内各类 WebShell 密码大全 爆破、社工用 webshell-password
  18. Google Earth Engine(GEE)——可视化动态图
  19. 人手,人力,人才,人物
  20. Spring Data JPA-JPA对象的四种状态

热门文章

  1. 思科与H3C IPSec 预共享密钥和证书认证 ,NAT穿越综合实验
  2. windows10/11安全中心怎么关闭
  3. 柯尼卡美能达C360怎么开机
  4. 猿人学·爬虫逆向高阶课
  5. bulidroot使能Qt5模块中的蓝牙_NFC功能
  6. L2-030 冰岛人 (25 分)
  7. S50非接触式IC卡存储控制浅谈(M1)
  8. Arduino ESP32 读取SD卡接口选择参考
  9. 事件(events)
  10. 【博主告白】沁园春·新年