一 、重要知識點:
1.platform設備模型
從Linux 2.6起引入了一套新的驅動管理和註冊機制,platform_device和platform_driver,Linux中大部分的設備驅動都可以使用這套機制。platform是一條虛擬的總線。設備用platform_device表示,驅動用platform_driver進行註冊,Linux platform driver機制和傳統的device driver機制(通過driver_register進行註冊)相比,一個明顯的優勢在於platform機制將設備本身的資源註冊進內核,由內核統一管理,在驅動中使用這些資源時通過platform device提供的標準結構進行申請並使用。這樣提高了驅動和資源的獨立性,並且具有較好的可移植性和安全性(這些標準接口是安全的)。
pltform機制本身使用並不複雜,由兩部分組成:platform_device和platform_driver。通過platform機制開發底層驅動的大致流程爲:定義platform_deive->註冊platform_device->定義platform_driver->註冊platform_driver。
首先要確認的就是設備的資源信息,例如設備的地址,中斷號等。
1)platform_device
在 2.6 內核中 platform 設備用結構體 platform_device 來描述,該結構體定義在 kernel/include/linux/platform_device.h 中,
structplatform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
該結構一個重要的元素是resource ,該元素存入了最爲重要的設備資源信息,定義在kernel/include/linux/ioport.h 中,
structresource {
const char *name;//資源的名稱
unsigned long start, end;//資源起始的和結束的物理地址
unsigned long flags;//資源的類型,比如MEM,IO,IRQ類型
struct resource *parent, *sibling, *child;//資源鏈表的指針
};
structplatform_device的分配使用
structplatform_device *platform_device_alloc(const char *name, int id)
name是設備名,id,設備id,一般爲-1,如果是-1,表示同樣名字的設備只有一個
舉個簡單的例子,name/id是“serial/1”則它的bus_id就是serial.1 如果name/id是“serial/0”則它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”則它的bus_id就是serial。
註冊平臺設備,使用函數
intplatform_device_add(struct platform_device *pdev)
註銷使用
voidplatform_device_unregister(struct platform_device *pdev)
2)platform_driver
在平臺設備驅動中獲取平臺設備資源使用
structresource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)
該函數用於獲取dev設備的第num個類型爲type的資源,如果獲取失敗,則返回NULL。例如 platform_get_resource(pdev,IORESOURCE_IRQ, 0)。
平臺驅動描述使用
structplatform_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 device_driver driver;
};
Probe()函數必須驗證指定設備的硬件是否真的存在,probe()可以使用設備的資源,包括時鐘,platform_data等,Platform driver可以通過下面的函數完成對驅動的註冊:
int platform_driver_register(structplatform_driver *drv);一般來說設備是不能被熱插拔的,所以可以將probe()函數放在init段裏面來節省driver運行時候的內存開銷:
int platform_driver_probe(struct platform_driver *drv, int (*probe)(structplatform_device *));
註銷使用void platform_driver_unregister(struct platform_driver *drv)
2.中斷處理
在Linux驅動程序中,爲設備實現一箇中斷包含 兩個步驟1.向內核註冊(申請中斷)中斷 2.實現中斷處理函數
request_irq用於實現中斷的註冊
intrequest_irq(unsigned in irq, void(*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void*dev_id)
向內核申請中斷號爲irq,中斷處理函數爲handler指針指向的函數,中斷標誌爲flag,設備名爲devname的中斷。成功返回0,或者返回一個錯誤碼。
當request_irq不用於共享中斷時,dev_id可以爲NULL,或者指向驅動程序自己的私有數據。但用於共享中斷時dev_id必須唯一。因爲free_irq時也需要dev_id做參數,這樣free_irq才知道要卸載共享中斷上哪個中斷服務處理函數。共享中斷會在後面講到。
在flag參數中,可以選以下參數
IRQF_DISABLED(SA_INTERRUPT)
如果設置該位,表示是一個“快速”中斷處理程序,如果沒有,那麼就是一個“慢速”中斷處理程序。
IRQF_SHARED(SA_SHITQ)
該位表示中斷可以在設備間共享。
快速/慢速中斷
這兩種類型的中斷處理程序的主要區別在於:快速中斷保證中斷處理的原子性(不被打斷),而慢速中斷則不保證。換句話說,也就是開啓中斷標誌位在運行快速中斷處理程序時
關閉的,因此在服務該中斷時,不會被其他類型的中斷打斷;而調用慢速中斷處理時,其他類型中斷扔可以得到服務。
共享中斷
共享中斷就是將不同的設備掛到同一個中斷信號線上。linux對共享的支持主要是位PCI設備服務。
釋放中斷
voidfree_irq(unsigned int irq)
當設備不再需要使用中斷時(通常是設備關閉和驅動卸載時),應該使用該函數把他們返回給內核使用。
禁用中斷
voiddisable_irq(int irq)
當一些代碼中不能使用中斷時(如支持自旋鎖的上下文中)使用該函數禁用中斷。
啓用中斷
voidenable_irq(int irq)
當禁止後可以使用該函數重新啓用。
二、驅動代碼
該驅動實現能夠讀取按鍵按下的鍵值,比如說如果是第一個鍵按下讀取的鍵值就爲1。
platform平臺設備
- #include <linux/device.h>
- #include <linux/string.h>
- #include <linux/platform_device.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <linux/poll.h>
- #include <linux/irq.h>
- #include <asm/irq.h>
- #include <linux/interrupt.h>
- #include <asm/uaccess.h>
- #include <mach/regs-gpio.h>
- #include <mach/hardware.h>
- #include <linux/cdev.h>
- #include <linux/miscdevice.h>
- #include <linux/sched.h>
- #include <linux/gpio.h>
- static struct resource key_resource[]=
- {
- [0] = {
- .start = IRQ_EINT8,
- .end = IRQ_EINT8,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = IRQ_EINT11,
- .end = IRQ_EINT11,
- .flags = IORESOURCE_IRQ,
- },
- [2]= {
- .start = IRQ_EINT13,
- .end = IRQ_EINT13,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- .start = IRQ_EINT14,
- .end = IRQ_EINT14,
- .flags = IORESOURCE_IRQ,
- },
- [4] = {
- .start = IRQ_EINT15,
- .end = IRQ_EINT15,
- .flags = IORESOURCE_IRQ,
- },
- [5] = {
- .start = IRQ_EINT19,
- .end = IRQ_EINT19,
- .flags = IORESOURCE_IRQ,
- },
- };
- struct platform_device *my_buttons_dev;
- static int __init platform_dev_init(void)
- {
- int ret;
- my_buttons_dev = platform_device_alloc("my_buttons", -1);
- platform_device_add_resources(my_buttons_dev,key_resource,6);//添加資源一定要用該函數,不能使用對platform_device->resource幅值
- //否則會導致platform_device_unregister調用失敗,內核異常。
- ret = platform_device_add(my_buttons_dev);
- if(ret)
- platform_device_put(my_buttons_dev);
- return ret;
- }
- static void __exit platform_dev_exit(void)
- {
- platform_device_unregister(my_buttons_dev);
- }
- module_init(platform_dev_init);
- module_exit(platform_dev_exit);
- MODULE_AUTHOR("Y-Kee");
- MODULE_LICENSE("GPL");
platform平臺驅動
- //platform driver
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/platform_device.h>
- #include <linux/interrupt.h>
- #include <linux/clk.h>
- #include <linux/uaccess.h>
- #include <linux/io.h>
- #include <mach/map.h>
- #include <mach/regs-gpio.h>
- #include <linux/poll.h>
- #include <linux/irq.h>
- #include <asm/unistd.h>
- #include <linux/device.h>
- static int buttons_irq[6];
- struct irq_des
- {
- int *buttons_irq;
- char *name[6];
- };
- struct irq_des button_irqs = {
- .buttons_irq = buttons_irq,
- .name = {"KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5"},
- };
- static volatile int key_values;
- static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
- static volatile int ev_press = 0;
- static irqreturn_t buttons_interrupt(int irq, void *dev_id)
- {
- int i;
- for(i=0; i<6; i++){
- if(irq == buttons_irq[i]){
- key_values = i;
- ev_press = 1;
- wake_up_interruptible(&button_waitq);
- }
- }
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
- {
- int i;
- int err = 0;
- for (i = 0; i < 6; i++) {
- err = request_irq(button_irqs.buttons_irq[i], buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
- button_irqs.name[i], (void *)&button_irqs.buttons_irq[i]);
- if (err)
- break;
- }
- if (err) {
- i--;
- for (; i >= 0; i--) {
- if (button_irqs.buttons_irq[i] < 0) {
- continue;
- }
- disable_irq(button_irqs.buttons_irq[i]);
- free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);
- }
- return -EBUSY;
- }
- return 0;
- }
- static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
- {
- int i;
- for (i = 0; i < 6; i++) {
- free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);
- }
- return 0;
- }
- static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
- {
- unsigned long err;
- if (!ev_press) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- else
- wait_event_interruptible(button_waitq, ev_press);
- }
- ev_press = 0;
- err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count));
- return err ? -EFAULT : min(sizeof(key_values), count);
- }
- static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
- {
- unsigned int mask = 0;
- poll_wait(file, &button_waitq, wait);
- if (ev_press)
- mask |= POLLIN | POLLRDNORM;
- return mask;
- }
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE,
- .open = s3c24xx_buttons_open,
- .release = s3c24xx_buttons_close,
- .read = s3c24xx_buttons_read,
- .poll = s3c24xx_buttons_poll,
- };
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "my_buttons",
- .fops = &dev_fops,
- };
- static int my_plat_probe(struct platform_device *dev)
- {
- int ret,i;
- struct resource *plat_resource;
- struct platform_device *pdev = dev;
- printk("my platform dirver find my platfrom device.\n");
- for(i=0; i<6; i++){
- plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);
- if(plat_resource == NULL)
- return -ENOENT;
- buttons_irq[i] = plat_resource->start;
- }
- ret = misc_register(&misc);
- if(ret)
- return ret;
- return 0;
- }
- static int my_plat_remove(struct platform_device *dev)
- {
- printk("my platfrom device has removed.\n");
- misc_deregister(&misc);
- return 0;
- }
- struct platform_driver my_buttons_drv = {
- .probe = my_plat_probe,
- .remove = my_plat_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "my_buttons",
- },
- };
- static int __init platform_drv_init(void)
- {
- int ret;
- ret = platform_driver_register(&my_buttons_drv);
- return ret;
- }
- static void __exit platform_drv_exit(void)
- {
- platform_driver_unregister(&my_buttons_drv);
- }
- module_init(platform_drv_init);
- module_exit(platform_drv_exit);
- MODULE_AUTHOR("Y-Kee");
- MODULE_LICENSE("GPL");
測試代碼
- /*
- * Buttons Example for Matrix V
- *
- * Copyright (C) 2004 capbily - friendly-arm
- * [email protected]
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <errno.h>
- int main(void)
- {
- int buttons_fd;
- int key_value;
- buttons_fd = open("/dev/buttons", 0);
- if (buttons_fd < 0) {
- perror("open device buttons");
- exit(1);
- }
- for (;;) {
- fd_set rds;
- int ret;
- FD_ZERO(&rds);
- FD_SET(buttons_fd, &rds);
- ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);
- if (ret < 0) {
- perror("select");
- exit(1);
- }
- if (ret == 0) {
- printf("Timeout.\n");
- } else if (FD_ISSET(buttons_fd, &rds)) {
- int ret = read(buttons_fd, &key_value, sizeof key_value);
- if (ret != sizeof key_value) {
- if (errno != EAGAIN)
- perror("read buttons\n");
- continue;
- } else {
- printf("buttons_value: %d\n", key_value+1);
- }
- }
- }
- close(buttons_fd);
- return 0;
- }
測試結果:
運行測試程序後按下第二個鍵,中斷上打印了多次按鍵的鍵值,產生原因是因爲按鍵抖動。導致按一下按鍵,產生多次中斷。