09-S3C2440驱动学习(三)嵌入式linux-platform平台总线驱动程序及分离分层构建驱动框架

最新更新时间:2022-05-28来源: eefocus关键字:S3C244  总线驱动 手机看文章 扫描二维码
随时随地手机看文章

简介Platform:


内核中有IIC总线、PCI总线、串口总线、SPI总线、can总线、单总线等,一些设备可以挂载在这些总线上,然后通过总线的match进行设备和驱动的匹配。但是有些设备并不属于这些总线,于是引入了虚拟总线,也就是Platform总线,对于的设备叫做Platform设备,对于的驱动叫做Platform驱动。


平台设备框图:

Platform总线:


struct bus_type platform_bus_type = {

.name = "platform",

.dev_attrs = platform_dev_attrs,

.match = platform_match,

.uevent = platform_uevent,

.suspend = platform_suspend,

.suspend_late = platform_suspend_late,

.resume_early = platform_resume_early,

.resume = platform_resume,

};

设备和驱动的匹配函数:


static int platform_match(struct device * dev, struct device_driver * drv)

{

struct platform_device *pdev = container_of(dev, struct platform_device, dev);

 

 

return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}

Platform设备:

struct platform_device {

const char * name;

u32 id;

struct device dev;

u32 num_resources;

struct resource * resource;

};

资源结构体的定义:


struct resource {

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

Platform驱动:

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;

};


一、分离分层构建驱动框架

input子系统中,采用了分离分层的方式构建驱动程序框架。input.c为核心层,其下有左边的硬件设备相关,和右边的软件handler相关处理函数。

二、platform总线驱动框架分析

一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。linux在系统启动时就注册了platform总线.

1.参考:Gpio_keys.c (driversinputkeyboard)来看一个例子

2.Bus-drv-dev模型: 


Bus结构体;


drv结构体:比较稳定的代码


dev结构体:硬件相关


三个都是结构体。

static int __init gpio_keys_init(void)

{

return platform_driver_register(&gpio_keys_device_driver);//注册平台设备。

}



总线是虚拟平台总线 platform_bus_type


int platform_driver_register(struct platform_driver *drv)

{

drv->driver.bus = &platform_bus_type;

if (drv->probe)

drv->driver.probe = platform_drv_probe;

if (drv->remove)

drv->driver.remove = platform_drv_remove;

if (drv->shutdown)

drv->driver.shutdown = platform_drv_shutdown;

if (drv->suspend)

drv->driver.suspend = platform_drv_suspend;

if (drv->resume)

drv->driver.resume = platform_drv_resume;

return driver_register(&drv->driver);

}



platform_driver_register--》driver_register


platform_driver_register:把driver的结构体放到bus中的drv链表中,还会和设备比较drv支不支持dev。


虚拟平台总线里面有个match函数


struct bus_type platform_bus_type = {

.name= "platform",

.dev_attrs= platform_dev_attrs,

.match= platform_match,

.uevent= platform_uevent,

.suspend= platform_suspend,

.suspend_late= platform_suspend_late,

.resume_early= platform_resume_early,

.resume= platform_resume,

};


根据名字判断是否匹配,匹配则调用.probe函数


static int platform_match(struct device * dev, struct device_driver * drv)

{

struct platform_device *pdev = container_of(dev, struct platform_device, dev);

return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}



3.


driver_register(稳定的代码):


(1)    会把drv放入Bus总线的drv链表里面


(2)    从bus的dev链表取出每个dev,用bus的match函数一一比较,判断dev能否支持drv。若可以支持调用drv的.probe函数


device_add(硬件相关):


(1)    把dev结构体放入bus的dev链表里面去


(2)    从bus的drv链表取出每个drv,用bus的match函数一一比较,判断drv能否支持dev。若可以支持调用drv的.probe函数


Bus里面有一个比较函数.match比较drv是否支持这个dev,能支持的话调用里面的.probe函数


4.自己写一个led的platform总线驱动程序


(1)参考Mach-smdk2440.c (archarmmach-s3c2440)


(2)led_dev.c

static struct platform_device s3c2440_device_sdi = {

    .name         = "s3c2440-sdi",

    .id       = -1,

    .num_resources    = ARRAY_SIZE(s3c2440_sdi_resource),

    .resource     = s3c2440_sdi_resource,

};

定义了一个平台设备,平台设备里面含有某些资源,寄存器起始地址,那个引脚等。


/* SDI */

static struct resource s3c2440_sdi_resource[] = {

    [0] = {

        .start = S3C2410_PA_SDI,

        .end   = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,

        .flags = IORESOURCE_MEM,

    },

