Linux GPIO驅動 - GPIO通用層

如果在內核配置的時候有打開CONFIG_GPIO_SYSFS標誌,內核就會在/sys目錄下導出GPIO的用戶空間操作接口。gpiolib_sysfs_init(drivers/gpio/gpiolib.c)是gpio lib的初始化函數,該函數首先在/sys/class/目錄下面創建一個gpio的class,然後把所有註冊到gpio lib的控制器導出到/sys/class/gpio/目錄下面。

static int __init gpiolib_sysfs_init(void)

{

......

status = class_register(&gpio_class);

......

for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {

......

status = gpiochip_export(chip);

......

}

......

}

gpio_class的name定義爲gpio,所以我們可以在/sys/class/下面看到gpio的目錄。class_attr被定義爲gpio_class_attrs,裏面定義了class的兩個屬性export和unexport,該屬性最終會在用戶空間生成對應的文件。class_attribute包含一個attribute變量和show/sotre兩個方法,其中attribute用來標示該屬性的名字和操作權限,show/store是對該屬性進行操作的函數,對應用戶空間的cat/echo操作。所以在/sys/class/gpio目錄下面可以看到兩個文件分別爲export和unexport,同時可以看到這兩個文件的屬性爲所有者可寫,即對應創建時定義的0200。所以這兩個文件只能用echo向文件寫內容,而不能通過cat顯示文件的內容。從export和unexport的定義也可以驗證這點,它們的show都定義爲NULL,shore分別定義export_store和unexport_store,由此可以得知這兩個函數就是用戶空間操作的最終執行函數。先跳過這兩個函數,繼續看gpiolib_sysfs_init的第二部分工作。

static struct class_attribute gpio_class_attrs[] = {

__ATTR(export, 0200, NULL, export_store),

__ATTR(unexport, 0200, NULL, unexport_store),

__ATTR_NULL,

};

#define __ATTR(_name,_mode,_show,_store) { \

.attr = {.name = __stringify(_name), .mode = _mode },\

.show = _show,

.store = _store,

}

第二部分的工作就是遍歷每一個註冊的gpio pin,然後把註冊到gpio lib的GPIO控制器導出到用戶空間。首先在創建一個名爲gpiochip*的設備,所以可以在/sys/class/gpio目錄下面看到對應的gpiochip*目錄,以及該目錄下面的subsystem和uevent兩個屬性文件。然後再在該目錄下面創建定義的屬性文件,從屬性數組gpiochip_attrs可以看到總共定義了三個屬性,分別爲base,label和ngpio,屬性的權限爲所有用戶可讀。最後把該控制器的exported標誌設置爲1,表示已經導出到用戶空間。

static int gpiochip_export(struct gpio_chip *chip)

{

......

dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,"gpiochip%d", chip->base);

status = sysfs_create_group(&dev->kobj,&gpiochip_attr_group);

......

}

回到第一部分來看export_store函數,該函數同樣分成gpio_request()和gpio_export()兩個階段。在gpio_request中,首先判斷該pin的有效性,即該pin的pin num不能超過最大的GPIO pin腳數,然後把FLAG_REQUESTED標記設爲1,表示該pin已被申請使用,同時把label屬性設置爲sysfs,最後調用gpio chip註冊時定義的request函數,由前面的硬件驅動分析可知,該函數直接返回一個0值。gpio_export()的執行過程和gpiochip_export比較類似,先創建一個gpio*的設備,該過程會在用戶空間的/sys/class/gpio目錄下面導出gpio*的目錄和該目錄下面的subsytem/uevent屬性文件。

int gpio_export(unsigned gpio, bool direction_may_change)

{

......

dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),desc, ioname ? ioname : "gpio%u", gpio);

status = sysfs_create_group(&dev->kobj,&gpio_attr_group);

if (!status && direction_may_change)

status = device_create_file(dev,&dev_attr_direction);

f (!status && gpio_to_irq(gpio) >= 0&& (direction_may_change|| !test_bit(FLAG_IS_OUT,&desc->flags)))  

status = device_create_file(dev,&dev_attr_edge);

......

set_bit(FLAG_EXPORT, &desc->flags);

......

}

在/sys/class/gpio/目錄下面導出gpio*目錄後,再在該目錄下生成各種屬性文件。首先創建的是gpio_attr_group屬性組,該組裏麪包含value和active_low兩個屬性,這兩個屬性用DEVICE_ATTR來定義,從定義可以看出,屬性文件的權限是0644,即所有者可讀可寫可執行,用戶組和其他用戶是可讀。在用戶空間可以通過value查看和設置GPIO pin的值,通過active_low來查看和設置pin的active_low的標誌位。如果註冊的GPIO chip有定義GPIO方向操作的函數,則創建direction屬性,該屬性同樣是用DEVICE_ATTR宏來創建,屬性的權限也爲0644,用戶可以利用/sys/class/gpio/gpio*/direction文件來控制gpio pin的操作方向。如果要導出的Pin可以作中斷使用,同時該pin沒有設置FLAG_IS_OUT位,則用創建edge屬性;當該pin作爲中斷pin使用時,用戶可以利用/sys/class/gpio/gpio*/edge文件設置中斷觸發的方式。

static const DEVICE_ATTR(active_low, 0644,gpio_active_low_show, gpio_active_low_store);

static const DEVICE_ATTR(value, 0644,gpio_value_show, gpio_value_store);

static /* const */ DEVICE_ATTR(direction, 0644,gpio_direction_show, gpio_direction_store);

static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);

以value屬性來分析屬性文件的具體操作,當用戶在用戶空間用cat打開value文件查看gpio pin的值時,gpio_value_show()會被調用。該函數的主要調用路徑爲  gpio_get_value_cansleep()->gpio_to_chip()->chip->get,即通過gpio pin找到該pin對應的gpio chip,然後利用該chip的get函數從GPIO控制器的寄存器裏面讀到具體的值。

當用戶在用戶空間用ehco把值寫到value文件時,gpio_value_store會被調用,該函數的作用跟gpio_value_show相反,是把用戶空間傳遞過來的值寫到GPIO控制器的寄存器。調用路徑爲gpio_set_value_cansleep()->gpio_to_chip()->chip->set。

發佈了23 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章