天天看點

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

1.gpio_key.c介紹

1.1 功能介紹

Linux核心下的 drivers/input/keyboard/gpio_keys.c實作了一個體系結構無關的GPIO按鍵驅動,使用此按鍵驅動,隻需在裝置樹gpio-key節點添加需要的按鍵子節點即可。驅動的實作非常簡單,但是較适合于實作獨立式按鍵驅動。

1.2 架構介紹

gpio-keys是基于input架構實作的一個通用GPIO按鍵驅動。該驅動基于platform_driver架構,實作了驅動和裝置分離,符合Linux裝置驅動模型的思想。本文以自己的了解介紹gpio_keys.c驅動實作原理及代碼技巧。

2.裝置樹

裝置樹節點轉化成device_node.gpio_keys.

c與其他驅動一樣采用platform總線架構,在裝置樹擷取硬體相關屬性。

Show me the code:

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

從上述代碼可看到,gpio_keys.c節點内定義兩個按鍵節點: “key_power”、”key_headset”。每個按鍵節點包括一個gpio所用到的所有硬體屬性。拿第一個裝置節點解析:

key_power {

                label = "Power Key";      //按鍵描述性名稱

                linux,code =<116>;            //鍵值,即中斷觸發上報的鍵值與核心定義的保持一緻。

                gpios = <&pmic_eic 1="">;  //按鍵gpio  &pmic_eic: gpio組由dtsi定義 1: gpio号 1:有效電平

                debounce-interval =<5>;  //去抖間隔 機關ms

                gpio-key,wakeup;        //可喚醒系統  

                gpio-key,level-trigger;     //中斷觸發方式level-trigger: 條件觸發 edge-trigger: 邊緣觸發

        };
           

複制

3.資料結構

優秀的代碼必然離不開優秀的資料結構,gpio_key.c之是以能夠做到完全脫離闆級晶片,實作通用與其資料結構的建立不無關系。以下列舉使用到的資料結構:

device_node //裝置樹節點結構體

device //裝置類型

platform_device //platform總線裝置

platform_data

input_dev

gpio_keys_platform_data

注:其中device_node、device 、platform_device 、三個結構體,是在驅動程式設計中經常被用到較為重要的資料結構。

以我自己的了解來解釋這三種結構體的關系:

device_node : 用于采集一個裝置樹節點資訊的結構體。是以需要具備裝置樹屬性的成員,例如:硬體裝置名(const char *name)、屬性指針(struct property *property)、父節點(struct device_node *parent)、子節點(struct device_node *child )等。

device : 指相同類型裝置的一種集合結構體。包含了這種類型的所有資訊,例如:裝置類型(const struct device_type *type;)、該裝置基于的總線類型(struct bus_type *bus)、具體的裝置樹節點資訊(struct device_node *of_node)等。

platform_device: 基于platform總線的裝置結構體。即挂在虛拟platform總線架構的裝置,其包含的成員:裝置結構體(struct device dev)、虛拟platform裝置名(const char *name)等。

列舉以下這三種最常見的結構體,之間的關系:

device_node = device->of_node

device = platform_device->dev

platform_device = of_find_device_by_node(device_node)
           

複制

由上述關系可知道,隻要有一個參數已知,其餘兩個參數都可以擷取到。列舉出他們建的關系:

platform_device --> device --> device_node --> platform_device

4.驅動實作

4.1擷取裝置樹屬性

按道理,能成功進入probe,就說明裝置樹platform_device和驅動platform_driver比對成功,傳進來的platform_device *pdev就包含裝置樹硬體資訊。

依然是熟悉的配方!裝置節點拿屬性。

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

先從傳來的參數裡拿裝置:

struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

struct device *dev = &pdev->dev;
           

複制

然後判斷有沒有資料, pdata若沒資料,我就繼續拿:

error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);

pdata = &alt_pdata;
           

複制

看一下gpio_keys_get_devtree_pdata裡是怎麼拿的:

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

注:容易進入誤區的一點,驅動裝置樹of _xx API并不都是直接從裝置樹裡直接拿到屬性的,而是先将裝置樹節點屬性值轉化為device_node資料結構體成員值,然後驅動通過of_xx API從device_node結構體中擷取到相應屬性的值。是以device_node的轉化就非常重要了!

通過上述代碼,發現還是從傳參struct device dev裡拿struct device_node node。

(1) 先拿裝置節點: node = dev->of_node;(也可以通過of_find_node_by_path(); 或of_find_compatible_node() 拿裝置節點。)

然後拿父節點屬性值 “input-name”,對應裝置樹

(2) 再通過of_xx API 拿node對應的屬性值。

這裡,源碼通過周遊檢測node下存在的子節點,實作對每一個按鍵屬性初始化。貼出周遊循環宏。

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

(3) 在周遊裡拿每個gpio預設電平。

即裝置樹中gpios = <&pmic_eic 1 1> 第三個參數。此屬性還有另一種寫法例如gpios = <&pmic 1 GPIO_ACTIVE_HIGH>;意義是一樣的。

of_get_gpio_flags(pp, 0, &flags); 傳回gpios項裡的第三個有效電平參數。

(4) 在周遊裡擷取中斷号

(5) 然後通過of_property_read_u32 可在device_node拿到指定屬性名的屬性值。

注:其實在節點屬性值擷取時,根據屬性值的格式都可以選擇of_property_xx函數擷取,代碼裡所謂的特定屬性擷取API大都是對of_property_xx的一層封裝,例如

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

附上驅動周遊擷取裝置樹節點屬性值代碼:

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性

4.2使用input架構

(1) 申請input裝置

input = input_allocate_device();
           

複制

(2) 填充input結構體成員

(3) 設定GPIO按鍵

主要負責申請GPIO管腳,設定狀态,輸出方向,中斷申請等

API: gpio_keys_setup_key(pdev, input, bdata, button);
           

複制

(4) 注冊input裝置

input_register_device(input);
           

複制

4.3上報按鍵事件

按鍵狀态發生變化時,會觸發中斷,在中斷子服務函數中,先通過消抖參數值判斷是否消抖,如果消抖就啟用定時器上報,若無需消抖就就直接上報按鍵事件。

中斷子服務函數:

gpio_key按鍵驅動1.gpio_key.c介紹2.裝置樹3.資料結構4.驅動實作4.1擷取裝置樹屬性