友堅4412開發板基於Timed_out框架的GPIO驅動分析

Timed GPIO驅動程序分析

Timed GPIO驅動程序是android系統基於linux內核新增加的一類驅動程序,這類驅動程序主要是運用了內核定時器,與內核定時器進行綁定,使得控制GPIO口的高低電平與時間打上關係,既可以實現在一定的時間實現GPIO口爲高或者低電平。Timed GPIO驅動被實現爲平臺設備驅動,Timed GPIO驅動源碼位於如下目錄:\kernel\drivers\staging\android

 

Timed GPIO驅動程序主要包括如下幾個文件:

 Timed_gpio.c

 Timed_gpio.h

 Timed_output.c

 Timed_output.h

 

Timed_gpio.c文件爲具體的驅動程序,Timed_output.c爲向sys文件系統註冊類的框架代碼,

下面將具體分析每一個文件的作用及實現的具體功能。

首先分析註冊類的框架代碼 :Timed_output.c   Timed_output.h

 

Timed_output.h文件分析:

struct timed_output_dev {

const char *name;

void  (*enable)(struct timed_output_dev *sdev, int timeout);

int  (*get_time)(struct timed_output_dev *sdev);

struct device *dev;

    int  index;

    int  state;

};

 

extern int timed_output_dev_register(struct timed_output_dev *dev);

extern void timed_output_dev_unregister(struct timed_output_dev *dev);

Timed_output.h文件主要定義了一個結構體timed_output_dev設備結構體,該結構體表示一個具體的設備,

Name:代表Time GPIO設備的名字,enable:爲一個函數指針,主要用於設置定時器的過期時間,

Enable:用於獲取離過期還剩餘的時間。Index:爲設備索引號,代表同一名字的設備的數量,state帶表當前設備的狀態值。

timed_output_dev_registertimed_output_dev_unregister這兩個函數聲明用於timed_output設備的註冊和卸載。

 

Timed_output.c文件分析:

該文件主要用於向系統註冊timed_output驅動程序框架,其中主要實現了Timed_output.h文件中定義的結構體及函數。

 

timed_output框架註冊函數的實現,這個函數用於將驅動程序註冊到kernel中,後面分析Timed GPIO驅動的具體實現時會調用此函數向系統註冊Timed GPIO驅動。

int timed_output_dev_register(struct timed_output_dev *tdev)

{

int ret;

if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)

return -EINVAL;

ret = create_timed_output_class(); 調用此函數在sys/class下生成timed_output

if (ret < 0)

return ret;

tdev->index = atomic_inc_return(&device_count);

tdev->dev = device_create(timed_output_class, NULL,

MKDEV(0, tdev->index), NULL, tdev->name);

if (IS_ERR(tdev->dev))

return PTR_ERR(tdev->dev);

ret = device_create_file(tdev->dev, &dev_attr_enable);

if (ret < 0)

goto err_create_file;

dev_set_drvdata(tdev->dev, tdev);

tdev->state = 0;

return 0;

err_create_file:

device_destroy(timed_output_class, MKDEV(0, tdev->index));

printk(KERN_ERR "timed_output: Failed to register driver %s\n",

tdev->name);

return ret;

}

 

此函數用於在sys/class下面創建類,類的名字爲timed_output

static int create_timed_output_class(void)

{

if (!timed_output_class) {

timed_output_class = class_create(THIS_MODULE, "timed_output");

if (IS_ERR(timed_output_class))

return PTR_ERR(timed_output_class);

atomic_set(&device_count, 0);

}

return 0;

}

 

 

其中下面兩個函數最爲關鍵,是內核空間和用戶空間的傳值過程的具體實現函數,

enable_show函數調用get_time函數並將返回的剩餘時間寫入buf並傳遞到用戶空間。

 

static ssize_t enable_show(struct device *dev, struct device_attribute *attr,

char *buf)

{

struct timed_output_dev *tdev = dev_get_drvdata(dev);

int remaining = tdev->get_time(tdev);

return sprintf(buf, "%d\n", remaining);

}

 

enable_store函數用於將用戶空間傳遞來的buf值寫入內核空間。

static ssize_t enable_store(

struct device *dev, struct device_attribute *attr,

const char *buf, size_t size)

{

struct timed_output_dev *tdev = dev_get_drvdata(dev);

int value;

if (sscanf(buf, "%d", &value) != 1)

return -EINVAL;

tdev->enable(tdev, value);

return size;

}

 

Timed_gpio.h文件分析:

#ifndef _LINUX_TIMED_GPIO_H

#define _LINUX_TIMED_GPIO_H

#define TIMED_GPIO_NAME "timed-gpio" //Time_GPIO驅動的名字,將顯示在/sys/class/ timed_output目錄下

struct timed_gpio {

const char *name; //GPIO的名字

unsigned  gpio; //具體的GPIO管腳

int  max_timeout;//最大的超時時間 

u8   active_low; //IO口電平狀態表示位

};

timed_gpio結構體僅用於定義單個的GPIO的相關信息

gpio_platform_data結構體用於定義一組GPIO的相關信息

