平台总线(platform_bus)的需求来源?
随着soc的升级,S3C2440->S3C6410->S5PV210->4412,以前的程序就得重新写一遍,做着大量的重复工作,
人们为了提高效率,发现控制器的操作逻辑(方法)是一样的,只有寄存器地址不一样,如果将与硬件有关的
代码(platform_device)和驱动代码(platform_driver)分开,升级soc后,因为驱动方式一样,
只需要修改与硬件有关的代码就可以,实现一个驱动控制多个设备。

平台(platform)总线是一种虚拟的总线,在 /sys/bus/platform 目录可以看到。
平台总线三要素:平台总线、平台设备、平台驱动
平台总线原则:先分离,后合并

分离:
将设备信息封装成 platform_device,将驱动信息封装成 platform_driver,并为各自起名称,
然后将 platform_device 中的 struct device 和 platform_driver 中的 struct device_driver 分别注册到设备链表和驱动链表中。

int platform_device_register(struct platform_device *pdev)

  return platform_device_add(pdev);

    ret = device_add(&pdev->dev);

int platform_driver_register(struct platform_driver *drv)

  return driver_register(&drv->driver);

    ret = bus_add_driver(drv);

合并:
在系统每注册一个设备(驱动)时,平台总线会找与之匹配的驱动(设备),匹配原则是名称相同。

装载(insmod)时设备和驱动没有顺序,卸载(rmmod)时必须先卸载设备文件,因为卸载设备会调用驱动中的 remove 函数

// 下面的“|”表示包含于上一个之中
// 描述设备的信息
struct platform_device {
const char * name; // 用于和platform_driver进行匹配的名字--自定义
int id; // 一般直接填-1,区分不同的控制组
struct device dev; // 父类
|
void (*release)(struct device *dev); // 设备卸载时调用的函数
void *platform_data; // 匹配后传递的自定义数据
... ...
u32 num_resources; // 资源的个数
struct resource * resource; // 描述资源信息
|
resource_size_t start; // 起始位置
resource_size_t end; // 结束位置
const char *name; // 自定义
unsigned long flags; // 区分不同的资源,一般是 内存或者中断资源
... ...
};
// 描述设备的操作方法
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 char *name; // 该名字可以用于匹配,但比id_table中的name优先级低
// 此名称在 /sys/bus/platform/drivers/xxx
... ...
const struct platform_device_id *id_table;
|
char name[PLATFORM_NAME_SIZE]; // 用于和platform_device的名字进行匹配,优先级高
};
// 平台总线
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, // 用于匹配,此函数可以看出匹配名称的优先级
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};

// 注册 platform_devive

int platform_device_register(struct platform_device *pdev);

// 注销 platform_devive

void platform_device_unregister(struct platform_device *pdev);

// 注册 platform_driver

int platform_driver_register(struct platform_driver *drv);

// 注销 platform_driver

void platform_driver_unregister(struct platform_driver *drv);

// 批量注册pdev

int platform_add_devices(struct platform_device **devs, int num);

获取资源的接口:

// 通过类型和编号获取资源

// 参数1:pdev

// 参数2:获取的资源类型

// 参数3:获取的资源编号

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);

// 通过类型和名称获取资源

// 参数3:获取资源的名称

struct resource * platform_get_resource_byname(struct platform_device * dev,unsigned int type,const char * name);

// 通过编号获取中断资源

int platform_get_irq(struct platform_device * dev,unsigned int num);

// 通过名称获取中断资源

int platform_get_irq_byname(struct platform_device * dev,const char * name);

// 重点:资源编号一定是按照同种类型来排序的
struct resource led_res[] = {
[] = { // 获取内存资源时,此内存资源的编号为0
... ...
.flags = IORESOURCE_MEM,
},
[] = { // 获取中断资源时,此中断资源的编号为0
... ...
.flags = IORESOURCE_IRQ,
},
[] = { // 获取内存资源时,此内存资源的编号为1
... ...
.flags = IORESOURCE_MEM,
},
};

