05. Linux platform驅動--按鍵驅動

什麼是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)
流程:

  1. 定義platform_device實例
  2. 根據實際情況定義platform_device裏的resource實例或這dev->platform_data實例
  3. 在模塊入口函數platform_device_register註冊一個平臺設備
  4. 在模塊出口函數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)
流程:

  1. 定義platform_driver實例
  2. 根據實際情況定義實現platform_driver裏的probe成員函數,裏面註冊什麼由你決定
  3. 根據實際情況定義實現platform_driver裏的remove成員函數,裏面註銷什麼由你決定
  4. 在模塊入口函數platform_driver_register註冊一個平臺驅動
  5. 在模塊出口函數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");

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章