struct timed_gpio_platform_data {

int   num_gpios;

struct timed_gpio *gpios;

};

#endif

 

下面將分析具體的Timed_gpio驅動程序

timed_gpio_driver定義如下:

該函數指明瞭具體的初始化函數(pore)和移除函數(remove)以及驅動的名字額模塊。

static struct platform_driver timed_gpio_driver = {

.probe  = timed_gpio_probe,

.remove  = timed_gpio_remove,

.driver  = {

.name  = TIMED_GPIO_NAME,

.owner  = THIS_MODULE,

},

};

 

調用platform_driver_register函數向kernel註冊平臺驅動

static int __init timed_gpio_init(void)

{

return platform_driver_register(&timed_gpio_driver);

}

timed_gpio探測函數

static int timed_gpio_probe(struct platform_device *pdev)

{

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

struct timed_gpio *cur_gpio;

struct timed_gpio_data *gpio_data, *gpio_dat;

int i, j, ret = 0;

if (!pdata)

return -EBUSY; //pdata->num_gpiosGPIO分配內存空間

gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios,

GFP_KERNEL);

if (!gpio_data)

return -ENOMEM;

for (i = 0; i < pdata->num_gpios; i++) {

cur_gpio = &pdata->gpios[i];

gpio_dat = &gpio_data[i];

hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,//初始化定時器

HRTIMER_MODE_REL);

gpio_dat->timer.function = gpio_timer_func;//定時器回調函數

spin_lock_init(&gpio_dat->lock);

gpio_dat->dev.name = cur_gpio->name;

gpio_dat->dev.get_time = gpio_get_time;

gpio_dat->dev.enable = gpio_enable;

ret = gpio_request(cur_gpio->gpio, cur_gpio->name);//申請GPIO

if (ret >= 0) {

ret = timed_output_dev_register(&gpio_dat->dev);//調用timed_output框架註冊函數

if (ret < 0)

gpio_free(cur_gpio->gpio);

}

if (ret < 0) {

for (j = 0; j < i; j++) {

timed_output_dev_unregister(&gpio_data[i].dev);

gpio_free(gpio_data[i].gpio);

}

kfree(gpio_data);

return ret;

}

gpio_dat->gpio = cur_gpio->gpio;

gpio_dat->max_timeout = cur_gpio->max_timeout;

gpio_dat->active_low = cur_gpio->active_low;

gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);//初始化GPIO的輸出值

}

platform_set_drvdata(pdev, gpio_data);

return 0;

}

 

初始化過程:

1. 首先調用kzalloc函數爲 GPIO分配內存空間

2. 調用hrtimer_init函數初始化化內核定時器

3. 設置GPIOenable函數爲gpio_enable

4. 設置GPIOget_time函數爲gpio_get_time

5. 調用timed_output_dev_register函數註冊設備驅動。

6. 初始化timed_gpio_data結構體

7. 調用gpio_direction_output函數設置GPIO的初始值。

 

GPIO驅動移除函數,調用timed_output_dev_unregister卸載驅動程序

static int timed_gpio_remove(struct platform_device *pdev)

{

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

struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev);

int i;

for (i = 0; i < pdata->num_gpios; i++) {

timed_output_dev_unregister(&gpio_data[i].dev);

gpio_free(gpio_data[i].gpio);

}

kfree(gpio_data);

return 0;

}

 

功能回調函數gpio_timer_func分析:定時器超時後將執行此函數,此函數根據active_low的值來設置GPIO的高低電平。

static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)

{

struct timed_gpio_data *data =

container_of(timer, struct timed_gpio_data, timer);

 

gpio_direction_output(data->gpio, data->active_low ? 1 : 0);

return HRTIMER_NORESTART;

}

 

 

gpio_enable函數爲關鍵函數接受用戶空間傳過來的value值用於在一定時間裏控制GPIO

static void gpio_enable(struct timed_output_dev *dev, int value)

{

struct timed_gpio_data *data =

container_of(dev, struct timed_gpio_data, dev);

unsigned long flags;

 

spin_lock_irqsave(&data->lock, flags);

 

/* cancel previous timer and set GPIO according to value */

hrtimer_cancel(&data->timer);

gpio_direction_output(data->gpio, data->active_low ? !value : !!value);

 

if (value > 0) {

if (value > data->max_timeout)

value = data->max_timeout;

       //啓動定時器函數

hrtimer_start(&data->timer,

ktime_set(value / 1000, (value % 1000) * 1000000),

HRTIMER_MODE_REL);

}

spin_unlock_irqrestore(&data->lock, flags);

}

到這裏相信大家對Time GPIO驅動已經用了深刻的印象和認識。下面將用一幅圖來說明整個Time GPIO驅動的調用過程。以總結回顧前面的分析。

wKiom1UuPZPh7nj0AAFoVmriAB0672.jpg 

讀者可以根據我的分析結合源代碼具體瞭解每一步的調用過程。

 

注:以上整個過程實現了並創建了設備節點/sys/class/timed_output/timed-gpio

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章