<平台设备设备驱动>

a:背景:

平台总线是Linux2.6的设备驱动模型中,关心总线,设备和驱动这3个实体。一个现实的Linux设备和驱动通常需要挂接在一种总线上(比如本身依附于PCI,USB,IIC,SPI等设备而言)。但是在嵌入式系统里面,SoC系统即集成的独立外设控制器,挂接在SoC内存空间的外设却没有这样的总线依附,为了和Linux设备驱动模型理论相互统一,Linux系统开发了Platform_bus这种虚拟总线,相应的设备叫做platform_device ,相应的驱动叫platform_driver。引入的一种虚拟总线,其优势是采用了总总线的模型对设备和驱动进行管理,同时提高程序的可移植性。

b:优势:

Linux platform_driver机制和传统的device_driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)

<平台设备驱动开发流程>

定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_device_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要匹配内核中所以已注册的设备名。

<平台总线>

a:内核数据结构

struct bus_type platform_bus_type = {

.name       = "platform",

.dev_attrs  = platform_dev_attrs,

.match      = platform_match,       //设备和驱动使用match函数来判断是否匹配

.uevent     = platform_uevent,

.pm     = PLATFORM_PM_OPS_PTR,

};

a-1:函数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);

/* match against the id table first */

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函数首先判断是否由id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数。

<平台设备>

a:内核数据结构

struct platform_device{

const char *name ;//设备名

int id;//设备编号,配合设备使用

struct device dev;

u32 num_resources;

struct resource  *resource; //设备资源

}

a-1:设备资源

定义硬件资源,比如设备内存,中断号,DMA通道

struct resource{

resource_size_t char;

resource_size_t end;

const char *name;

unsigned long flags; //用于表明多个资源中的某一种资源,比如中断号,内存。

struct resource *parent,*siling ,*child;

};

a-1-1:"unsigned long flags",这里的flags可以取以下值,表示不同的设备资源

IORESOURCE_IO//IO资源

IORESOURCE_MEN//设备内存资源

IORESOURCE_IRQ//设备中断资源

IORESOURCE_DMA//设备DMA资源

a-1-2:一般驱动中调用该函数获得这些资源

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

b:注册平台设备

int platform _device _register (struct platform_device *pdev )

<平台驱动>

a:内核数据结构

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 pm_ext_ops *pm;
    struct device_driver driver;
};

a-1:函数int (*probe)(struct platform_device *);

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{

struct s3c24xx_i2c *i2c = &s3c24xx_i2c;

struct resource *res;

int ret;

/* find the clock and enable it */

i2c->dev = &pdev->dev;

i2c->clk = clk_get(&pdev->dev, "i2c");

if (IS_ERR(i2c->clk)) {

dev_err(&pdev->dev, "cannot get clock\n");

ret = -ENOENT;

goto out;

}

dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

clk_enable(i2c->clk);

/* map the registers */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* 获取设备的IO资源地址 */

if (res == NULL) {

dev_err(&pdev->dev, "cannot find IO resource\n");

ret = -ENOENT;

goto out;

}

i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1, pdev->name); /* 申请这块IO Region */

if (i2c->ioarea == NULL) {

dev_err(&pdev->dev, "cannot request IO\n");

ret = -ENXIO;

goto out;

}

i2c->regs = ioremap(res->start, (res->end-res->start)+1); /* 映射至内核虚拟空间 */

if (i2c->regs == NULL) {

dev_err(&pdev->dev, "cannot map IO\n");

ret = -ENXIO;

goto out;

}

dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);

/* setup info block for the i2c core */

i2c->adap.algo_data = i2c;

i2c->adap.dev.parent = &pdev->dev;

/* initialise the i2c controller */

ret = s3c24xx_i2c_init(i2c);

if (ret != 0)

goto out;

/* find the IRQ for this unit (note, this relies on the init call to ensure no current IRQs pending */

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /* 获取设备IRQ中断号 */

if (res == NULL) {

dev_err(&pdev->dev, "cannot find IRQ\n");

ret = -ENOENT;

goto out;

}

ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c);申请IRQ

……

return ret;

}

b:注册总线驱动

int platform_driver_register(struct platform_driver*)

<平台私有数据>

a:struct platform_data{ }

设备除了可以再bsp中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存,DMA通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不宜直接放置在设备驱动本身,因此platform也提供了platform_data的支持,platform_data的形式是自定义的,比如对于dm9000网卡来说,platform_data中可以存放mac地址,总线宽度,板上有误eeprom等信息。

