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-16 20:11

Tiny210按键分层分离(总线-驱动-设备模型)
led_dev.c驱动源码: #include linux/module.h #include linux/version.h #include linux/init.h #include linux/kernel.h #include linux/types.h #include linux/interrupt.h #include linux/list.h #include linux/timer.h #include linux/init.h #include linux/serial_core.h #include linux/platform_device.h // 分配/设置/注册一个platfo
[单片机]
06-S3C2440学习之移植2012u-boot到S3C2440(移植过程三)支持NorFlash
上一节 我们实现了支持nand flash 启动(点击查看),并不代表uboot此时可以对nand进行读写操作,只是可以把uboot烧写到nand然后上电后,对代码 进行重定位,实现了nand启动。这一节我们加入对nor 的支持,使uboot可以识别出nor flash 并可以读写nor flash。 (1)查看上一节最后的报错: (2)搜索Flash:查看代码: 程序会陷入死循环,并打印出错误信息。 (3)找不到不卡死,继续往下走:修改为如下 修改是为了 从nand启动时不在此处卡死。 (4)看一下这个 怎么才能识别出nor flash if (!flash_detect_legacy(cfi_
[单片机]
06-S3C2440学习之移植2012u-boot到S3C2440(移植过程三)支持NorFlash

推荐帖子

开关电源的中部分元件的重要性
最近被一个项目的CE和EFT折腾的疲惫,带火牛的项目也做过几个,都没遇见这样的,怀疑电源本身对干扰的抑制不好,拆开电源后看到电源适配器的AC输入端无X电容,无共模电感,有些甚至输出的型滤波器也没有。想知道这是都是成本的原因吗?另外,据了解此类适配器在出厂测试时电子负载基本都是在恒流模式,并不会测试动态负载下的情况,这样又如何保证在动态负载突变时的情况一致。开关电源上省下来的元件还是要加到后级产品上,这样做法真的科学吗?开关电源的中部分元件的重要性成本呗感觉就是为了控制成本,
classd 开关电源学习小组
[BearPi-Pico H2821]测评 ⑥丢包及连接稳定性测试
上两篇我们查了一下SLEserver和client端的demo,怎么运行的,收发应该调用那些接口已经清楚了,就可以开始做丢包及连接稳定性测试了测试方案server端代码不用动,client端连接server后,创建一个TASK,定时用SLE向server发数据,并计数,发送到最大次数后,停止发送,摧毁task。然后提取双方日志,查看中途是否有断连、收发次数是否有缺少1.client端代码修改1.1发送函数参照之前找到的发送函数sle_ua
不爱胡萝卜的仓鼠 RF/无线
因公司生产需要求购STM32F437VIT6芯片
因公司生产需要求购STM32F437VIT6芯片,数量需要3-5K,拆包的、散的也可以,只要是原装的就可以,年份19+以后的都行,如果有这个芯片库存的公司可与我联系,我们会高于原价数倍的价格采购,具体的价格我们详谈,能帮找到也可,电话、微信同号:13718461954因公司生产需要求购STM32F437VIT6芯片您散新原装接受价和全新原装接受价能到多少?我这有朋友在搞你朋友那边是贸易还是工厂的库存啊?数量需要3-5K,拆包的、散的也可以,只要是原装的就可以,年份19+以
dongfanghanke 淘e淘
MOSFET的开关电压Vgs
如题。请问一下,MOSFET的手册里面哪个参数能看的出来,当其作为开关管,完全打开的时候,Vgs的电压?同事跟我讲,默认12V大多数都可以完全打开(NMOS)。低于12V就有点悬,MOS打开不完全,会导致Rds的电阻过大,MOS管发热。所以他现在设计的电路图,基本上都是12V打开MOS管。举个例子,如下图MOSFET的开关电压VgsMOS管完全打开当然是看Id是否最大值同事说的有一定道理。但是要根据MOS管的手册数据看。以楼主的图中的小功率MOS管2N7002来说
平漂流 模拟电子
三台以上西门子S7-200之间的MODBUS无线通讯程序
本例程是针对两台以及三台以上西门子S7-200之间的的无线MODBUS协议数据的传输,组成一点(主机)对多点(从机)的无线PLC测控网络,其中一台S7-200作为主机,从机根据编号不同可以增加到255个,本方案在大量工程中使用,用户可以参考例程源码编写自己的应用程序。一、主机:S7-2001.一个系统中需要把一个S7-200作为主机,与主机连接的西门子专用无线数据终端型号为:DTD434M无线数据终端。2.主机自带RS485通信接口,可以与DTD433M控制器的RS485接口
dataie123456 RF/无线
用C和WIN32 API写了个五子棋的小游戏,现在想加个联网的功能,用SOCKET,有几个问题大家帮忙
1.SOCKET通信写在哪个消息里?我认为应该在每次按下的时候,SERVER端进行一次发送和一次接受,CLINET端进行一次接收和一次发送2.如何判断两个机器有没有联通啊,如何调试3,希望大家能给个WINAPI的例子,要C的C++不懂呢谢谢了用C和WIN32API写了个五子棋的小游戏,现在想加个联网的功能,用SOCKET,有几个问题大家帮忙1、楼主正解,SOCKET消息应该在SEVER或CLIENT走每一步棋的时候发。2、建立连接时,可以根据connect()与accep
clhlx0785 嵌入式系统
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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