Android 从上层到底层-----kernel层
Android led底层驱动
CPU:RK3288
系统:Android 5.1
功能:上层 app 控制 led 亮灭
开发板:Firefly RK3288
1、在dts文件中增加 led 设备
path:kernel/arch/arm/boot/dts/firefly-rk3288.dts
firefly-led{ //匹配内容 compatible = "firefly,led"; //led引脚及有效电平 led-work = <&gpio8 GPIO_A2 GPIO_ACTIVE_LOW>; status = "okay"; };
2、led 驱动
path:kernel/drivers/firefly/leds.c
#include <linux/init.h> #include <linux/kernel.h> #include <linux/uaccess.h> #include <linux/proc_fs.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/miscdevice.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #define DEBUG #ifdef DEBUG #define LED_DBG(fmt,arg...) do{\ printk("<LED-DEBUG> [%d]"fmt"\n", __LINE__, ##arg);\ }while(0) #else #define LED_DBG(fmt,arg...) #endif struct led_data { int work_pin; //led引脚 int work_status; //led状态 int work_enable; //led使能电平 }; struct led_data *led_info; static int led_drv_open(struct inode *inode, struct file *filp) { LED_DBG("--%s()--", __func__); return 0; } static int led_drv_release(struct inode *inode, struct file *filp) { LED_DBG("--%s()--", __func__); return 0; } static ssize_t led_drv_read(struct file *filp, char __user *buf, size_t len, loff_t *pos) { int ret; LED_DBG("--%s()--", __func__); ret = copy_to_user(buf, &(led_info->work_status), len); if(ret > 0) { LED_DBG("copy to user failed."); return ret; } return len; } static ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos) { int on; int ret; LED_DBG("--%s()--", __func__); ret = copy_from_user(&on, buf, len); if(ret > 0) { LED_DBG("copy from user failed."); return ret; } gpio_direction_output(led_info->work_pin, !on); led_info->work_status = on; return ret; } //上层没添加此函数api static long led_drv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { LED_DBG("--%s()--", __func__); return 0; } struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_drv_open, .release = led_drv_release, .write = led_drv_write, .read = led_drv_read, .unlocked_ioctl = led_drv_ioctl, }; //注册为杂项设备 struct miscdevice led_dev = { .minor = MISC_DYNAMIC_MINOR, .fops = &led_fops, .name = "firefly_leds", }; static int led_drv_probe(struct platform_device *pdev) { int ret = -1; enum of_gpio_flags work_flags; struct device_node *led_node = pdev->dev.of_node; LED_DBG("--%s()--", __func__); led_info = kzalloc(sizeof(struct led_data), GFP_KERNEL); if(led_info == NULL) { LED_DBG("Alloc GFP_KERNEL memory failed."); return -ENOMEM; } //从dts中获取led的引脚,和有效电平 led_info->work_pin = of_get_named_gpio_flags(led_node, "led-work", 0, &work_flags); if(!gpio_is_valid(led_info->work_pin)) { LED_DBG("Work pin is invaild."); ret = -ENODEV; goto dev_fail; } led_info->work_enable = (work_flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1; ret = gpio_request(led_info->work_pin, "work_led"); if(ret < 0) { LED_DBG("Work led request failed."); goto dev_fail; } gpio_direction_output(led_info->work_pin, !(led_info->work_enable)); led_info->work_status = 0; misc_register(&led_dev); return 0; request_fail: gpio_free(led_info->work_pin); dev_fail: kfree(led_info); return ret; } static int led_drv_remove(struct platform_device *pdev) { misc_deregister(&led_dev); LED_DBG("--%s()--", __func__); gpio_free(led_info->work_pin); kfree(led_info); } int led_drv_suspend(struct device *dev) { LED_DBG("--%s()--", __func__); return 0; } int led_drv_resume(struct device *dev) { LED_DBG("--%s()--", __func__); return 0; } //suspend和resume暂时没调试成功 const struct dev_pm_ops led_pm_ops = { .suspend = led_drv_suspend, .resume = led_drv_resume, }; static const struct of_device_id of_gpio_leds_match[] = { {.compatible = "firefly,led", }, {}, }; static struct platform_driver led_drv = { .probe = led_drv_probe, .remove = led_drv_remove, .driver = { .name = "leds", .owner = THIS_MODULE, .pm = &led_pm_ops, .of_match_table = of_match_ptr(of_gpio_leds_match), }, }; module_platform_driver(led_drv); MODULE_LICENSE("GPL"); MODULE_AUTHOR("AaronLee");
3、Makefile
path:kernel/drivers/firefly/Makefile
obj-$(CONFIG_FIREFLY_RK3288_LEDS) += leds.o
4.Kconfig
path:kernel/drivers/firefly/Kconfig
menu "Firefly Device Options" config FIREFLY_RK3288_LEDS bool "select firefly rk3288 leds" default y endmenu
5、defconfig文件中增加led
path:kernel/arch/arm/configs/firefly-rk3288_defconfig
CONFIG_FIREFLY_RK3288_LEDS=y
6、修改led节点的权限
path:device/rockchip/common/ueventd.rockchip.rc
/dev/firefly_leds 0666 system system