日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux驱动之平台设备

發布時間:2025/6/16 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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驱动之平台设备的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。