    [1] = {

        .start = IRQ_SDI,

        .end   = IRQ_SDI,

        .flags = IORESOURCE_IRQ,

    }

};


入口函数:注册一个平台设备



platform_device_register最后会执行如上:放入平台总线的设备链表里面去。


加上头文件

 (3)另一边led_drv.c

 

参考:


(4)编译:


(5)回到以前实现led:注册一个字符设备,构造一个结构体

 

staticvolatile unsigned long *gpio_con;


staticvolatile unsigned long *gpio_dat;寄存器


       res = platform_get_resource(pdev,IORESOURCE_MEM, 0);


       gpio_con = ioremap(res->start,res->end - res->start + 1);//大小


staticint pin;引脚


       res = platform_get_resource(pdev,IORESOURCE_IRQ, 0);


       pin=res->start;

(6).probe做什么自己决定,自己的核心内容


三、platform总线驱动代码实现

 (1)led_dev.c 分配/设置/注册一个platform_device


#include

#include

 

#include

 

#include

#include

#include

#include

#include

#include

#include

#include

 

 

/* 分配/设置/注册一个platform_device */

 

static struct resource led_resource[] = {

    [0] = {

        .start = 0x56000050,

        .end   = 0x56000050 + 8 - 1,

        .flags = IORESOURCE_MEM,

    },

    [1] = {

        .start = 5,

        .end   = 5,

        .flags = IORESOURCE_IRQ,

    }

 

};

 

static void led_release(struct device * dev)

{

}

 

 

static struct platform_device led_dev = {

    .name         = "myled",

    .id       = -1,

    .num_resources    = ARRAY_SIZE(led_resource),

    .resource     = led_resource,

    .dev = { 

    .release = led_release, 

},

};

 

static int led_dev_init(void)

{

platform_device_register(&led_dev);

return 0;

}

 

static void led_dev_exit(void)

{

platform_device_unregister(&led_dev);

}

 

module_init(led_dev_init);

module_exit(led_dev_exit);

 

MODULE_LICENSE("GPL");



(2)led_drv.c 分配/设置/注册一个platform_driver


/* 分配/设置/注册一个platform_driver */

 

#include

#include

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

static int major;

static struct class *cls;

static volatile unsigned long *gpio_con;

static volatile unsigned long *gpio_dat;

static int pin;

 

static int led_open(struct inode *inode, struct file *file)

{

//printk("first_drv_openn");

/* 配置为输出 */

*gpio_con &= ~(0x3<<(pin*2));

*gpio_con |= (0x1<<(pin*2));

return 0;

}

 

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

