Linux裝置驅動工程師之路——platform型別按鍵驅動
Linux裝置驅動工程師之路——platform按鍵驅動
Y-Kee
轉載請註明來自於衡陽師範學院08電2 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498
一 、重要知識點:
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; }
測試結果:
執行測試程式後按下第二個鍵,中斷上列印了多次按鍵的鍵值,產生原因是因為按鍵抖動。導致按一下按鍵,產生多次中斷。
PS:
最近感冒了,很不舒服,很久木有更新啦,身體是革命的本錢,還是得養好身體啊。
相關推薦
Linux裝置驅動工程師之路——platform型別按鍵驅動
Linux裝置驅動工程師之路——platform按鍵驅動 Y-Kee 轉載請註明來自於衡陽師範學院08電2 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498 一 、重要知識點: 1.platform裝置模型
Linux裝置驅動工程師之路——裝置模型(上)底層模型
Linux裝置驅動工程師之路——裝置模型(上)底層模型 K-Style 一、重要知識點 1.Sysfs檔案系統 Sysfs檔案系統是一種類似於proc檔案系統的特殊檔案系統,它存在於記憶體當中,當系統啟動時由核心掛載於記憶體當中。用於將
學習《Linux裝置模型淺析之驅動篇》筆記(一)
原文中說了,核心版本為2.6.29;這裡都貼3.15的核心原始碼; 檔案/drivers/rtc/rtc-s3c.c static struct platform_driver s3c_rtc_driver = { .probe= s3c_rtc_pro
Linux-C成長之路(九)Linux C程式設計實戰之路 複合資料型別
Linux C程式設計實戰之路 複合資料型別 咱們知道,C語言中有許多基本資料型別,比如int型,float型,double型等,我們經常使用這些基本資料型別來表達一些簡單的資料,比如一個人的年齡可以用 int 型資料來表示,一本書的價格可以用 float 型
Linux kernel中斷子系統之(五):驅動申請中斷API
思路 esc 設計師 數組 還需 申請 進一步 time num 一、前言本文主要的議題是作為一個普通的驅動工程師,在撰寫自己負責的驅動的時候,如何向Linux Kernel中的中斷子系統註冊中斷處理函數?為了理解註冊中斷的接口,必須了解一些中斷線程化(threaded i
Web前端開發好學嗎?談談一位學姐的前端工程師之路
努力 不同 最終 log jpg 問題 mage image 前端工程 我的第一篇博客。。。。。。。 我是一名工科女,因高考失利與理想的院校擦肩而過,從而選擇了機電專業。畢業後找工作時才發現機電專業的工作並不太適合我。我的父母也支持我轉專業求職,但這個過程有時會讓我迷茫。在
Linux菜鳥起飛之路【二】Linux基本常識
copy 聯合 das 安裝第三方 所有組 以及 改進 伯克利 版本 一、Unix操作系統基本常識 1.什麽是Unix? Unix是一個計算機操作系統,是一個用來協調、管理和控制計算機硬件與軟件資源的控制程序。 2.Unix操作系統的特點? 多用戶與多任務。多用戶表示在同一
Linux菜鳥起飛之路【七】文件合並、歸檔和壓縮
div 結果 執行 img src gzip 圖片 com .com 一、文件合並操作 1.覆蓋符號與追加符號 a)“>”代表將左邊文件的內容覆蓋右邊文件的內容,如果右邊文件不存在則創建這個文件 b)“>>”代表將左邊文件的內容追加到右邊文件中,如果右邊文
Linux應用開發自學之路
回來 高並發 做了 人員 marvel 匯編 物聯網 原理 書籍 前言 在 「關於我 」那篇博文裏,朋友們應該知道了我不是科班出身,是由機械強行轉行到Linux應用開發方向。下面我就詳細向大家介紹自己這一路上的轉行歷程,希望對大家有所啟發。 我是學機械專業的,對於機械專業我
「 非著名技術人 」良許,Linux應用開發自學之路
小編導讀 良許,是小ken的一個好哥們,線下面基已確認過眼神,是個很靠譜的人。目前在廣州一家世界500強公司就職,是一名Linux應用開發工程師。良許在大學所學專業與IT毫不相干——專業是機械,無意間接觸到嵌入式,於是深深被程式設計工作所吸引,從0開始自學程式設計,一年後被世界500
前端工程師之路
作為一個前端程式設計師,要學的東西很多,要看的技術書籍也不少。各種層出不窮的框架和工具出現,出現百花齊放的景象。 為了保持競爭力,還是要多讀書,多嘗試新的框架和工具,多擼程式碼。練就一雙強壯的麒麟臂,才能在江湖留得一席之地。 列出一些書單,工具集和要掌握的知識。 書單
Java之路:型別轉換
資料型別轉換 1、自動型別轉換 也稱隱式轉換,擴充套件轉換,將範圍小的資料型別賦值給範圍大的資料型別,編譯器會自動進行轉換。 例如: byte b = 20; int a = b; // 將byte型別賦值給int型別,編譯器會自動將byte型別轉化為Int型別 2、強制型
【linux--菜鳥學習之路】fread和fwrite
fread 函式名: fread 功 能: 從一個流中讀資料 用 法: int fread(void *ptr, int size, int nitems, FILE *stream); 參 數:用於接收資料的地址(ptr) 單個元素的大小(siz
初識Linux 驅動移植 之 dm9621網絡卡驅動移植
概述 將kernel移植到開發板並能正常載入和啟動核心後,發現網絡卡並沒有工作,因此將網絡卡作為第一個移植的實踐。這篇文章用於記錄移植dm9621網絡卡過程中遇到的問題以及如何定位問題並嘗試解決。 配置核心 在找到dm9621網絡卡驅動的原始碼後,需要將其新增
演算法工程師之路——Deeplearning.ai神經網路與深度學習篇Week1
寫在前面——一點碎碎念 天將降大任於是人也,必先苦其心志,勞其筋骨,餓其體膚,空乏其身,行拂亂其所為。——《孟子•告子下》 塵埃落定,在好好學(wan)習(shua)三年之後,我成功僥倖收穫了的UESTC MSE的Offer,心裡萬分激動,想著B
演算法工程師之路——Deeplearning.ai神經網路與深度學習篇Week3
上一週的回顧 過去的一週真的發生了很多意想不到、驚心動魄的事情,從大學四年最後一次體測到唐獎競爭,從小IG力挽狂瀾到RNG遺憾折戟,生活可謂是充滿了無數的可能。也正是因為這樣,我們的生活才不至於那麼乏味,像工廠流水線生產一樣標準化、制度化,而隨時都可
演算法工程師之路——Deeplearning.ai神經網路與深度學習篇Week4
上一週的回顧 剛剛過去了10月最後完整的一週,馬上就要撲向11月的懷抱了。很開心能夠在每週總結自己的所學所得,整理為部落格,這樣也是一種讓人充滿了成就感的事情。在上一週我開始學著逐漸運用之前提到的多執行緒學習法進行知識的擴充,收到了不錯的效益,一方面
全棧工程師之路---2018最全web開發路線圖
前後端都需要掌握的技能 Git — You should’ve learned Git yesterday, 15 minutes to learn Git SSH HTTP / HTTPS Terminal Usage — Basic Mac
全棧工程師之路-中級篇之小程式開發-第一章第五節從px到rpx
這一小節作為第一章的最後一節,其實關於單位的文章,在部落格上倒是挺多的。 我一直在想,我是不是有必要再寫這一節,還是給大家提供一些閱讀連結。 因為關於單位的定義都是比較官方的。沒什麼好討論的,我這裡做一個彙總和簡單的說明吧。 首先本文只以移動裝置為例說明。 本文摘要:設計師
工程師之路點滴心得
1、生產環境中出現的bug很難重現,而且隨著程式越來越複雜,也越來越難以進行除錯。因此,為了能夠在出現問題時快速定位到原因和解決問題,一定要詳細列印日誌。 2、儘量看權威的,官方的資料,能夠節省大量時