a-1:如对于 DM9000 网卡而言, platform_data 为一个 dm9000_plat_data 结构体,我们就可以将 MAC 地址、总线宽度、板上有无 EEPROM 信息等放入 platform_data:

static struct dm9000_plat_data ldd6410_dm9000_platdata = {
   .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
   .dev_addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 },
};
static struct platform_device ldd6410_dm9000 = {
   .name= "dm9000",
   .id= 0,
   .num_resources= ARRAY_SIZE(ldd6410_dm9000_resource),
   .resource =ldd6410_dm9000_resource,
   .dev = {
       .platform_data = &ldd6410_dm9000_platdata, //定义和初始化来自上面
   }
};

<凭他设备驱动实例>

#include <linux/module.h>

#include <linux/device.h>

#include <linux/platform_device.h>

#include <linux/ioport.h>

static struct resource beep_resource[] =

{

[0] ={

.start = 0x114000a0,

.end =  0x114000a0 + 0x4,

.flags = IORESOURCE_MEM,

},

[1] ={

.start = 0x139D0000,

.end =  0x139D0000 + 0x14,

.flags = IORESOURCE_MEM,

}

};

static void hello_release(struct device *dev)

{

printk("hello_release\n");

return ;

}

static struct platform_device hello_device=

{

.name = "bigbang",

.id = -1,

.dev.release = hello_release,

.num_resources = ARRAY_SIZE(beep_resource),

.resource = beep_resource,

};

static int hello_init(void)

{

printk("hello_init");

return platform_device_register(&hello_device);

}

static void hello_exit(void)

{

printk("hello_exit");

platform_device_unregister(&hello_device);

return;

}

MODULE_LICENSE("GPL");

module_init(hello_init);

module_exit(hello_exit);

2、driver.c

[cpp] view plain copy

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/platform_device.h>

#include <asm/io.h>

static int major = 250;

static int minor=0;

static dev_t devno;

static struct class *cls;

static struct device *test_device;

#define TCFG0         0x0000

#define TCFG1         0x0004

#define TCON          0x0008

#define TCNTB0        0x000C

#define TCMPB0        0x0010

static unsigned int *gpd0con;

static void *timer_base;

#define  MAGIC_NUMBER    'k'

#define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)

#define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)

#define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)

static void fs4412_beep_init(void)

{

writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);

writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);

writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );

writel (500, timer_base +TCNTB0  );

writel (250, timer_base +TCMPB0 );

writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );

}

void fs4412_beep_on(void)

{

writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );

}

void fs4412_beep_off(void)

{

writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );

}

static void beep_unmap(void)

{

iounmap(gpd0con);

iounmap(timer_base);

}

static int beep_open (struct inode *inode, struct file *filep)

{

fs4412_beep_on();

return 0;

}

static int beep_release(struct inode *inode, struct file *filep)

{

fs4412_beep_off();

return 0;

}

#define BEPP_IN_FREQ 100000

static void beep_freq(unsigned long arg)

{

writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );

writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );

}

static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)

{

switch(cmd)

{

case BEEP_ON:

fs4412_beep_on();

break;

case BEEP_OFF:

fs4412_beep_off();

break;

case BEEP_FREQ:

beep_freq( arg );

break;

default :

return -EINVAL;

}

return 0;

}

static struct file_operations beep_ops=

{

.open     = beep_open,

.release = beep_release,

.unlocked_ioctl      = beep_ioctl,

};

static int beep_probe(struct platform_device *pdev)

{

int ret;

printk("match ok!");

gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);

timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);

devno = MKDEV(major,minor);

ret = register_chrdev(major,"beep",&beep_ops);

cls = class_create(THIS_MODULE, "myclass");

if(IS_ERR(cls))

{

unregister_chrdev(major,"beep");

return -EBUSY;

}

test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello

if(IS_ERR(test_device))

{

class_destroy(cls);

unregister_chrdev(major,"beep");

return -EBUSY;

}

fs4412_beep_init();

return 0;

}

static int beep_remove(struct platform_device *pdev)

{

beep_unmap();

device_destroy(cls,devno);

class_destroy(cls);

unregister_chrdev(major,"beep");

return 0;

}

static struct platform_driver beep_driver=

{

.driver.name = "bigbang",

.probe = beep_probe,

.remove = beep_remove,

};

static int beep_init(void)

{

printk("beep_init");

return platform_driver_register(&beep_driver);

}

static void beep_exit(void)

{

printk("beep_exit");

platform_driver_unregister(&beep_driver);

return;

}