{

int val;

 

//printk("first_drv_writen");

 

copy_from_user(&val, buf, count); // copy_to_user();

 

if (val == 1)

{

// 点灯

*gpio_dat &= ~(1< }

else

{

// 灭灯

*gpio_dat |= (1< }

return 0;

}

 

 

static struct file_operations led_fops = {

    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open   =   led_open,     

.write = led_write,    

};

 

static int led_probe(struct platform_device *pdev)

{

struct resource *res;

 

/* 根据platform_device的资源进行ioremap */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

gpio_con = ioremap(res->start, res->end - res->start + 1);

gpio_dat = gpio_con + 1;

 

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

pin = res->start;

 

/* 注册字符设备驱动程序 */

 

printk("led_probe, found ledn");

 

major = register_chrdev(0, "myled", &led_fops);

 

cls = class_create(THIS_MODULE, "myled");

 

class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */

return 0;

}

 

static int led_remove(struct platform_device *pdev)

{

/* 卸载字符设备驱动程序 */

/* iounmap */

printk("led_remove, remove ledn");

 

class_device_destroy(cls, MKDEV(major, 0));

class_destroy(cls);

unregister_chrdev(major, "myled");

iounmap(gpio_con);

return 0;

}

 

struct platform_driver led_drv = {

.probe = led_probe,

.remove = led_remove,

.driver = {

.name = "myled",

}

};

 

 

static int led_drv_init(void)

{

platform_driver_register(&led_drv);

return 0;

}

static void led_drv_exit(void)

{

platform_driver_unregister(&led_drv);

}

module_init(led_drv_init);

module_exit(led_drv_exit);

MODULE_LICENSE("GPL");



(3)makefile


KERN_DIR = /work/system/linux-2.6.22.6

 

all:

make -C $(KERN_DIR) M=`pwd` modules 

 

clean:

make -C $(KERN_DIR) M=`pwd` modules clean

rm -rf modules.order

 

obj-m += led_drv.o

obj-m += led_dev.o


(4)测试程序


#include

#include

#include

#include

 

/* led_test on

 * led_test off

 */

int main(int argc, char **argv)

{

int fd;

int val = 1;

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

if (fd < 0)

{

printf("can't open!n");

}

if (argc != 2)

{

printf("Usage :n");

printf("%s n", argv[0]);

return 0;

}

 

if (strcmp(argv[1], "on") == 0)

[1] [2]
关键字:S3C244  总线驱动 编辑:什么鱼 引用地址:09-S3C2440驱动学习(三)嵌入式linux-platform平台总线驱动程序及分离分层构建驱动框架

上一篇:10-S3C2440驱动学习(四)嵌入式linux-LCD驱动程序
下一篇:08-S3C2440驱动学习(二)嵌入式linux-input子系统分析与应用

推荐阅读

老查的ARM学习笔记:chapter-2(linux总线设备驱动详解)
1 总线设备驱动模型概述随着技术的不断进步,系统的拓扑结构也越来越复杂,对智能电源管理,热插拔的支持要求也越来越高,2.3内核已经难以满足这些要求,为了适应这种形势需要,linux2.6内核提供了全新的内核设备模型。总线的作用就是感知设备是否连接上usb,网卡等等,总线设备驱动模型更好的支持热插拔的设备,也更好的提供移植性。在设备模型中,我们将看到,设备驱动主要是由总线,驱动程序,设备三个部分构成,通过这三个标准部件,把各种纷繁杂乱的设备归结过来,达到简化设备驱动编写的目的,下面我们就将按三个部分总线,驱动和设备来学习一下。2 总线1 总线的描述在Linux 内核中, 总线由bus_type 结构表示,在这个结构中最重要的是总线名称
发表于 2022-05-26
老查的ARM学习笔记:chapter-2(linux<font color='red'>总线</font>设备<font color='red'>驱动</font>详解)
通过CAN总线控制VESC驱动直流无刷电机
最近在驱动一个直流无刷电机,驱动这一块不是我的研究重点,只是拿来用。但系统上用到CAN总线,找来找去找到了VESC这种神级物品,自然是拿一块来玩玩。拿到我手上的VESC是国内某工作室的改版VESC V6.4(应部分网友需求,放出链接)。硬件方案是STM32F405+DRV8301+NVMFS5C628,带有CAN口、PPM口、USB口。一个完全不知道参数的星型直流无刷电机,就这么1分钟就能转动。不得不说,本杰明大神的VESC Tool真是个神器,傻瓜式的一键调参。但是,本人的需求并不是通过VESC Tool让电机转速来,而是通过CAN口来向VESC下发指令,间接地控制直流无刷电机按需转动。一开始把VESC源代码拿出来看,各种各样的线
发表于 2022-04-20
通过CAN<font color='red'>总线</font>控制VESC<font color='red'>驱动</font>直流无刷电机
基于虚拟仪器和CAN总线集成方案实现车用驱动电机测试平台的设计
引言能源短缺和环保问题促使人们转向开发低污染或者零污染的清洁汽车。燃料电池汽车被认为是最有希望替代内燃机汽车成为下一代公路运输工具的主流。无论是纯电动、混合动力还是燃料电池汽车,都以电动机作为驱动力源。一套适用的车用驱动电机的测试平台对于整车动力系统的开发非常重要。然而目前国内的电机测试平台一般不是针对车用驱动电机而设计,而且自动化程度不高,无法满足测试的要求。因此需要开发一套专用的车用驱动电机测试平台,这对于整车动力系统的设计及优化至关重要。虚拟仪器技术是近几年在自动化测试和控制领域发展起来的一项新技术。其代表产品为美国NI 仪器公司的LabVIEW,目前在包括汽车行业的众多领域得到广泛应用。本文结合燃料电池轿车的技术特点和要求,
发表于 2022-04-20
基于虚拟仪器和CAN<font color='red'>总线</font>集成方案实现车用<font color='red'>驱动</font>电机测试<font color='red'>平台</font>的设计
第32章 STM32F429的SPI总线应用之驱动W25QXX(支持查询,中断和DMA)
32.1 初学者重要提示学习本章节前,务必优先学习第31章。W25Q64FV属于NOR型Flash存储芯片。W25Q64FV手册下载地址:链接 (这是一个超链接),当前章节配套例子的Doc文件件里面也有存放。本章第3小节整理的知识点比较重要,务必要了解下,特别是页编程和页回卷。对SPI Flash W25QXX的不同接线方式(1线,2线或者4线,这里的线是指的数据线),编程命令是不同的。W25Q64FV最高支持104MHz,但最高读命令03H速度是50MHz。文件bsp_spi_bus.c文件公共的总线驱动文件,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备的配置。函数sf_WriteBu
发表于 2022-04-02
第32章 STM32F429的SPI<font color='red'>总线</font>应用之<font color='red'>驱动</font>W25QXX(支持查询,中断和DMA)
第33章 STM32F429的SPI总线应用之驱动DAC8563(双通道,16bit分辨率,正负10V)
33.1 初学者重要提示学习本章节前,务必优先学习第31章。对于DAC8562和DAC8563,教程中不做区分,因为DAC8562和DAC8563完全兼容,区别仅仅在于CLR引脚有效时,DAC8562数据设置为0, DAC8563数据设置为32767。本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8563的一些细节用法。我们的H7板子配套了SPI + DMA方式控制DAC856X,而F4系列不方便实现,确切的说是可以用DMA方式,但是不方便控制写入速度,需要借助定时器中断进行更新,实用价值不是很大。DAC856X数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件
发表于 2022-04-02
第33章 STM32F429的SPI<font color='red'>总线</font>应用之<font color='red'>驱动</font>DAC8563(双通道,16bit分辨率,正负10V)
第34章 STM32F429的SPI总线应用之驱动DAC8501(双路输出,16bit分辨率,0-5V)
34.1 初学者重要提示学习本章节前,务必优先学习第31章。DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。我们的H7板子配套了SPI + DMA方式控制DAC8501,而F4系列不方便实现,确切的说是可以用DMA方式,但是不方便控制写入速度,需要借助定时器中断进行更新,实用价值不是很大。DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。文件bsp_spi_bus.c文件公共的总线驱动文件,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备的配置。34.2 DAC结构分
发表于 2022-04-02
第34章 STM32F429的SPI<font color='red'>总线</font>应用之<font color='red'>驱动</font>DAC8501(双路输出,16bit分辨率,0-5V)

推荐帖子

美国CDE电容模块在缓冲电路中的应用
美国CDE电容模块在缓冲电路中的应用众所周知,在电力电子器件的应用电路中,无一例外地都要设置缓冲电路,即吸收电路。一些初次应用全控型器件的读者或许有这样的感受:器件莫名其妙地损坏了。虽然损坏的原因颇多,但缓冲电路和缓冲电容选择不当是不可忽略的重要原因。器件损坏,不外乎是器件在开关过程中遭受了过量di/dt、dv/dt或瞬时功耗的危害而造成的。缓冲电路的作用,就是改变器件的开关轨迹,控制各种瞬态过电压,降低器件开关损耗,保护器件安全运行。图1所示为GTR驱动感性负载时的开关波形。不难看出,在
zbz0529 分立器件
有关芯片散热的两个参数
ThermalResistanceJunction-case, ThermalResistanceJunction-ambient, 请教下电源芯片手册中,以上两个参数有什么区别。 谢谢!!~有关芯片散热的两个参数
burton 电源技术
通过串口下载Vxworks Image
我想通过串口下载镜像文件到目标机,config,h文件配置如下: #defineDEFAULT_BOOT_LINE"tsfs(0,0)PC-12032051:/vxWorks 主机上只有一个串口,所以这就把打印信息输出到屏幕了,#defineINCLUDE_PC_CONSOLE #undefCONSOLE_TTY #defineCONSOLE_TTYNONE #undefWDB_TTY_CHANNEL #defineWDB_TTY_CHANN
gyudi 实时操作系统RTOS
[新手求助]matlab音频处理
在用matlab进行音频处理是,通过X(2)=0,让频谱中一个特定的频率强度为0后,sound无法播出任何声音 这是为什么? [x,fs,bits]=wavread(\'c:\\Users\\xpfab\\Desktop\\icerain.wav\'); X=fft(x); n=length(x); X(2)=0 magX=abs(X); xnew=ifft(X); sound(xnew,fs,bits); [新手求助]matlab音频处理
dreambutterfly DSP 与 ARM 处理器
请各位大虾 帮帮忙~~~关于单片机的问题~~谢谢
我现在是一名学生刚接触单片机的编程,遇到了点问题,如下: 我想制作一个跑马灯,有八个灯泡,主芯片是8051,我想让这个跑马灯,跑起来,而且每个灯每秒亮五次,然后换下一个灯···希望大家帮帮忙,编程请用C51程序,谢谢大虾门了,请帮帮我这个菜鸟,我们说清楚的请回帖告诉我我会详细的再说一下的··谢谢谢···很急啊~~请各位大虾帮帮忙~~~关于单片机的问题~~谢谢
美女吧是 嵌入式系统
坛子里谁搞机顶盒研发?
如题,哪位朋友搞机顶盒研发呢? 有个朋友在寻找,我帮忙在这里问问坛子里谁搞机顶盒研发?
soso 移动便携
小广播
设计资源 培训 开发板 精华推荐

何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2023 EEWORLD.com.cn, Inc. All rights reserved