Linux裝置驅動的分層設計思想

來源:互聯網
上載者:User
原文連結:http://21cnbao.blog.51cto.com/109393/336263 1.1 裝置驅動核心層和例化在物件導向的程式設計中,可以為某一類相似的事物定義一個基類,而具體的事物可以繼承這個基類中的函數。如果對於繼承的這個事物而言,其某函數的實現與基類一致,那它就可以直接繼承基類的函數;相反,它可以重載之。這種物件導向的設計思想極大地提高了代碼的可重用能力,是對現實世界事物間關係的一種良好呈現。Linux核心完全由C語言和組合語言寫成,但是卻頻繁用到了物件導向的設計思想。在裝置驅動方面,往往為同類的裝置設計了一個架構,而架構中的核心層則實現了該裝置通用的一些功能。同樣的,如果具體的裝置不想使用核心層的函數,它可以重載之。舉個例子:return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2){if (bottom_dev->funca)return bottom_dev->funca(param1, param2);/* 核心層通用的funca代碼 */...}上述core_funca的實現中,會檢查底層裝置是否重載了funca(),如果重載了,就調用底層的代碼,否則,直接使用通用層的。這樣做的好處是,核心層的代碼可以處理絕大多數該類裝置的funca()對應的功能,只有少數特殊裝置需要重新實現funca()。再看一個例子:return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2){/*通用的步驟代碼A */...bottom_dev->funca_ops1();/*通用的步驟代碼B */...bottom_dev->funca_ops2();/*通用的步驟代碼C */...bottom_dev->funca_ops3();}上述代碼假定為了實現funca(),對於同類裝置而言,操作流程一致,都要經過“通用代碼A、底層ops1、通用代碼B、底層ops2、通用代碼 C、底層ops3”這幾步,分層設計明顯帶來的好處是,對於通用代碼A、B、C,具體的底層驅動不需要再實現,而僅僅只關心其底層的操作ops1、 ops2、ops3。圖1明確反映了裝置驅動的核心層與具體裝置驅動的關係,實際上,這種分層可能只有2層(圖1的a),也可能是多層的(圖1的b)。{
window.open("http://blog.51cto.com/viewpic.php?refimg=" + this.src)
}' src="http://www.linuxdriver.cn/blog_images/Linux_532/clip_image002_thumb.jpg" border="0" alt="clip_image002" width="625" height="450"> 圖1 Linux裝置驅動的分層這樣的分層化設計在Linux的input、RTC、MTD、I2 C、SPI、TTY、USB等諸多裝置驅動類型中屢見不鮮。下面的2節以input和RTC為例先行進行一番說明,當然,後續的章節會對幾個大的裝置類型對應驅動的層次進行更詳細的分析。1.2 輸入裝置驅動輸入裝置(如按鍵、鍵盤、觸控螢幕、滑鼠等)是典型的字元裝置,其一般的工作機理是底層在按鍵、觸摸等動作發送時產生一個中斷(或驅動通過timer 定時查詢),然後CPU通過SPI、I2 C或外部儲存空間匯流排讀取索引值、座標等資料,放入1個緩衝區,字元裝置驅動管理該緩衝區,而驅動的read()介面讓使用者可以讀取索引值、座標等資料。顯然,在這些工作中,只是中斷、讀值是裝置相關的,而輸入事件的緩衝區管理以及字元裝置驅動的file_operations介面則對輸入裝置是通用的。基於此,核心設計了輸入子系統,由核心層處理公用的工作。Linux核心輸入子系統的架構2所示。{
window.open("http://blog.51cto.com/viewpic.php?refimg=" + this.src)
}' src="http://www.linuxdriver.cn/blog_images/Linux_532/clip_image004_thumb.jpg" border="0" alt="clip_image004" width="650" height="747"> 圖2 Linux輸入裝置驅動的分層輸入核心提供了底層輸入裝置驅動程式所需的API,如分配/釋放一個輸入裝置:struct input_dev *input_allocate_device(void);void input_free_device(struct input_dev *dev);input_allocate_device()返回的是1個input_dev的結構體,此結構體用於表徵1個輸入裝置。註冊/登出輸入裝置用的如下介面:int __must_check input_register_device(struct input_dev *);void input_unregister_device(struct input_dev *);報告輸入事件用的如下介面:/* 報告指定type、code的輸入事件 */void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); /* 報告索引值 */void input_report_key(struct input_dev *dev, unsigned int code, int value);/* 報告相對座標 */void input_report_rel(struct input_dev *dev, unsigned int code, int value);/* 報告絕對座標 */void input_report_abs(struct input_dev *dev, unsigned int code, int value);/* 報告同步事件 */void input_sync(struct input_dev *dev);而所有的輸入事件,核心都用統一的資料結構來描述,這個資料結構是input_event,形如代碼清單7。代碼清單7 input_event結構體1 struct input_event {2 struct timeval time;3 __u16 type;4 __u16 code;5 __s32 value;6 };drivers/input/keyboard/gpio_keys.c基於input架構實現了一個通用的GPIO按鍵驅動。該驅動基於 platform_driver架構,名為“gpio-keys”。它將硬體相關的資訊(如使用的GPIO號,電平等)屏蔽在板檔案 platform_device的platform_data中,因此該驅動可應用於各個處理器,具有良好的跨平台性。代碼清單8列出了該驅動的 probe()函數。代碼清單8 GPIO按鍵驅動的probe()函數1 static int __devinit gpio_keys_probe(struct platform_device *pdev)2 {3 struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;4 struct gpio_keys_drvdata *ddata;5 struct input_dev *input;6 int i, error;7 int wakeup = 0;89 ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +10 pdata->nbuttons * sizeof(struct gpio_button_data),11 GFP_KERNEL); 12 input = input_allocate_device(); 13 if (!ddata || !input) {14 error = -ENOMEM;15 goto fail1;16 }17 18 platform_set_drvdata(pdev, ddata);19 20 input->name = pdev->name;21 input->phys = "gpio-keys/input0";22 input->dev.parent = &pdev->dev;23 24 input->id.bustype = BUS_HOST;25 input->id.vendor = 0x0001;26 input->id.product = 0x0001;27 input->id.version = 0x0100;28 29 ddata->input = input;30 31 for (i = 0; i < pdata->nbuttons; i++) {32 struct gpio_keys_button *button = &pdata->buttons[i];33 struct gpio_button_data *bdata = &ddata->data[i];34 int irq;35 unsigned int type = button->type ?: EV_KEY;36 37 bdata->input = input;38 bdata->button = button;39 setup_timer(&bdata->timer,40 gpio_check_button, (unsigned long)bdata);41 42 ...43 error = request_irq(irq, gpio_keys_isr,44 IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |45 IRQF_TRIGGER_FALLING,46 button->desc ? button->desc : "gpio_keys",47 bdata);48 if (error) {49 ...50 }51 52 if (button->wakeup)53 wakeup = 1;54 55 input_set_capability(input, type, button->code); 56 }57 58 error = input_register_device(input); 59 if (error) {60 pr_err("gpio-keys: Unable to register input device, "61 "error: %d/n", error);62 goto fail2;63 }64 65 device_init_wakeup(&pdev->dev, wakeup);66 67 return 0;68 ...69 }上述代碼的第12行分配了1個輸入裝置,第20~27行初始化了該input_dev的一些屬性,第58行註冊了這個輸入裝置。第31~56行則申請了此GPIO按鍵裝置需要的中斷號,並初始化了timer。第55行設定此輸入裝置可告知的事情。在註冊輸入裝置後,底層輸入裝置驅動的核心工作只剩下在按鍵、觸摸等人為動作發生的時候,報告事件。代碼清單9列出了GPIO按鍵中斷髮生時的附隨報告代碼。代碼清單9 GPIO按鍵中斷髮生時的附隨報告1 static void gpio_keys_report_event(struct gpio_button_data *bdata)2 {3 struct gpio_keys_button *button = bdata->button;4 struct input_dev *input = bdata->input;5 unsigned int type = button->type ?: EV_KEY;6 int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;7 8 input_event(input, type, button->code, !!state); 9 input_sync(input); 10 }11 12 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)13 {14 struct gpio_button_data *bdata = dev_id;15 struct gpio_keys_button *button = bdata->button;16 17 BUG_ON(irq != gpio_to_irq(button->gpio));18 19 if (button->debounce_interval)20 mod_timer(&bdata->timer,21 jiffies + msecs_to_jiffies(button->debounce_interval));22 else23 gpio_keys_report_event(bdata);24 25 return IRQ_HANDLED;26 } 第8行是報告索引值,而第9行是1個同步事件,暗示前面報告的訊息屬於1個訊息組。譬如使用者在報告完X座標後,又報告Y座標,之後報告1個同步事件,應用程式即可知道前面報告的X、Y這2個事件屬於1組,它會將2者聯合起來形成1個(X,Y)的座標。代碼清單8第2行擷取platform_data,而platform_data實際上是定義GPIO按鍵硬體資訊的數組,第31行的for迴圈工具這些資訊申請GPIO並初始化中斷,對於LDD6140電路板而言,這些資訊如代碼清單10。代碼清單10 LDD6410開發板GPIO按鍵的platform_data1 static struct gpio_keys_button ldd6410_buttons[] = {2 {3 .gpio = S3C64XX_GPN(0),4 .code = KEY_DOWN,5 .desc = "Down",6 .active_low = 1,7 },8 {9 .gpio = S3C64XX_GPN(1),10 .code = KEY_ENTER,11 .desc = "Enter",12 .active_low = 1,13 .wakeup = 1,14 },15 {16 .gpio = S3C64XX_GPN(2),17 .code = KEY_HOME,18 .desc = "Home",19 .active_low = 1,20 },21 {22 .gpio = S3C64XX_GPN(3),23 .code = KEY_POWER,24 .desc = "Power",25 .active_low = 1,26 .wakeup = 1,27 },28 {29 .gpio = S3C64XX_GPN(4),30 .code = KEY_TAB,31 .desc = "Tab",32 .active_low = 1,33 },34 {35 .gpio = S3C64XX_GPN(5),36 .code = KEY_MENU,37 .desc = "Menu",38 .active_low = 1,39 },40 };41 42 static struct gpio_keys_platform_data ldd6410_button_data = {43 .buttons = ldd6410_buttons,44 .nbuttons = ARRAY_SIZE(ldd6410_buttons),45 };46 47 static struct platform_device ldd6410_device_button = {48 .name = "gpio-keys",49 .id = -1,50 .dev = {51 .platform_data = &ldd6410_button_data,52 }53 };1.3 RTC裝置驅動RTC(即時鐘)藉助電池供電,在系統掉電的情況下依然可以行走。它通常還具有產生周期中斷以及產生鬧鐘(alarm)中斷的能力,是一種典型的字元裝置。作為一種字元裝置驅動,RTC需要有file_operations中介面函數的實現,如open()、release()、read()、 poll()、ioctl()等,而典型的IOCTL包括RTC_SET_TIME、RTC_ALM_READ、RTC_ALM_SET、 RTC_IRQP_SET、RTC_IRQP_READ等,這些對於所有的RTC是通用的,只有底層的具體實現是裝置相關的。因此,drivers/rtc/rtc-dev.c實現了RTC驅動通用的字元裝置驅動層,它實現了file_opearations的成員函數以及一些關於RTC的通用的控制碼,並向底層匯出rtc_device_register()、rtc_device_unregister()用於註冊和登出RTC;匯出rtc_class_ops結構體用於描述底層的RTC硬體操作。這一RTC通用層實現的結果是,底層的RTC驅動不再需要關心RTC 作為字元裝置驅動的具體實現,也無需關心一些通用的RTC控制邏輯,圖3表明了這種關係。{
window.open("http://blog.51cto.com/viewpic.php?refimg=" + this.src)
}' src="http://www.linuxdriver.cn/blog_images/Linux_532/clip_image006_thumb.jpg" border="0" alt="clip_image006" width="244" height="127"> 圖3 Linux RTC裝置驅動的分層drivers/rtc/rtc-s3c.c實現了S3C6410的RTC驅動,其註冊RTC以及綁定的rtc_class_ops的代碼如代碼清單11。代碼清單11 S3C6410 RTC驅動的rtc_class_ops執行個體與RTC註冊 1 static const struct rtc_class_ops s3c_rtcops = { 2 .open = s3c_rtc_open,3 .release = s3c_rtc_release,4 .ioctl = s3c_rtc_ioctl,5 .read_time = s3c_rtc_gettime,6 .set_time = s3c_rtc_settime,7 .read_alarm = s3c_rtc_getalarm,8 .set_alarm = s3c_rtc_setalarm,9 .irq_set_freq = s3c_rtc_setfreq,10 .irq_set_state = s3c_rtc_setpie,11 .proc = s3c_rtc_proc,12 };13 14 static int s3c_rtc_probe(struct platform_device *pdev)15 {16 ... 17 rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, 18 THIS_MODULE);19 ...20 }
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.