plat_led_dev.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h> #include "plat_led.h" #define GPL2_0 0x11000100
#define GP_SIZE 8 // 平台自定义数据,与硬件相关
struct regled led_reg = {
.ctl_clr = 0x0f,
.ctl_set = 0x01,
.dat_clr = 0x01,
.dat_set = 0x01,
}; static struct resource led_res[] = {
[] = {
.start = GPL2_0,
.end = GPL2_0 + GP_SIZE - ,
.name = "led0",
.flags = IORESOURCE_MEM,
},
[] = {
.start = ,
.end = ,
.name = "virt_irq",
.flags = IORESOURCE_IRQ,
},
}; void plat_led_release(struct device *dev)
{
// 为了卸载模块时不报错误
} struct platform_device led_pdev = {
.name = "plat_led",
.id = -,
.dev = {
.platform_data = &led_reg,
.release = plat_led_release,
},
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
}; static int __init plat_led_dev_init(void)
{
platform_device_register(&led_pdev); return ;
} static void __exit plat_led_dev_exit(void)
{
platform_device_unregister(&led_pdev);
} module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aaron Lee");

plat_led_drv.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h> #include <asm/io.h>
#include <asm/uaccess.h> #include "plat_led.h" struct samsung *platled; static int platled_open(struct inode *inode, struct file *fops)
{
writeb(readb(platled->reg_base+) & (~platled->reg->dat_clr), platled->reg_base+); return ;
} static int platled_close(struct inode *inode, struct file *fops)
{
return ;
} static ssize_t platled_write(struct file *fops, const char __user *buf, size_t size, loff_t *fpos)
{
int value;
//暂时忽略返回值
copy_from_user(&value, buf, size); if (value)
writeb((readb(platled->reg_base+) & (~platled->reg->dat_clr)) | platled->reg->dat_set, platled->reg_base+);
else
writeb(readb(platled->reg_base+) & (~platled->reg->dat_clr), platled->reg_base+); return ;
} const struct file_operations platled_fops = {
.open = platled_open,
.release = platled_close,
.write = platled_write,
}; static int led_register(void)
{
int ret; platled = kmalloc(sizeof(struct samsung), GFP_KERNEL);
if (platled == NULL)
{
printk("kmalloc fail!\n");
return -ENOMEM;
} platled->major = register_chrdev(, "plat_led", &platled_fops);
if (platled->major < )
{
printk("register_chrdev fail!\n");
ret = -EFAULT;
goto chrdev_err;
} platled->cls = class_create(THIS_MODULE, "plat_led");
if (platled->cls < )
{
printk("class_create fail!\n");
ret = -EFAULT;
goto class_err;
} platled->dev = device_create(platled->cls, NULL, MKDEV(platled->major, ), NULL, "plat_led");
if (platled->dev < )
{
printk("device_create fail!\n");
ret = -EFAULT;
goto device_err;
} return ; device_err:
class_destroy(platled->cls); class_err:
unregister_chrdev(platled->major, "plat_led"); chrdev_err:
kfree(platled); return ret;
} static void led_unregister(void)
{
device_destroy(platled->cls, MKDEV(platled->major, ));
class_destroy(platled->cls);
unregister_chrdev(platled->major, "plat_led");
kfree(platled);
} int plat_led_probe(struct platform_device *pdev)
{
int ret;
struct resource *res; ret = led_register();
if (ret < )
{
printk("plat_led_probe fail\n");
return -EFAULT;
} res = platform_get_resource(pdev, IORESOURCE_MEM, );
if (res == NULL)
{
printk("platform_get_resource fail\n");
return -ENODEV;
}
// 获取平台自定义数据
platled->reg = pdev->dev.platform_data; platled->reg_base = ioremap(res->start, resource_size(res));
writel((readl(platled->reg_base) & (~platled->reg->ctl_clr)) | platled->reg->ctl_set, platled->reg_base); return ;
} int plat_led_remove(struct platform_device *pdev)
{
iounmap(platled->reg_base);
led_unregister(); return ;
} const struct platform_device_id led_id_table[] = {
{"plat_led", 0x1234}, //第二个整数是自定义
}; struct platform_driver led_pdrv = {
.probe = plat_led_probe,
.remove = plat_led_remove,
.driver = {
.name = "red_led",
},
.id_table = led_id_table,// name用于匹配
}; static int __init plat_led_drv_init(void)
{
platform_driver_register(&led_pdrv); return ;
} static void __exit plat_led_drv_exit(void)
{
platform_driver_unregister(&led_pdrv);
} module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aaron Lee");

