Linux驱动之平台设备
<平台设备设备驱动>
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;">
Linux驱动之平台设备的更多相关文章
- Linux驱动之平台设备驱动模型简析(驱动分离分层概念的建立)
Linux设备模型的目的:为内核建立一个统一的设备模型,从而有一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
- linux驱动之获取设备树信息
上一篇文章学习了字符设备的注册,操作过的小伙伴都知道上一篇文章中测试驱动时是通过手动创建设备节点的,现在开始学习怎么自动挂载设备节点和设备树信息的获取,这篇文章中的源码将会是我以后编写字符驱动的模板. ...
- Linux Platform devices 平台设备驱动
设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...
- 【Linux高级驱动】平台设备驱动机制的编程流程与编译进内核
[平台设备驱动机制的编程流程] [如何将驱动静态的编译进内核镜像] 1.添加资源(dev-led.c) 1.1:一般来说,系统习惯上将资源放在arch/arm/plat-samsung/目录中 cp ...
- Linux驱动设计——字符设备驱动(一)
Linux字符设别驱动结构 cdev结构体 struct cdev { struct kobject kobj; struct module *owner; const struct file_ope ...
- 【Linux驱动】字符设备驱动
一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面 ...
- 9、Linux驱动的杂项设备
杂项设备,是字符设备中的特殊,它的主设备号,是 10,不同的杂项设备,通过次设备号进行区分. 1.注册与注销 int misc_register(struct miscdevice * misc) 完 ...
随机推荐
- WEB前端技巧之JQuery为动态添加的元素绑定事件.md
jquery 为动态添加的元素绑定事件 如果直接写click函数的话,只能把事件绑定在已经存在的元素上,不能绑定在动态添加的元素上 可以用delegate来实现 .delegate( select ...
- uva 10625 Board Wrapping
https://vjudge.net/problem/UVA-10652 给出n个长方形,用一个面积尽量小的凸多边形把他们围起来 求木板占包装面积的百分比 输入给出长方形的中心坐标,长,宽,以及长方形 ...
- ASP.NET MVC学习(一)之路由篇Route
什么是路由 通过[路由]配置,路由可以规定URL的特殊格式,使其达到特殊效果. 在ASP.NET MVC框架中,通过路由配置URL,使用户的URL请求可以映射到Controller下的action方法 ...
- python scrapy cookies 处理
def start_requests(self): cookies = 'anonymid=jcokuqwe................省略' # 首先是对cookies进行分割以;为节点 ook ...
- VMware Linux 下 Nginx 安装配置 (一)
资源准备 1. pcre-8.34.tar.gz: ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ 2. zlib-1.2.8.tar.g ...
- BZOJ4818 序列计数
4818: [Sdoi2017]序列计数 Time Limit: 30 Sec Memory Limit: 128 MB Description Alice想要得到一个长度为n的序列,序列中的数都是 ...
- 【洛谷 P2726】 [SHOI2005]树的双中心(树的重心)
先考虑一个\(O(N^2)\)做法. 设选的两个点为\(x,y\),则一定可以将树分成两个集合\(A,B\),使得\(A\)集合所有点都去\(x\),\(B\)集合所有点都去\(y\),而这两个集合的 ...
- Dream_Spark定制第二课
Spark版本定制第2天:通过案例对SparkStreaming透彻理解之二 本期内容: 1 解密Spark Streaming运行机制 2 解密Spark Streaming架构 一切不能进行实时流 ...
- C#并行计算 Parallel.Foreach&Parallel.For
Parallel.For(int fromInclude, int toExclude, Action<int> body) 栗子: Parallel.For(0, 10, (i) =&g ...
- Linux Core Dump【转】
转自:http://www.cnblogs.com/hazir/p/linxu_core_dump.html 当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中 ...