在.dts中定義一個led_gpio(client device)節點,子節點包含pinctrl系統,整個節點會在核心中注冊成一個platform_device。對應的要注冊一個platform_driver,當insmod drv.ko時,device和driver比對之後,就執行probe()獲得引腳,注冊file_operations,設定方向、讀值/寫值。???在file_opretions中進行讀寫???
不同之處,之前用board_A_led.c注冊的platform_device,用chip_demo_gpio.c注冊platform_driver。現在隻剩下leddrv.c檔案了,其他檔案都沒有了啊!看來pinctrl+gpio可以省好多代碼!
7.10.3.1.ledtest.c
沒有改變。
7.10.3.2.leddrv.c
用代碼對比看了下改動挺大的,逐一分析。
1.增加了2個.h檔案,
#include <linux/gpio/consumer.h> //包含GPIO擷取和使用的函數
#include <linux/platform_device.h>
2.新增一個結構體指針變量,static struct gpio_desc *led_gpio,看下原定義:
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
……
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
};
搜尋了一下,有一個部落格有一個很好的說明,借用下:https://blog.csdn.net/u010388659/article/details/81286821,
“如上圖所示,右上方部分為GPIO驅動對其它驅動提供的GPIO操作接口,其對應的右下方部分為GPIO硬體操作接口,也就是說對外提供的接口最終會一一對應的對硬體GPIO進行操作。再來看左邊部分,左上方部分為一全局數組,記錄各個GPIO的描述符,即對應左下方的gpio_desc結構體,其中gpio_chip指向硬體層的GPIO,flags為一标志位,用來訓示目前GPIO是否已經占用,當用gpio_request申請GPIO資源時,flags位就會置位,當調用gpio_free釋放GPIO資源時,flags就會清零。label是一個字元串指針,用來作說明。”(暫時不了解)
3.led_init()/led_exit(),看驅動程式先看入口函數和出口函數,入口函數用來向核心注冊platform_driver,以前的函數是向核心注冊一個chrdev然後建立dev class,出口函數也對應隻是解除安裝platform_driver即可。另外,定義了platform_driver結構體,之前是在chip_demo_gpio.c中定義的,結構體内容不多說了之前都說過。
134: /* 4.定義比對platform_device的compatible變量 */
135: static const struct of_device_id ask100_leds[] = {
136: { .compatible = "100ask,leddrv" },
137: { },
138: };
126: /* 1. 定義platform_driver */
127: static struct platform_driver chip_demo_gpio_driver = {
128: .probe = chip_demo_gpio_probe,
129: .remove = chip_demo_gpio_remove,
130: .driver = {
131: .name = "100ask_led",
132: .of_match_table = ask100_leds,
133: },
134: };
135:
136: /* 2. 注冊platform_driver */
137: static int __init led_init(void)
138: {
139: int err;
141: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
142:
143: /* 2.1 注冊platform_driver */
144: err = platform_driver_register(&chip_demo_gpio_driver);
145:
146: return err;
147: }
148:
149: /* 3. 解除安裝platform_driver */
150: static void __exit led_exit(void)
151: {
152: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
153:
154: /* 3.1 解除安裝platform_driver */
155: platform_driver_unregister(&chip_demo_gpio_driver);
156: }
看下platform_driver_register(),可以依次追蹤一下driver是如何和device macthing的以及如何調用probe函數的。
如果probe和remove沒有内容的話,那最簡單的leddrv是不是可以就這樣了,編譯,insmod之後platform_driver和經裝置樹轉化的platform_driver比對之後就可以什麼都不做了!不行,比如執行測試程式,./ledtest /dev/led0 on,需要先open這個/dev/led0次裝置,問題是此時核心中沒都沒這個次裝置呢!是以還是得需要file_operations注冊一個裝置,之前都是在入口函數中注冊,現在轉移到probe中注冊了。
4.probe(),主要有4點功能:
24: /* 6.0定義gpio_dsec結構體 */
25: static struct gpio_desc *led_gpio;
83: /* 6. 定義probe函數
84: * 功能:
85: * a.從platform_device獲得GPIO節點;
86: * b.注冊file_operations結構體,建立主裝置;
87: * c.建立裝置類;
88: * d.建立次裝置;
89: */
90: static int chip_demo_gpio_probe(struct platform_device *pdev)
91: {
92: //int err;
93:
94: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
95:
96: /* 6.1擷取裝置節點led-gpios=<...>; */
97: led_gpio = gpiod_get(&pdev->dev, "led", 0);
98: if (IS_ERR(led_gpio)) {
99: dev_err(&pdev->dev, "Failed to get GPIO for led\n");
100: return PTR_ERR(led_gpio);
101: }
102:
103: /* 6.2注冊file_operations結構體,建立100ask_led裝置 */
104: major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/100ask_led */
105:
106: /* 6.3建立100ask_led_class */
107: led_class = class_create(THIS_MODULE, "100ask_led_class");
108: if (IS_ERR(led_class)) {
109: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
110: unregister_chrdev(major, "led");
111: gpiod_put(led_gpio);
112: return PTR_ERR(led_class);
113: }
114:
115: /* 6.4建立100ask_ledx次裝置 */
116: device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0);
116: /* /dev/100ask_led0 */
117:
118: return 0;
120: } « end chip_demo_gpio_probe »
122: /* 8實作remove函數 */
123: static int chip_demo_gpio_remove(struct platform_device *pdev)
124: {
125: /* 依次解除安裝次裝置、裝置類、主裝置和釋放gpio節點 */
126: device_destroy(led_class, MKDEV(major, 0));
127: class_destroy(led_class);
128: unregister_chrdev(major, "100ask_led");
129: gpiod_put(led_gpio);
130:
131: return 0;
132: }
看下gpiod_get()
/**
* gpiod_get - obtain a GPIO for a given GPIO function
* @dev: GPIO consumer, can be NULL for system-global GPIOs
* @con_id: function within the GPIO consumer
* @flags: optional GPIO initialization flags
*
* Return the GPIO descriptor corresponding to the function con_id of device
* dev, -ENOENT if no GPIO has been assigned to the requested function, or
* another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return gpiod_get_index(dev, con_id, 0, flags);
}
然後gpiod_get->gpiod_get_index->of_find_gpio->
of_get_named_gpiod_flags依次找到dts led-gpio node,那它又是怎麼去比對的pinctrl呢?自己找了下:
platform_driver_register
__platform_driver_register
driver_register
bus_add_driver
driver_attach
bus_for_each_dev
__driver_attach
driver_probe_device
really_probe
pinctrl_bind_pins
pinctrl_lookup_state
ret = drv->probe(dev);
5.定義file_operations結構體,結構體成員沒有變化,open函數和write有所改變,可以直接對gpio子系統操作。
28: /* 7.實作open/read/write/close等函數 */
29:
30: /* 7.1實作open函數 */
31: static int led_drv_open (struct inode *node, struct file *file)
32: {
33: //int minor = iminor(node);
34:
35: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
36: /* 根據次裝置号初始化LED */
37: gpiod_direction_output(led_gpio, 0);
38:
39: return 0;
40: }
41:
48:
49: /* 7.3實作write函數 */
50: /* write(fd, &val, 1); */
51: static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t
51: *offset)
52: {
53: int err;
54: char status;
58: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
59: err = copy_from_user(&status, buf, 1);
60:
61: /* 根據次裝置号和status控制LED */
62: gpiod_set_value(led_gpio, status);
63:
64: return 1;
65: }
/**
* gpiod_direction_output - set the GPIO direction to output
* @desc: GPIO to set to output
* @value: initial output value of the GPIO
*
* Set the direction of the passed GPIO to output, such as gpiod_set_value() can
* be called safely on it. The initial value of the output must be specified
* as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
* account.
*
* Return 0 in case of success, else an error code.
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return _gpiod_direction_output_raw(desc, value);
}
_gpiod_direction_output_raw
gpiod_direction_input
gpio_chip_hwgpio
*********************************************************************
/**
* gpiod_set_value() - assign a gpio's value
* @desc: gpio whose value will be assigned
* @value: value to assign
*
* Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
* account
*
* This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_value_cansleep() */
WARN_ON(desc->gdev->chip->can_sleep);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
_gpiod_set_raw_value(desc, value);
}
_gpiod_set_raw_value
_gpio_set_open_drain_value
_gpio_set_open_source_value
小結:核心通過gpio_get()獲知led-gpios,進而擷取到IO的複用配置,通過gpiod_direction_output設定IO為output,通過gpiod_set_value設定IO邏輯電平,進而控制LED。