MODULE_LICENSE("GPL");

module_init(beep_init);

module_exit(beep_exit);

3、makefile

[cpp] view plain copy

ifneq  ($(KERNELRELEASE),)

obj-m:=device.o driver.o

$(info "2nd")

else

#KDIR := /lib/modules/$(shell uname -r)/build

KDIR := /home/fs/linux/linux-3.14-fs4412

PWD:=$(shell pwd)

all:

$(info "1st")

make -C $(KDIR) M=$(PWD) modules

clean:

rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order

endif

4、test.c

[cpp] view plain copy

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

main()

{

int fd,i,lednum;

fd = open("/dev/beep",O_RDWR);

if(fd<0)

{

perror("open fail \n");

return ;

}

sleep(10);

close(fd);

}

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

转载于:https://www.cnblogs.com/big-devil/p/8590028.html

Linux驱动之平台设备相关推荐

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

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

  2. 【linux驱动之字符设备驱动基础】

    linux驱动之字符设备驱动基础 文章目录 linux驱动之字符设备驱动基础 前言 一.开启驱动学习之路 二.驱动预备知识 三.什么是驱动? 3.1 驱动概念 3.2 linux 体系架构 3.3 模 ...

  3. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    参考原文:https://www.kancloud.cn/yueqian_scut/emlinux/106829 对原文笔误地方做了修改.重新排版 目录 字符设备驱动.平台设备驱动.设备驱动模型.sy ...

  4. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

  5. linux驱动之字符设备

    linux驱动之字符设备 linux驱动设备分类 linux驱动分为了三种驱动: 字符设备: 字符设备和应用程序之间是以字节进行进行数据交换的.在进行数据交换的时候数据是以一定顺序进行传输的,传输是实 ...

  6. linux uart寄存器 代替 printk,Linux驱动学习之设备树(设备树下的LED驱动实验),...

    Linux驱动学习之设备树(设备树下的LED驱动实验), 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设 ...

  7. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

  8. 【Linux驱动开发】设备树详解(二)设备树语法详解

    ​ 活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...

  9. 【Linux驱动】字符设备驱动

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面 ...

最新文章

  1. 专家观点:人工智能,还能再“聪明”些吗?
  2. 阿里NASA计划:城市大脑成智能研究第一平台
  3. 双击word,excel等office文档很慢
  4. [数据结构] 二叉树基础
  5. 《Linux内核分析》实践2
  6. Android框架式编程之BufferKnife
  7. python虚拟环境和pyenv_Python多版本管理器pyenv和虚拟环境pyenv-virtualenv的安装设置...
  8. 清空VS2003/VS2005最近打开解决方案记录
  9. CF Round#436 div2
  10. CISCO、JUNIPER网络技术训练中心ITAA等级四(RS)学习计划蓝图(version 1.0)
  11. dreamweaver cs6配置phonegap环境
  12. Raki的读paper小记:WaveTransformer: A Novel Architecture for Audio Captioning
  13. cocos 《成语接龙》---Button(按钮)组件
  14. mysql外文文献中英文3千字_MySQL数据库管理外文中英文翻译文献.doc
  15. nrf52832 comp for sdk 15.2.0
  16. NAND FLASH 坏块相关知识
  17. Bill Gekas儿童摄影作品欣赏
  18. 一些开源代码网站介绍(持续添加中....)
  19. 使用GPU进行神经网络计算详解
  20. OpenVZ虚拟机安装过程详解

热门文章

  1. Omi框架学习之旅 - 插件机制之omi-touch 及原理说明
  2. haslayout详解
  3. (树)判断二叉树是否为BST
  4. MessageBox只弹出一次
  5. 华为复制加密门禁卡_MIUI12轻体验:关于模拟门禁卡,你想知道的都在这里
  6. python中xrange函数_python中xrange和range的区别
  7. 【Linux 内核】CFS 调度器 ④ ( 调度子系统组件模块 | 主调度器、周期性调度器 | 调度器类 )
  8. 【错误记录】IntelliJ IDEA 中右键点击源码目录选择 New 选项 没有创建 Java Class 选项 ( 将对应的源码目录标记为 Sources 选项 )
  9. 【C 语言】字符串模型 ( 两头堵模型 | 将 两头堵模型 抽象成业务模块函数 | 形参返回值 | 函数返回值 | 形参指针判空 | 形参返回值操作 )
  10. 【Android 逆向】Android 权限 ( adb 降权相关的属性 | ro.secure 属性 | ro.debuggable 属性 | service.adb.root 属性 )