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;">
轉載于:https://www.cnblogs.com/big-devil/p/8590028.html
總結
以上是生活随笔為你收集整理的Linux驱动之平台设备的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Element el-upload上传组
- 下一篇: 为 Python Web App 编写