platform平台总线的LED驱动代码实践

文章介绍了在S5PV210平台上的LED驱动程序开发,涉及platform_device的配置,gpio管理,以及如何避免多个LED设备共享驱动导致的问题。作者通过创建自定义结构体并定义probe和remove函数来确保正确驱动不同LED灯。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码汇总

1、检查mach-x210.c中是否有led 相关的 platform_device(1726行上下附近都是)
  注意:platform_device中的name要与platform_driver驱动中相同,否则无法匹配。可能三个led都会匹配上同一个驱动,使得probe函数被执行三次。这就使得每次申请使用后要及时释放,否则第二个设备就无法使用platform_driver了,会出现错误。

添加:

static struct s5pv210_led_platdata x210_led0_pdata = {
	.name		= "led0",
	.gpio		= S5PV210_GPJ0(3),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct platform_device x210_led0 = {
	.name		= "s5pv210_led",
	.id		= 0,
	.dev		= {
		.platform_data	= &x210_led0_pdata,
	},
};

static struct s5pv210_led_platdata x210_led1_pdata = {
	.name		= "led1",
	.gpio		= S5PV210_GPJ0(4),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct platform_device x210_led1 = {
	.name		= "s5pv210_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &x210_led1_pdata,
	},
};

2、参考mach-mini2440.c中添加led的platform_device定义在mach-x210.c文件中

  所以在kernel\arch\arm\mach-s5pv210\include\mach目录下添加leds-gpio.h文件,否则没有类似struct s3c24xx_led_platdata这样的数据结构的定义

/* arch/arm/mach-s5cpv210/include/mach/leds-gpio.h
 *
 * Copyright (c) 2006 Simtec Electronics
 *	http://armlinux.simtec.co.uk/
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * S5PV210 - LEDs GPIO connector
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#ifndef __ASM_ARCH_LEDSGPIO_H
#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"

#define S5PV210_LEDF_ACTLOW	(1<<0)		/* LED is on when GPIO low */
#define S5PV210_LEDF_TRISTATE	(1<<1)		/* tristate to turn off */

struct s5pv210_led_platdata {
	unsigned int		 gpio;
	unsigned int		 flags;

	char			*name;
	char			*def_trigger;
};

#endif /* __ASM_ARCH_LEDSGPIO_H */

   将修改好的文件同步到ubuntu的内核源码目录下,重新编译内核进行烧录

3、驱动程序:led-s5pv210.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
#include <linux/slab.h>


#define GPIO_LED1 S5PV210_GPJ0(3)

#define x210_LED_OFF 1      //210开发板中led正极接电源,负极接GPIO
#define x210_LED_ON  0      //所以1是灭,0是亮
 
struct led_classdev led_cdev;//定义结构体变量,用于描述led设备类的一个设备

struct s5pv210_gpio_led {
	
	struct led_classdev		 cdev;
	struct s5pv210_led_platdata	*pdata;

};

static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
	return platform_get_drvdata(dev);
}

static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)
{
	return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
}

static void s5pv210_led_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	struct s5pv210_gpio_led *p = to_gpio(led_cdev);
	
	printk(KERN_INFO "s5pv210_led_set\n");
	
	//在这里根据用户设定的值(value)操作硬件
	if (value == LED_OFF)
	{
		gpio_set_value(p->pdata->gpio, x210_LED_OFF);
		
	}
	else
	{
		gpio_set_value(p->pdata->gpio, x210_LED_ON);
		
	}

}

static int s5pv210_led_probe(struct platform_device *dev)
{
	int ret = -1;
	
	struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
	struct s5pv210_gpio_led *led;

	printk(KERN_INFO "-----s5pv210_led_probe------\n");

	led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);
	
	if (led == NULL) {
		dev_err(&dev->dev, "No memory for device\n");
		return -ENOMEM;
	}

	platform_set_drvdata(dev, led);
	
	//在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源
	if (gpio_request(pdata->gpio, pdata->name))
	{
		printk(KERN_INFO "pdata.gpio_request failed.\n");
		
	}
	else
	{
		//设置为输出模式,并且默认输出1让led灯灭
		gpio_direction_output(pdata->gpio, 1);
	}

	
	//填充描述led类设备的属性的结构体变量
	led->cdev.name = pdata->name;
	led->cdev.brightness = 0;
	led->cdev.brightness_set = s5pv210_led_set;
	led->pdata = pdata;
	
	ret = led_classdev_register(&dev->dev, &led->cdev);//来源于led-class.c,创建属于led类的
												 //一个设备
	if (ret < 0) {								
		printk(KERN_INFO "led_classdev_register failed\n");
		return ret;
	}
	
	return 0;

}

static int s5pv210_led_remove(struct platform_device *dev)
{
	struct s5pv210_gpio_led *led = pdev_to_gpio(dev);
	
	printk(KERN_INFO "-----s5pv210_led_remove------\n");

	led_classdev_unregister(&led->cdev);
	kfree(led);

	gpio_free(led->pdata->gpio);

	return 0;	

}

