linux驱动之如何使用内核自带驱动程序

以LED驱动程序为例

一, 内核自带LED驱动使能

1,内核自带的驱动, 都是通过图形化界面配置, 选择使能或者不使用
输入命令打开linux配置菜单

make menuconfig

按照如下路径打开 LED驱动配置项:

-> Device Drivers                                                                                
 -> LED Support (NEW_LEDS [=y]) 
   ->LED Support for GPIO connected LEDs 

在这里插入图片描述
配置好 Linux内核以后退出配置界面,打开.config文件,会找到“CONFIG_LEDS_GPIO=y”
y是编译到内核, m是编译到模块
在这里插入图片描述
重新编译生成内核镜像 zImage

二, 分析内核LED驱动

找到CONFIG_LEDS_GPIO对应的驱动
进入drivers/leds目录, 打开Makefile文件
搜索CONFIG_LEDS_GPIO
在这里插入图片描述
驱动对应的便是 leds-gpio.c文件

分析

注册入口出口 :

module_platform_driver(gpio_led_driver);

利用宏定义字符替换一行搞定

/* module_platform_driver() - Helper macro for drivers that don't do
 * anything special in module init/exit.  This eliminates a lot of
 * boilerplate.  Each module may only use this macro once, and
 * calling it replaces module_init() and module_exit()
 */
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \  ---->__VA_ARGS__可变参数
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

匹配platform_device

static struct platform_driver gpio_led_driver = {
	.probe		= gpio_led_probe,
	.shutdown	= gpio_led_shutdown,
	.driver		= {
		.name	= "leds-gpio", //------>无设备树用来匹配platform_device
		.of_match_table = of_gpio_leds_match,  //------->有设备树的匹配
	},
};

static const struct of_device_id of_gpio_leds_match[] = {
	{ .compatible = "gpio-leds", },//------>这里就可以知道这个驱动匹配的是gpio-leds这个设备树节点
	{},
};

MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

probe函数

