平臺總線程序設計html
1、平臺總線概述node
平臺總線(Platform bus)是linux2.6內核加入的一種虛擬總線,其優點在於採用了總線的模型對設備與驅動進行了管理,這總線的模型對設備與驅動進行了管理,這樣提升了程序的可移植性。linux
經過平臺總線機制開發設備驅動的流程如圖:函數
平臺總線的結構:platform_bus_type:學習
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_attrs = platform_dev_attrs, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .pm = &platform_dev_pm_ops, 7 };
該結構中,最重要的是咱們的匹配函數platform_match:spa
1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev = to_platform_device(dev); 4 struct platform_driver *pdrv = to_platform_driver(drv); 5 6 /* Attempt an OF style match first */ 7 if (of_driver_match_device(dev, drv)) 8 return 1; 9 10 /* Then try to match against the id table */ 11 if (pdrv->id_table) 12 return platform_match_id(pdrv->id_table, pdev) != NULL; 13 14 /* fall-back to driver name match */ 15 return (strcmp(pdev->name, drv->name) == 0); 16 }
在匹配函數裏,有咱們熟悉的代碼:總線設備驅動程序學習中使用過的線程
2、平臺設備設計
平臺設備使用struct platform_device來描述:code
1 struct platform_device{ 2 const char *name; /*設備名*/ 3 int id; /*設備編號,配合設備名使用*/ 4 struct device dev; 5 u32 num_resources; 6 struct resource *resource; /*設備資源*/ 7 };
在這個結構中,重要的成員name,設備要和驅動的名字同樣。另外一個是resource。設備的資源,例如中斷號,寄存器.....都是資源。這些就放到resource這個結構裏: orm
其中struct resource *resource描述:
1 struct resource{ 2 resource_size_t start; 3 resource_size_t end; 4 const char *name; 5 unsigned long flags; /*資源的類型*/ 6 struct resource *parent, *sibling, *child; 7 };
1.1註冊和註銷平臺設備
註冊平臺設備,使用函數:
int platform_device_register(struct platform_device *pdev)
註銷平臺設備,使用函數:
int platform_device_unregister(struct platform_device *pdev)
使用實例:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/interrupt.h> 5 #include <linux/of_platform.h> 6 7 MODULE_LICENSE("GPL"); 8 9 #define GPNCON 0x7f008830 10 11 //定義資源 12 struct resource key_resource[] ={ 13 [0] = { //第一項資源 14 .start = GPNCON, 15 .end = GPNCON+8, 16 .flags = IORESOURCE_MEM, 17 }, 18 [1] = { //第二項資源,中斷號 19 .start = IRQ_EINT(0), 20 .end = IRQ_EINT(1), 21 .flags = IORESOURCE_IRQ, 22 }, 23 }; 24 25 //定義平臺設備結構 26 struct platform_device key_device = { 27 .name = "my-key", 28 .id = 0, 29 .num_resources = 2, 30 .resource = key_resource, 31 }; 32 33 int keydev_init(void) 34 { 35 int ret; 36 //平臺設備的註冊 37 ret = platform_device_register(&key_device); 38 return ret; 39 } 40 41 void keydev_exit(void) 42 { 43 platform_device_unregister(&key_device); 44 } 45 46 module_init(keydev_init); 47 module_exit(keydev_exit);
執行結果以下:
3、平臺驅動
平臺驅動使用struct platform_driver 描述:
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 void (*shutdown)(struct platform_device *); 5 int (*suspend)(struct platform_device *, pm_message_t state); 6 int (*resume)(struct platform_device *); 7 struct device_driver driver; 8 const struct platform_device_id *id_table; 9 };
其中probe函數是當驅動找到對應的設備後要執行的函數,remove函數是當設備移除是要調用的函數。
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/miscdevice.h> 4 #include <linux/interrupt.h> 5 #include <linux/io.h> 6 #include <linux/fs.h> 7 #include <linux/slab.h> 8 #include <linux/uaccess.h> 9 #include <linux/platform_device.h> 10 #include <linux/sched.h> 11 12 MODULE_LICENSE("GPL"); 13 14 struct work_struct *work1; 15 struct timer_list key_timer; 16 17 unsigned int *key_base; 18 struct resource *res_irq; 19 struct resource *res_mem; 20 21 unsigned int key_num = 0; 22 wait_queue_head_t key_q; 23 24 void work1_func(struct work_struct *work) 25 { 26 mod_timer(&key_timer,jiffies + (HZ/10)); //啓動定時器 27 //100毫秒超時=HZ/10,HZ=1秒。jiffies是系統當前時間 28 } 29 30 void key_timer_func(unsigned long data) //定時器超時函數 31 { 32 unsigned int key_val; 33 //超時的時候,就要讀取data 34 key_val = readw(key_base+1) &0x01; 35 if(key_val==0) 36 key_num = 1; 37 38 key_val = readw(key_base+1) &0x02; 39 if(key_val==0) 40 key_num = 2; 41 42 //當有數據的時候,須要喚醒 43 wake_up(&key_q); 44 } 45 46 irqreturn_t key_int(int irp,void *dev_id) 47 { 48 //1.檢測是否發生了按鍵中斷 49 //2.清除已經發生的按鍵中斷 50 //前面的都是硬件相關的工做,必須在中斷裏面執行 51 //下面是硬件無關的工做,咱們把它放到到中斷之外的work1_func函數去處理。 52 //3.提交下半部 53 schedule_work(work1); 54 return IRQ_HANDLED; 55 } 56 57 void key_hw_init(void) //硬件初始化 58 { 59 unsigned short data; 60 61 data = readw(key_base); 62 data &= ~0xfff; 63 data |= 0xaaa; 64 writew(data,key_base); 65 } 66 67 int key_open(struct inode *node,struct file *filp) 68 { 69 return 0; 70 } 71 72 ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos) 73 { 74 wait_event(key_q,key_num); //進入睡眠 75 printk(KERN_EMERG"key num is%d\n",key_num); 76 copy_to_user(buf,&key_num,4); //返回內核的給用戶 77 key_num = 0; 78 return 4; 79 } 80 81 struct file_operations key_fops = 82 { 83 .open = key_open, 84 .read = key_read, 85 }; 86 87 struct miscdevice key_miscdev = 88 { 89 .minor = 200, //次設備號 90 .name = "key", 91 .fops = &key_fops, 92 }; 93 94 static int key_probe(struct platform_device *pdev) 95 { 96 int ret; 97 int size; 98 ret = misc_register(&key_miscdev); //1註冊混雜設備 99 if(ret!=0) 100 printk(KERN_EMERG"register fail!\n"); 101 102 //獲取資源信息 103 res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0); 104 105 request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //註冊中斷處理程序 106 request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //註冊中斷處理程序 107 108 res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0); 109 size = res_mem->end - res_mem->start+1; 110 key_base = ioremap(res_mem->start,size); 111 112 //按鍵硬件初始化 113 key_hw_init(); /*這個函數裏面會用到上面獲取到的資源信息因此必須放在獲取資源以後執行*/ 114 115 //建立工做1 116 work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); 117 INIT_WORK(work1,work1_func); 118 119 //初始化內核定時器 120 init_timer(&key_timer); 121 key_timer.function = key_timer_func; 122 123 //註冊定時器 124 add_timer(&key_timer); 125 126 //初始化等待隊列 127 init_waitqueue_head(&key_q); 128 return ret; 129 } 130 131 static int key_remove(struct platform_device *pdev) 132 { 133 return misc_deregister(&key_miscdev); //註銷混雜設備 134 } 135 136 struct platform_driver key_driver ={ 137 .probe = key_probe, 138 .remove = key_remove, 139 .driver = { 140 .name = "my-key", 141 }, 142 }; 143 144 //驅動程序初始化 145 static int button_init(void) 146 { 147 return platform_driver_register(&key_driver);; 148 } 149 150 static void button_exit(void) 151 { 152 platform_driver_unregister(&key_driver); 153 } 154 155 module_init(button_init); 156 module_exit(button_exit);
運行結果以下:
這裏面遇到最糾結的問題就是:剛開始的時候將硬件初始化放在了平臺總線的前面結果只要想運行設備驅動成和設備程序就會出現段錯誤,之因此這樣是由於沒有考慮硬件初始化的時候會用到獲取資源信息裏面的內容。一下是正確的初始化過程
1 //獲取資源信息 2 res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0); 3 4 request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //註冊中斷處理程序 5 request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //註冊中斷處理程序 6 7 res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0); 8 size = res_mem->end - res_mem->start+1; 9 key_base = ioremap(res_mem->start,size); 10 11 //按鍵硬件初始化 12 key_hw_init(); /*這個函數裏面會用到上面獲取到的資源信息因此必須放在獲取資源以後執行*/ 13