static struct platform_driver s5pv210_led_driver = {
	.probe		= s5pv210_led_probe,
	.remove		= s5pv210_led_remove,
	.driver		= {
		.name		= "s5pv210_led",
		.owner		= THIS_MODULE,
	},
};

static int __init s5pv210_led_init(void)
{
	return platform_driver_register(&s5pv210_led_driver);
}

static void __exit s5pv210_led_exit(void)
{
	platform_driver_unregister(&s5pv210_led_driver);
}


module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");							// 描述模块的许可证
MODULE_AUTHOR("aston <1264671872@qq.com>");		// 描述模块的作者
MODULE_DESCRIPTION("s5pv210 led driver");		// 描述模块的介绍信息
MODULE_ALIAS("s5pv210_led");					// 描述模块的别名信息

细讲:(详细步骤)

第一步:

在drivers\leds中新建leds-s5pv210.c文件,此文件为driver文件

仿照leds-s3c24xx.c文件去写:(卸载和装载函数后面写)

static int s5pv210_led_remove(struct platform_device *dev)//卸载函数
{
	xxxxx

	return 0;
}

static int s5pv210_led_probe(struct platform_device *dev)//装载函数
{
	xxxxx
	return 0;
}

static struct platform_driver s5pv210_led_driver = {
	.probe		= s5pv210_led_probe,
	.remove		= s5pv210_led_remove,
	.driver		= {
		.name		= "s5pv210_led",
		.owner		= THIS_MODULE,
	},
};

static int __init s5pv210_led_init(void)
{
	return platform_driver_register(&s5pv210_led_driver);
}

static void __exit s5pv210_led_exit(void)
{
	platform_driver_unregister(&s5pv210_led_driver);
}

module_init(s5pv210_led_init);
module_exit(s3c24xx_led_exit);

第二步:

现在写device

1.检查.\arch\arm\mach-s5pv210中的mach-x210.c中是否有 led 相关的 platform_device(发现没有)

包括在smdkc110_devices[]数组中也找不到 led ,所以我们需要编写 led 相关的 platform_device 并放在这个数组中。

2、参考mach-mini2440.c中添加 led 的 platform_device ,定义在mach-x210.c文件中

在.\arch\arm\mach-s5pv210\include\mach目录下新建文件:leds-gpio.h 并编写 s5pv210_led_platdate 结构体

#ifndef __ASM_ARCH_LEDSGPIO_H
#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"

#define S5PV210_LEDF_ACTLOW	(1<<0)		/* LED is on when GPIO low */
#define S5PV210_LEDF_TRISTATE	(1<<1)		/* tristate to turn off */

struct s5pv210_led_platdata {
	unsigned int		 gpio;
	unsigned int		 flags;

	char			*name;
	char			*def_trigger;
};

#endif /* __ASM_ARCH_LEDSGPIO_H */

make完后生成了新的zimage,我们把它tftpboot中后,重启开发板查看: 找到了我们写的device

(开发板会自动装载)

第三步:

 前两步我写好了driver的模板与device。现在,让两个“相遇”

现在我们还差装载driver(通过ismode)

1.根据我们的推测:driver只要一装载,就会注册

 2.注册了driver后,因为我们有device 了,所以device和driver的name会进行匹配,匹配上了后就会进行 probe 函数

所以我们现在开始写probe 函数:(见上文的汇总代码),接下来写的是遇到的bug与解决

BUG

gpio request failed,led1可以正常驱动,但是led2和3就不行

分析:

注册了三个led的device,这三个led都是独立的设备,这三个设备都会试图去匹配同一个driver,因为这三个led的设备platform_device的name都是s5pv210_led。每次匹配都会调用一次probe函数。

因为之前的驱动代码是根据直接注册指定的io口,他固定死了,所以导致了重复注册并报错。

(下图为之前代码)。

 思路:我们想办法:

把device的platform_date提取出来,pdata被赋值为 dev->dev.platform_data,即设备的配置数据

struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
gpio_request(pdata->gpio, pdata->name)

 BUG2

我们如果用三个方法操作led,会变得很麻烦(需要编写三个方法)。所以我们使用一个方法,在方法的内部对各个led进行区分。

遇到的困难:led_classdev中,不能传递gpio号。

解决:

自己定义一个结构体:里面包含led的led_classdev和s5pv210_led_platdata(s5pv210_led_platdata里面有gpio号,我们就可以通过下面的函数进行指针的传递)

struct s5pv210_gpio_led {
	struct led_classdev		 cdev;
	struct s5pv210_led_platdata	*pdata;
};

 有两个函数:

platform_get_drvdata(dev);:可以通过platform_device反推s5pv210_gpio_led的指针。通过 platform_device 结构体获取到与之关联的 struct s3c24xx_gpio_led 结构体指针。

container_of(led_cdev, struct s5pv210_gpio_led, cdev);:将一个 struct led_classdev 结构体指针转换为一个指向 struct s3c24xx_gpio_led 结构体的指针。从 struct led_classdev 结构体获取到与之关联的 struct s3c24xx_gpio_led 结构体指针。

static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
	return platform_get_drvdata(dev);
}

static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)
{
	return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值