Linux驱动之平台设备
<平臺(tái)設(shè)備設(shè)備驅(qū)動(dòng)>
a:背景:
平臺(tái)總線是Linux2.6的設(shè)備驅(qū)動(dòng)模型中,關(guān)心總線,設(shè)備和驅(qū)動(dòng)這3個(gè)實(shí)體。一個(gè)現(xiàn)實(shí)的Linux設(shè)備和驅(qū)動(dòng)通常需要掛接在一種總線上(比如本身依附于PCI,USB,IIC,SPI等設(shè)備而言)。但是在嵌入式系統(tǒng)里面,SoC系統(tǒng)即集成的獨(dú)立外設(shè)控制器,掛接在SoC內(nèi)存空間的外設(shè)卻沒(méi)有這樣的總線依附,為了和Linux設(shè)備驅(qū)動(dòng)模型理論相互統(tǒng)一,Linux系統(tǒng)開(kāi)發(fā)了Platform_bus這種虛擬總線,相應(yīng)的設(shè)備叫做platform_device ,相應(yīng)的驅(qū)動(dòng)叫platform_driver。引入的一種虛擬總線,其優(yōu)勢(shì)是采用了總總線的模型對(duì)設(shè)備和驅(qū)動(dòng)進(jìn)行管理,同時(shí)提高程序的可移植性。
b:優(yōu)勢(shì):
Linux platform_driver機(jī)制和傳統(tǒng)的device_driver 機(jī)制(通過(guò)driver_register函數(shù)進(jìn)行注冊(cè))相比,一個(gè)十分明顯的優(yōu)勢(shì)在于platform機(jī)制將設(shè)備本身的資源注冊(cè)進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動(dòng)程序中使用這些資源時(shí)通過(guò)platform device提供的標(biāo)準(zhǔn)接口進(jìn)行申請(qǐng)并使用。這樣提高了驅(qū)動(dòng)和資源管理的獨(dú)立性,并且擁有較好的可移植性和安全性(這些標(biāo)準(zhǔn)接口是安全的)
?
<平臺(tái)設(shè)備驅(qū)動(dòng)開(kāi)發(fā)流程>
定義好了platform_device結(jié)構(gòu)體后就可以調(diào)用函數(shù)platform_add_devices向系統(tǒng)中添加該設(shè)備了,之后可以調(diào)用platform_device_register()進(jìn)行設(shè)備注冊(cè)。要注意的是,這里的platform_device設(shè)備的注冊(cè)過(guò)程必須在相應(yīng)設(shè)備驅(qū)動(dòng)加載之前被調(diào)用,即執(zhí)行platform_driver_register之前,原因是因?yàn)轵?qū)動(dòng)注冊(cè)時(shí)需要匹配內(nèi)核中所以已注冊(cè)的設(shè)備名。
?
<平臺(tái)總線>
a:內(nèi)核數(shù)據(jù)結(jié)構(gòu)
struct bus_type platform_bus_type = { ?
? ? .name ? ? ? = "platform", ?
? ? .dev_attrs ?= platform_dev_attrs, ?
? ? .match ? ? ?= platform_match, ? ? ? //設(shè)備和驅(qū)動(dòng)使用match函數(shù)來(lái)判斷是否匹配 ?
? ? .uevent ? ? = platform_uevent, ?
? ? .pm ? ? = PLATFORM_PM_OPS_PTR, ?
?
};
a-1:函數(shù)platform_match()
/* platform_match函數(shù)用于匹配總線中的驅(qū)動(dòng)和設(shè)備 */ ?
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函數(shù)首先判斷是否由id_table,如果有則使用id_table來(lái)進(jìn)行匹配,否則,判斷platform_device和platform_driver成員里的name,如果二者的name字段相同則匹配,如果匹配則調(diào)用platform_driver的probe函數(shù)。
?
<平臺(tái)設(shè)備>
a:內(nèi)核數(shù)據(jù)結(jié)構(gòu)
struct platform_device{
const char *name ;//設(shè)備名
int id;//設(shè)備編號(hào),配合設(shè)備使用
struct device dev;
u32 num_resources;
struct resource ?*resource; //設(shè)備資源
}
a-1:設(shè)備資源
定義硬件資源,比如設(shè)備內(nèi)存,中斷號(hào),DMA通道?
struct resource{
resource_size_t char;
resource_size_t end;
const char *name;
unsigned long flags; //用于表明多個(gè)資源中的某一種資源,比如中斷號(hào),內(nèi)存。
struct resource *parent,*siling ,*child;
};
a-1-1:"unsigned long flags",這里的flags可以取以下值,表示不同的設(shè)備資源
IORESOURCE_IO//IO資源
IORESOURCE_MEN//設(shè)備內(nèi)存資源
IORESOURCE_IRQ//設(shè)備中斷資源
IORESOURCE_DMA//設(shè)備DMA資源
a-1-2:一般驅(qū)動(dòng)中調(diào)用該函數(shù)獲得這些資源
int platform_get_irq(struct platform_device *dev, unsigned int num);
?
b:注冊(cè)平臺(tái)設(shè)備
?
int platform _device _register (struct platform_device *pdev )
?
<平臺(tái)驅(qū)動(dòng)>
a:內(nèi)核數(shù)據(jù)結(jié)構(gòu)
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:函數(shù)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); /* 獲取設(shè)備的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); /* 申請(qǐng)這塊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); /* 映射至內(nèi)核虛擬空間 */
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); /* 獲取設(shè)備IRQ中斷號(hào) */
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);申請(qǐng)IRQ
……
return ret;
}
b:注冊(cè)總線驅(qū)動(dòng)
int platform_driver_register(struct platform_driver*)
?
<平臺(tái)私有數(shù)據(jù)>
a:struct platform_data{?}
設(shè)備除了可以再bsp中定義資源以外,還可以附加一些數(shù)據(jù)信息,因?yàn)閷?duì)設(shè)備的硬件描述除了中斷,內(nèi)存,DMA通道以外,可能還會(huì)有一些配置信息,而這些配置信息也依賴于板,不宜直接放置在設(shè)備驅(qū)動(dòng)本身,因此platform也提供了platform_data的支持,platform_data的形式是自定義的,比如對(duì)于dm9000網(wǎng)卡來(lái)說(shuō),platform_data中可以存放mac地址,總線寬度,板上有誤eeprom等信息。
a-1:如對(duì)于 DM9000 網(wǎng)卡而言, platform_data 為一個(gè) dm9000_plat_data 結(jié)構(gòu)體,我們就可以將 MAC 地址、總線寬度、板上有無(wú) 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, //定義和初始化來(lái)自上面
? ?}
};
?
<憑他設(shè)備驅(qū)動(dòng)實(shí)例>
#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;">
轉(zhuǎn)載于:https://www.cnblogs.com/big-devil/p/8590028.html
總結(jié)
以上是生活随笔為你收集整理的Linux驱动之平台设备的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Element el-upload上传组
- 下一篇: 为 Python Web App 编写