您好,登錄后才能下訂單哦!
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_register,timed_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_gpios個GPIO分配內存空間
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. 設置GPIO的enable函數為gpio_enable
4. 設置GPIO的get_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驅動的調用過程。以總結回顧前面的分析。
讀者可以根據我的分析結合源代碼具體了解每一步的調用過程。
注:以上整個過程實現了并創建了設備節點/sys/class/timed_output/timed-gpio
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。