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

发布者:konglingdeyuan最新更新时间: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子系统分析与应用

推荐阅读最新更新时间:2024-11-13 11:34

C51IO口模拟I2C总线驱动AT24C16 (EEPROM部分)
/* 名称:C51IO口模拟I2C总线驱动AT24C16 说明:关于EEPROM,即这里的AT24C16是一个特殊形式的FLASH存储器,不过其容量一般较少。比较适合于存储少量的数据。 AT24C16的通信接口是标准的I2C通信,即我们需要根据I2C通信协议来操纵EEPROM设备。 关于AT24C16的的各种操作,这里就不细讲了,简单介绍一下。 (1)、主机向AT24C16写一个字节:首先需要发送设备地址,然后发送需要访问的存储器地址。然后在发送要写入的数据。这里省略了开始、结束和确认等信号的产生。 (2)、指定页写入n个字节:和(1)的基本操作很类似。不同的是可以连续写入n个数据。这里要小注意一点的就是,写入的数
[单片机]
基于PCI总线的双DSP系统及WDM驱动程序设计
1 硬件设计 1.1 PCI总线控制芯片PCI2040 PCI总线是一种不依附于某个具体处理器的局部总线,它支持32位或64位的总线宽度,频率通常是33MHz,目前最快的PCI2.0总线工作频率是66MHz。工作在33MHz、32位时,理论上最大数据传输速率能达到133MB/s。它支持猝发工作方式,提高了传输速度,支持即插即用,PCI部件和驱动程序可以在各种不同的平台上运行 。 实现PCI总线协议一般有两种方法,一是用FPGA设计实现,但PCI协议比较复杂,因此难度较大;二是采用PCI总线控制芯片,如AMCC公司的S5933、PLX公司的PCI9080等通用的PCI接口芯片。TI公司专门推出了针对PCI总线和DSP接口的芯片
[嵌入式]

推荐帖子

关于SensorTag设计实现阶段延期的正式说明,请参与者都注意了啊!!
有的网友已经开始发心得,同时每天群里也在如火如荼地对SensorTag进行着讨论交流,看见这些,我们真的非常高兴。春节即将来临,提前祝大家节日快乐,同时,由于过年,休假、旅行、聚会......大家都有各自的事情要做,所以经过我们商量,SensorTag设计实现阶段延期到2014年3月24号结束,即日起到3月24号的任何时间段,大家都可以提交设计。【点击此处提交设计】至于周计划的提交,因为大家提交第一周计划的起点时间并不相同,所以只要在3月24号之前完成八周计划即可。最后祝大
EEWORLD社区 无线连接
轻量级多级菜单控制框架程序
#轻量级菜单框架(C语言)作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含UI菜单设计;很多开发人员都会有自己的菜单框架模块,防止重复造轮子,网上有很多这种菜单框架的代码,但是大多耦合性太强,无法独立出来适配不同的菜单设计。本文介绍一个降低了耦合性,完全独立的菜单框架,菜单显示风格和显示平台完全由自己根据需求设计,而菜单操作统一由菜单模块处理即可,提高程序的移植性。##介绍主要特点有:-移植性强,无需修改即可使用-采用
大橙子疯 单片机
来聊聊夏普的窄边框骨传导手机AQUOS CRYSTAL
今天的很多科技网站都在报道夏普的一款手机。下午碰巧与好友也聊了一些,欢迎大家说出自己的看法和观点:先从手机本身说起:最初知道这款个性的手机还是在8月末的一个科技新闻上看到的,大概意思是说夏普出了一款用自家效果最好的CGS屏做的超窄边框手机。它运用“水晶玻璃触摸面板(名为CRYSTALDISPLAY的技术)”技术,边缘为斜边切割,通过光学透镜达到了“隐藏”黑边的效果,再加上原本手机的边框就很窄,所以整体上看起来便达到了“近乎无边”。外形沿袭了
eric_wang 移动便携
CC2640 CC1310高低温测试
CC13/26XX是TI全新一代支持Sub1G、2.4G私有协议、BLE、Zigbee、RF4CE和6LowPan的超低功耗多协议SOC处理器。CC2640为BLE低功耗蓝牙芯片,CC1310为支持低于1GHz的无线产品SOC。在datasheet都标注其支持的温度范围为-40至85℃,而在实验室高低温箱做高低温测试,运行CW载波,频偏在该温度范围下似乎都超出了范围。那实际研发的终端产品在-40至85℃还能稳定工作吗?图一下面我们以CC26
Aguilera RF/无线
智能家居有哪些不足?如何解决这些问题呢?
什么是智能家居?智能家居是指利用综合布线技术、通信技术、物联网技术、安防技术、自动控制技术、音视频技术、人工智能等,整合与家居生活相关的设施,构建高效的住宅设施和家庭日程事务管理系统。智能家居的概念起源很早,市场前景也很广阔。然而,在探索智能家居的过程中,行业的乱象也随之产生。智能家居常见的缺点有哪些?1、装了不会用,利用率不高。由于智能家居的操作比较复杂,目前智能家居的市场还不够成熟,企业对系统的设计也不够人性化,所以操作起来比较困难,本来是要享受生活的,但是使用过程过于
石榴姐 RF/无线
Keysight34972A 数据采集器
Keysight(原Agilent)34972A数据采集开关单元包括3插槽主机、内置的6位半数字万用表以及8个不同的开关和控制模块。具有内置的LAN和USB接口。34972A能够支持所134970A插入式模块。Keysight34972A数据采集器
zbdianzikeji 淘e淘
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved