什麼是platform(平臺)總線
相對於USB、PCI、I2C、SPI等物理總線來說,platform總線是一種虛擬、抽象出來的總線,實際中並不存在這樣的總線。那爲什麼需要platform總線呢?其實是Linux設備驅動模型爲了保持設備驅動的統一性而虛擬出來的總線。因爲對於usb設備、i2c設備、pci設備、spi設備等等,他們與cpu的通信都是直接掛在相應的總線下面與我們的cpu進行數據交互的,但是在我們的嵌入式系統當中,並不是所有的設備都能夠歸屬於這些常見的總線,在嵌入式系統裏面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設卻不依附與此類總線。所以Linux驅動模型爲了保持完整性,將這些設備掛在一條虛擬的總線上(platform總線),而不至於使得有些設備掛在總線上,另一些設備沒有掛在總線上。
兩個結構體platform_device和platform_driver
對於任何一種Linux設備驅動模型下的總線都由兩個部分組成:描述設備相關的結構體和描述驅動相關的結構體在platform總線下就是platform_device和platform_driver,下面是對兩個結構體的各個元素進行分析:
platform_device結構體:(include\linux\platform_device.h)
struct platform_device {
const char * name; /* 平臺設備名字 */
int id; /* 一般設置爲-1,表示系統自動分配 */
bool id_auto;
struct device dev; /* 每一個設備都有一個 device 結構體 */
u32 num_resources; /* 資源數量 */
struct resource* resource; /* 資源 */
/* platform_device_id,裏面有個名字列表 */
const struct platform_device_id *id_entry;
…
};
其中有個重要的結構體成員,那就是資源(resource)
struct resource {
resource_size_t start; /* 資源的起始值 */
resource_size_t end; /* 資源的結束值 */
const char *name;
/* 資源的類型,如:
* IORESOURCE_IO(IO 資源)、 IORESOURCE_MEM(內存資源)、
* IORESOURCE_IRQ(中斷資源)、 IORESOURCE_DMA(DMA 資源)
*/
unsigned long flags;
struct resource *parent, *sibling, *child;
};
platform_driver結構體:(include\linux\platform_device.h)
struct platform_driver {
int (*probe)(struct platform_device *); // 這個probe函數其實和 device_driver中的是一樣的功能,但是一般是使用device_driver中的那個
int (*remove)(struct platform_device *); // 卸載平臺設備驅動的時候會調用這個函數,但是device_driver下面也有,具體調用的是誰這個就得分析了
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; // 內置的device_driver 結構體
const struct platform_device_id *id_table; // 該設備驅動支持的設備的列表 他是通過這個指針去指向 platform_device_id 類型的數組
};
平臺總線如何匹配設備和驅動
int platform_driver_register(struct platform_driver *); // 用來註冊我們的設備驅動
void platform_driver_unregister(struct platform_driver *); // 用來卸載我們的設備驅動
int platform_device_register(struct platform_device *); // 用來註冊我們的設備
void platform_device_unregister(struct platform_device *); // 用來卸載我們的設備
平臺總線驅動設備驅動實例
平臺設備驅動實例(platform_device)
流程:
- 定義platform_device實例
- 根據實際情況定義platform_device裏的resource實例或這dev->platform_data實例
- 在模塊入口函數platform_device_register註冊一個平臺設備
- 在模塊出口函數platform_device_unregister註銷一個平臺設備
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#define GPH2CON 0xE0200C40
static struct resource key_resource[] = {
[0] = {
.start = GPH2CON,
.end = GPH2CON + 8,
.flags = IORESOURCE_MEM
},
[1] = {
.start = IRQ_EINT(16),
.end = IRQ_EINT(17),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device key_dev = {
.name = "my_key",
.id = 0,
.num_resources = ARRAY_SIZE(key_resource),
.resource = key_resource,
};
static int __init key_dev_init()
{
platform_device_register(&key_dev);
printk("platform device register success!\n");
return 0;
}
static void __exit key_dev_exit()
{
platform_device_unregister(&key_dev);
}
module_init(key_dev_init);
module_exit(key_dev_exit);
MODULE_LICENSE("GPL");
平臺驅動實例(platform_driver)
流程:
- 定義platform_driver實例
- 根據實際情況定義實現platform_driver裏的probe成員函數,裏面註冊什麼由你決定
- 根據實際情況定義實現platform_driver裏的remove成員函數,裏面註銷什麼由你決定
- 在模塊入口函數platform_driver_register註冊一個平臺驅動
- 在模塊出口函數platform_driver_unregister註銷一個平臺驅動
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#define GPH2CON 0xE0200C40
#define GPH2DAT 0xE0200C44
volatile int ev_press = 0;
unsigned int key_num = 0;
struct work_struct *work1 = NULL;
struct timer_list key_timer;
unsigned int * key_base = NULL;
DECLARE_WAIT_QUEUE_HEAD(key_queue);
struct resource *res = NULL;
struct resource *res_irq = NULL;
static int key_open (struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t key_read (struct file * file, char __user * buf, size_t size, loff_t * ppos)
{
wait_event(key_queue, ev_press);
copy_to_user(buf, &key_num, 4);
key_num = 0;
ev_press = 0;
return 0;
}
static const struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};
static struct miscdevice key_misc =
{
.minor = 200,
.name = "210key",
.fops = &key_fops,
};
void key_hw_init()
{
unsigned short data = 0;
data = readl(key_base);
data |= 0xf;
writel(data, key_base);
}
static irqreturn_t key_irq(int irq, void *dev_id)
{
/* 檢查是否產生中斷(共享中斷才用)*/
/* 清除中斷標誌*/
/* 中斷下半部: 將產生中斷的中斷號傳遞到工作函數,提交工作(到內核默認的工作隊列)*/
schedule_work(work1);
return IRQ_HANDLED;
}
void work1_fun()
{
mod_timer(&key_timer, jiffies + HZ/5);
}
static void key_timer_function (unsigned long data)
{
unsigned int pinval = 0;
ev_press = 1;
pinval = readl(key_base + 1) & 0x01;
if(0 == pinval)
{
key_num = 1;
}
pinval = readl(key_base + 1) & 0x02;
if(0 == pinval)
{
key_num = 2;
}
wake_up(&key_queue);
}
static int key_probe(struct platform_device *pdev)
{
int ret = 0;
int size = 0;
/* 註冊混雜設備驅動*/
ret = misc_register(&key_misc);
printk("register success!\n");
if(ret != 0)
printk("register fail!\n");
/* 硬件初始化*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
key_base = ioremap(res->start, size);
key_hw_init();
/* 註冊中斷*/
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
request_irq(res_irq->start, key_irq, IRQF_TRIGGER_FALLING, "S1", (void *)0);
request_irq(res_irq->end, key_irq, IRQF_TRIGGER_FALLING, "S2", (void *)1);
/* 初始化工作*/
work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work1, work1_fun);
/* 初始化定時器*/
init_timer(&key_timer);
key_timer.function = key_timer_function;
/* 註冊定時器*/
add_timer(&key_timer);
return 0;
}
static int key_remove(struct platform_device *pdev)
{
kfree(work1);
/* 註銷中斷*/
free_irq(IRQ_EINT(16), (void *)0);
free_irq(IRQ_EINT(17), (void *)1);
/* 註銷混雜設備*/
misc_deregister(&key_misc);
return 0;
}
static struct platform_driver key_driver = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.name = "my_key",
},
};
static int __init key_drv_init()
{
return platform_driver_register(&key_driver);
}
static void __exit key_drv_exit()
{
platform_driver_unregister(&key_driver);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");