plat_led.h

#ifndef __PLAT_LED_H_
#define __PLAT_LED_H_ struct regled {
unsigned long ctl_clr;
unsigned long ctl_set;
unsigned long dat_clr;
unsigned long dat_set;
}; struct samsung {
int major;
struct class *cls;
struct device *dev;
struct regled *reg;
void *reg_base;
}; #endif

Linux platform平台总线、平台设备、平台驱动的更多相关文章

  1. 从需求的角度去理解Linux系列:总线、设备和驱动

    笔者成为博客专家后整理以前原创的嵌入式Linux系列博文,现推出以让更多的读者受益. <从需求的角度去理解linux系列:总线.设备和驱动>是一篇有关如何学习嵌入式Linux系统的方法论文 ...

  2. linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxx ...

  3. 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  4. 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

    本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...

  5. 迅为4412开发板Linux驱动教程——总线_设备_驱动注冊流程具体解释

    视频下载地址: 驱动注冊:http://pan.baidu.com/s/1i34HcDB 设备注冊:http://pan.baidu.com/s/1kTlGkcR 总线_设备_驱动注冊流程具体解释 • ...

  6. 《Linux总线、设备与驱动》USB设备发现机制

    说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考. 一.程序在内核中的位置 1.usb host做为pci总线下的一个设备存在(嵌入式系统中有可能也会直接挂在 ...

  7. linux设备驱动归纳总结(八):2.总线、设备和驱动的关系【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110295.html linux设备驱动归纳总结(八):2.总线.设备和驱动的关系 xxxxxxxxx ...

  8. platform总线,设备,驱动的注册

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  10. 【Linux开发】linux设备驱动归纳总结(八):2.总线、设备和驱动的关系

    linux设备驱动归纳总结(八):2.总线.设备和驱动的关系 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. 移动端解决悬浮层(悬浮header、footer)会遮挡住内容的方法

    固定Footer Bootstrap框架提供了两种固定导航条的方式: ☑  .navbar-fixed-top:导航条固定在浏览器窗口顶部 ☑  .navbar-fixed-bottom:导航条固定在 ...

  2. day34 GIL锁 线程队列 线程池

    一.Gil锁(Global Interpreter Lock) python全局解释器锁,有了这个锁的存在,python解释器在同一时间内只能让一个进程中的一个线程去执行,这样python的多线程就无 ...

  3. 使用npm install时一直报错-4048 operation not permitted

    一:权限问题 首先看到operation not permitted我们能想到权限问题,所以这时候我们可以以管理员身份运行cmd或者直接快捷键Win+X来打开. 二:依赖包错误 如上图,根据错误日志我 ...

  4. nisght heap increase

    sudo gedit /usr/local/cuda-5.5/libnsight/nsight.ini --launcher.defaultActionopenFile-vm../jre/bin/ja ...

  5. iOS 在工程内部创建一个静态库target

    当你在开发项目的时候需要把公用的东西打包出来,其他项目方便使用的时候,打包成静态库是你的最优选择,在工程内部开发的时候新建一个target进行静态库的开发可以使你的开发调试更加方便而不是单独新建一个工 ...

  6. 重构 改善既有代码的设计 Replace Method with Method Object(以函数对象取代函数)

    你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段.然后你可以在同一个对象中将这个大型函数分解为多个小型 ...

  7. L328 What Is Millennial Burnout?

    What Is Millennial Burnout?Do you often feel stressed? Does the pace of life make you feel like you' ...

  8. poi读取excel内容工具类

    该工具类可以读取excel2007,excel2003等格式的文件,xls.xlsx文件格式 package com.visolink; import org.apache.poi.hssf.user ...

  9. 前端-URL到页面显示的全过程

    大概有以下几步: DNS解析(浏览器缓存→系统缓存→路由器缓存→ISP DNS缓存→从根域名服务器递归搜索) 建立TCP链接(TCP的三次握手) 浏览器向服务器发送HTTP请求 服务器返回结果给浏览器 ...

  10. 2017-2018 ACM-ICPC NEERC B题Berland Army 拓扑排序+非常伤脑筋的要求

    题目链接:http://codeforces.com/contest/883/problem/B There are n military men in the Berland army. Some ...