一 、重要知識點:
從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。
首先要確認的就是設備的資源信息,例如設備的地址,中斷號等。
在 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)
在平臺設備驅動中獲取平臺設備資源使用
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?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
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");??
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
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");??
//platform driver#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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;elsewait_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??
*??capbily@hotmail.com??
*/??
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
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;??
}??
/** Buttons Example for Matrix V** Copyright (C) 2004 capbily - friendly-arm*capbily@hotmail.com*/#include #include #include #include #include #include #include #include #include #include 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;}
運行測試程序后按下第二個鍵,中斷上打印了多次按鍵的鍵值,產生原因是因為按鍵抖動。導致按一下按鍵,產生多次中斷。
?
評論
查看更多