static int gpio_led_probe(struct platform_device *pdev)
{
	struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
	struct gpio_leds_priv *priv;
	int i, ret = 0;

	if (pdata && pdata->num_leds) { //没有设备树走这里
		priv = devm_kzalloc(&pdev->dev,
				sizeof_gpio_leds_priv(pdata->num_leds),
					GFP_KERNEL);
		if (!priv)
			return -ENOMEM;

		priv->num_leds = pdata->num_leds;
		for (i = 0; i < priv->num_leds; i++) {
			ret = create_gpio_led(&pdata->leds[i],
					      &priv->leds[i],
					      &pdev->dev, pdata->gpio_blink_set);
			if (ret < 0)
				return ret;
		}
	} else {//有设备树走这里
		priv = gpio_leds_create(pdev);
		if (IS_ERR(priv))
			return PTR_ERR(priv);
	}

	platform_set_drvdata(pdev, priv);

	return 0;
}
static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct fwnode_handle *child;
	struct gpio_leds_priv *priv;
	int count, ret;

	count = device_get_child_node_count(dev); //获取节点下有几个子节点
	if (!count)
		return ERR_PTR(-ENODEV);

	priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);//子节点数量来申请空间
	if (!priv)
		return ERR_PTR(-ENOMEM);

	device_for_each_child_node(dev, child) { //遍历设备子节点, 所有子节点属性读取出来
		struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
		struct gpio_led led = {};
		const char *state = NULL;
		struct device_node *np = to_of_node(child);

		led.gpiod = devm_get_gpiod_from_child(dev, NULL, child); //获取gpiod gpio描述符
		if (IS_ERR(led.gpiod)) {
			fwnode_handle_put(child);
			return ERR_CAST(led.gpiod);
		}

		ret = fwnode_property_read_string(child, "label", &led.name);//读取设备树label属性
		if (ret && IS_ENABLED(CONFIG_OF) && np)
			led.name = np->name;
		if (!led.name) {
			fwnode_handle_put(child);
			return ERR_PTR(-EINVAL);
		}

		fwnode_property_read_string(child, "linux,default-trigger",
					    &led.default_trigger);//读取linux,default-trigger属性

		if (!fwnode_property_read_string(child, "default-state",
						 &state)) {//读取default-state属性
			if (!strcmp(state, "keep"))
				led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
			else if (!strcmp(state, "on"))
				led.default_state = LEDS_GPIO_DEFSTATE_ON;
			else
				led.default_state = LEDS_GPIO_DEFSTATE_OFF;
		}

		if (fwnode_property_present(child, "retain-state-suspended"))
			led.retain_state_suspended = 1;
		if (fwnode_property_present(child, "panic-indicator"))
			led.panic_indicator = 1;

		ret = create_gpio_led(&led, led_dat, dev, NULL);
		if (ret < 0) {
			fwnode_handle_put(child);//删除节点引用
			return ERR_PTR(ret);
		}
		led_dat->cdev.dev->of_node = np;
		priv->num_leds++;
	}

	return priv;
}
static int create_gpio_led(const struct gpio_led *template,
	struct gpio_led_data *led_dat, struct device *parent,
	gpio_blink_set_t blink_set)
{
	int ret, state;

	led_dat->gpiod = template->gpiod;
	if (!led_dat->gpiod) {
		/*
		 * This is the legacy code path for platform code that
		 * still uses GPIO numbers. Ultimately we would like to get
		 * rid of this block completely.
		 */
		unsigned long flags = GPIOF_OUT_INIT_LOW;

		/* skip leds that aren't available */
		if (!gpio_is_valid(template->gpio)) {
			dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
					template->gpio, template->name);
			return 0;
		}

		if (template->active_low)
			flags |= GPIOF_ACTIVE_LOW;

		ret = devm_gpio_request_one(parent, template->gpio, flags,
					    template->name);//申请gpio
		if (ret < 0)
			return ret;

		led_dat->gpiod = gpio_to_desc(template->gpio);
		if (!led_dat->gpiod)
			return -EINVAL;
	}

	led_dat->cdev.name = template->name;
	led_dat->cdev.default_trigger = template->default_trigger;
	led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
	if (!led_dat->can_sleep)
		led_dat->cdev.brightness_set = gpio_led_set;
	else
		led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking;
	led_dat->blinking = 0;
	if (blink_set) {
		led_dat->platform_gpio_blink_set = blink_set;
		led_dat->cdev.blink_set = gpio_blink_set;
	}
	if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
		state = gpiod_get_value_cansleep(led_dat->gpiod);
		if (state < 0)
			return state;
	} else {
		state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
	}
	led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
	if (!template->retain_state_suspended)
		led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
	if (template->panic_indicator)
		led_dat->cdev.flags |= LED_PANIC_INDICATOR;

	ret = gpiod_direction_output(led_dat->gpiod, state);//设置成输出模式
	if (ret < 0)
		return ret;

	return devm_led_classdev_register(parent, &led_dat->cdev);
}


三, 根据绑定文档在设备树里添加对应的设备节点

Documentation/devicetree/bindings/leds/leds-gpio.txt在这里插入图片描述
①、创建一个节点表示 LED灯设备,比如 dtsleds,如果板子上有多个 LED灯的话每个 LED
灯都作为 dtsleds的子节点。
②、dtsleds节点的 compatible属性值一定要为“gpio-leds”。
③、设置 label属性,此属性为可选,每个子节点都有一个 label属性,label属性一般表示
LED灯的名字,比如以颜色区分的话就是 red、green等等。
④、每个子节点必须要设置 gpios属性值,表示此 LED所使用的 GPIO引脚!
⑤、可以设置“linux,default-trigger”属性值,也就是设置 LED灯的默认功能,可以查阅
Documentation/devicetree/bindings/leds/common.txt这个文档来查看可选功能,比如:
backlight:LED灯作为背光。
default-on:LED灯打开
heartbeat:LED灯作为心跳指示灯,可以作为系统运行提示灯。
ide-disk:LED灯作为硬盘活动指示灯。
timer:LED灯周期性闪烁,由定时器驱动,闪烁频率可以修改
⑥、可以设置“default-state” 属性值,可以设置为 on、off或 keep,为 on的时候 LED灯默
认打开,为 off的话 LED灯默认关闭,为 keep的话 LED灯保持当前模式。

1  dtsleds { 
2      compatible = "gpio-leds"; 
3  
4      led0 { 
5          label = "red"; 
6          gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; 
7          linux,default-trigger = "heartbeat"; 
8          default-state = "on"; 
9      }; 
10 };

用户空间也能操作led

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值