嵌入式Linux GPIO驱动翻车记——设备树里改了一下午

📅 2026/7/7 20:36:36
嵌入式Linux GPIO驱动翻车记——设备树里改了一下午
这玩意儿在设备树里配置一下就行了。——说这话的人一定没踩过完整一遍坑。一个月前接了个活某个ARM板子上要控制一路GPIO输出高低电平切换配个LED指示灯。心想多大事儿不就是设备树里加个节点写个简单的字符驱动echo 1 /sys/class/leds/xxx/brightness 完事。结果从下午两点折腾到晚上十点。先看设备树。我用的这个SoCGPIO控制器注册在pinctrl下面每个bank有32个pin。按理说加个gpio-leds节点就行leds { compatible gpio-leds; status_led { label status:green; gpios gpio3 15 GPIO_ACTIVE_HIGH; linux,default-trigger heartbeat; }; };编译设备树烧录重启。ls /sys/class/leds/ 一看空的。这就开始折腾了。第一反应设备树语法错了ftdump 看了一下编译后的dtb节点确实在。但/sys下面没创出来大概率是驱动没匹配上。查内核配置CONFIG_LEDS_GPIO没有开。好家伙这板子默认核心里没编这个驱动。于是重新编内核。make menuconfigDevice Drivers → LED Support → LED Support for GPIO connected LEDs编成模块。make -j8 zImage modules make modules_install depmod -a reboot然后 modprobe leds_gpio。再 ls /sys/class/leds/出来了。但另一个问题来了我想在用户态直接操作这个GPIO不用LED子系统自己写个驱动来控制时序。有次做PWM模拟的时候发现LED子系统的brightness有个内部反转逻辑跟你想的不一样。所以又写了这么个玩意儿#include linux/module.h #include linux/fs.h #include linux/uaccess.h #include linux/gpio.h #include linux/platform_device.h #include linux/of.h #include linux/of_gpio.h #define DEVICE_NAME mygpio #define CLASS_NAME mygpio static int major; static struct class *mygpio_class NULL; static int gpio_pin -1; static ssize_t mygpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { char kbuf[4]; unsigned long val; if (count 3) count 3; if (copy_from_user(kbuf, buf, count)) return -EFAULT; kbuf[count] \0; if (kstrtoul(kbuf, 10, val)) return -EINVAL; gpio_set_value(gpio_pin, val ? 1 : 0); return count; }写的时候注意了copy_from_user之前得检查buffer长度。之前有内核模块因为没检查直接panic整个板子重启别提多尴尬了。然后就是设备树里要加自己的compatiblemygpio { compatible mygpio,ctrl; mygpio-gpios gpio3 15 GPIO_ACTIVE_HIGH; };驱动里用 of_get_named_gpio 来解析static int mygpio_probe(struct platform_device *pdev) { struct device *dev pdev-dev; struct device_node *np dev-of_node; gpio_pin of_get_named_gpio(np, mygpio-gpios, 0); if (gpio_pin 0) { dev_err(dev, failed to get gpio: %d\n, gpio_pin); return gpio_pin; } if (gpio_request(gpio_pin, mygpio)) { dev_err(dev, gpio request failed\n); return -EBUSY; } gpio_direction_output(gpio_pin, 0); return 0; }折腾完发现最坑的其实不是代码本身是Makefile。内核模块的Makefile有一堆讲究KERNEL_DIR、交叉编译链、arch、CROSS_COMPILE但凡有一个少设了编出来的模块要么insmod报错invalid format要么符号找不到。obj-m : mygpio.o KERNEL_DIR : /home/ubuntu/linux-4.19 CROSS_COMPILE : arm-linux-gnueabihf- ARCH : arm all: $(MAKE) -C $(KERNEL_DIR) M$(PWD) ARCH$(ARCH) \ CROSS_COMPILE$(CROSS_COMPILE) modules clean: $(MAKE) -C $(KERNEL_DIR) M$(PWD) ARCH$(ARCH) clean有次CROSS_COMPILE忘了设编出来x86架构的.koinsmod的时候提示wrong ELF class查了半小时才反应过来——板子是ARM的我特么编了个x86的模块上去。最后echo 1 /dev/mygpio灯亮了。就一个灯折腾了一个大半天。嵌入式Linux就这样——上层看着简单每层都有坑。设备树、内核配置、驱动框架、Makefile、交叉编译一条链上任何一个环节断了你都跑不通。下次谁再说配个GPIO很简单我建议ta先自己走一遍完整流程。