简单led驱动(未使用led子系统)

1:概述

2:交叉编译环境搭建

3:编写驱动

4:测试驱动

5:总结


1:概述

1.1:代码基于exynos 4412平台,android 4.4系统,为了提高代码的移植性,将led驱动挂在platform虚拟总线下,自动建立设备节点,创建节点属性,并且gpio操作全部采用标准的gpio操作;

1.2:代码参考led子系统,使用了led子系统的一些数据结构;


2:交叉编译环境搭建

2.1:安装adb工具,adb shell进入板子文件系统,使用 cat /proc/version查看板子的内核版本,然后下载版本相同的内核源码;

2.2:安装交叉编译工具,我使用的是arm-2009q3编译工具,在将其放在/usr/local/arm/目录下,并在.vimrc中将编译工具加入到系统环境变量中;

2.3:内核目录下,修改Makefile中的ARCH和CROSS_COMPILE,生成.config文件(具体依据硬件平台);

2.4:执行make,相当于执行make zImage 和make modules,等待编译完成;


3:贴出代码和Makefile,具体参考注释

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/err.h>
#include <mach/gpio-exynos4.h>

#define USE_IMMEDIATE

static struct class *leds_class;

//用来描述单个的led ddevice
static struct led_dev
{
	struct device *dev;
	struct gpio_led led;
};

//描述设备中所有led 
static struct leds_driver_data
{
	unsigned char leds_num;
	struct led_dev leds[];
};

//节点属性读方法
static ssize_t led_val_show(struct device *dev,struct device_attribute *attr, char *buf)  
{  
	int val = 0;
	struct led_dev *led_dev;
	led_dev = dev_get_drvdata(dev);
	
	val = gpio_get_value(led_dev->led.gpio);
	
	if(val == 1)
		return sprintf(buf,"%s is on\n",led_dev->led.name);
	else
		return sprintf(buf,"%s is off\n",led_dev->led.name);
}

//节点属性写方法
static ssize_t led_val_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t size)  
{  
	struct led_dev *led_dev;
	led_dev = dev_get_drvdata(dev);
	
	if(memcmp(buf,"ON",2) == 0)
	{
		gpio_set_value(led_dev->led.gpio,1);
	}
	else
	if(memcmp(buf,"OFF",3) == 0)
	{
		gpio_set_value(led_dev->led.gpio,0);
	}

	return size;   /*必须返回size*/  
}

//设备属性定义
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,led_val_show, led_val_store);

//探测函数
static int s3c4410_led_probe(struct platform_device *pdev)
{
	int result = 0;
	unsigned char i = 0;
	struct gpio_led_platform_data *pdata = pdev->dev.platform_data;   //从platform_device中获取led的平台数据
	struct leds_driver_data *leds_data;                               //定义led驱动的数据结构体指针,这个结构描述设备中所有的led

	leds_class = class_create(THIS_MODULE,"leds");                    //在/sys/class/下创建leds目录
	if(IS_ERR(leds_class))
	{
		 result = PTR_ERR(leds_class);
		 printk(KERN_ALERT "Failed create leds class\n");
	}

	//为led驱动数据结构体动态申请内存
	leds_data = kmalloc((sizeof(struct leds_driver_data) + sizeof(struct led_dev)*pdata->num_leds),GFP_KERNEL);
	if(leds_data == NULL)
	{
		result = -ENOMEM;  
		printk(KERN_ALERT "Failed alloc template\n"); 
		goto class_destroy;
	}
	
	//获得设备中led的个数
	leds_data->leds_num = pdata->num_leds;

	//遍历设备中的led
	for(i=0;i<leds_data->leds_num;i++)
	{	
		struct led_dev *led_dev;
		led_dev = &leds_data->leds[i];
		led_dev->led.name = pdata->leds[i].name;     //取出名字
		led_dev->led.gpio = pdata->leds[i].gpio;     //取出gpio号

		//在/sys/class/leds/下创建目录,每个led对应一个目录
		led_dev->dev =  device_create(leds_class,&pdev->dev,0,leds_data,"%s",led_dev->led.name);
		if(IS_ERR(led_dev->dev))
		{
			result = PTR_ERR(led_dev->dev);
			printk(KERN_ALERT "Failed create device\n");
			goto free;
		}

		//创建设备节点,比如/sys/class/leds/led1/val
		result = device_create_file(led_dev->dev,&dev_attr_val);
		if(result < 0)
		{
			printk(KERN_ALERT "Failed to create arrribute val.\n");
			goto device_destroy;
		}

		//申请gpio
		gpio_request(led_dev->led.gpio,led_dev->led.name);

		//将device设备模型中的私有指针指向led_dev,方便在节点属性方法中使用led_dev结构
		dev_set_drvdata(led_dev->dev,led_dev);
	}
	
	//将平台设备数据私有指针指向leds_data,这个函数会调用dev_set_drvdata()函数,方便在remove函数中使用leds_data结构
	platform_set_drvdata(pdev,leds_data);

	return 0;

device_destroy:
	device_destroy(leds_class,0);   //销毁设备

free:
	kfree(leds_data);   //释放内存

class_destroy:
	class_destroy(leds_class);  //销毁leds_class类
	return result;;
}

static int s3c4410_led_remove(struct platform_device *pdev)
{
	char i = 0;
	struct leds_driver_data *leds_data = platform_get_drvdata(pdev);  //得到led驱动私有数据结构
	for(i=0;i<leds_data->leds_num;i++)  //遍历释放内存
	{
		struct led_dev *led_dev;
		led_dev = &leds_data->leds[i];
		gpio_free(led_dev->led.gpio);   //释放gpio
		device_remove_file(led_dev->dev,&dev_attr_val);  //删除属性节点
	}
	device_destroy(leds_class,0);    //销毁device设备,删除leds下的目录
	kfree(leds_data);                //释放内存
	class_destroy(leds_class);       //销毁leds_class类,删除leds目录
	leds_class = NULL;  

	platform_set_drvdata(pdev, NULL);
	return 0;
}

static struct platform_driver platform_led_driver = {
	.probe          = s3c4410_led_probe,
	.remove         = s3c4410_led_remove,
	.driver         = {
		.name           = "gpio-leds",
		.owner          = THIS_MODULE,
	},
};

static struct gpio_led gpio_leds[] = 
{
	{
		.name = "led1",
		.gpio = EXYNOS4_GPK1(1),
	},
	{
		.name = "led2",
		.gpio = EXYNOS4_GPL2(0),
	},
};

static struct gpio_led_platform_data gpio_leds_data = 
{
	.leds = gpio_leds,
	.num_leds = ARRAY_SIZE(gpio_leds),
};

static struct platform_device platform_led_device = 
{
	.name = "gpio-leds",   //必须和platform_driver中的name相同
	.id = -1,
	.dev = {
		.platform_data = &gpio_leds_data,  //平台设被中的led私有数据
	},
};


static int __init led_init(void)
{
	platform_device_register(&platform_led_device);

	return platform_driver_register(&platform_led_driver);
}

static void __exit led_exit(void)
{
	platform_driver_unregister(&platform_led_driver);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Golf/fxb,<1029930509@qq.com>");
MODULE_DESCRIPTION("Led platform device driver");
Makefile

#Kbuild Makefile

#Kbuild的生成规则,编译对象hello_word依赖文件Hello_Word.c文件
#将编译对象hello_word编译成模块
obj-m := led.o
led-objs := leds.o            

#ubuntu使用的内核源代码,build是链接文件

KERNEL_DIR :=/usr/src/golf/android4.4/iTop4412_Kernel_3.0


#当前目录
PWD := $(shell pwd)                               

 #首先执行内核源代码中的TOP Makefile文件,设置内核编译环境,再执行用户定义的Kbuile Makefile文件
all:
	make -C $(KERNEL_DIR) M=$(PWD) modules   

#make clean命令
clean:                                                 
	rm *.o *.ko *.mod.c    

#执行"make clean"会无视"clean"文件存在与否。
.PHONY :clean                                        


4:测试代码

4.1:使用adb push 将模块放在板子中,insmod模块

4.2:在/sys/clas/leds/下会有led1和led2两个目录,目录中有val属性,可以使用cat读取当前led等状态,使用echo "OFF" > val 或者 echo "ON" > val,控制led开关;


5:总结

5.1:代码把移植性放在首位,参考led子系统进行驱动设计,并且使用platform总线,使用标准的gpio操作,以后要使用。只需要修改platform_device即可;

5.2:led的驱动,按照led子系统来移植是最可取的方法,因为内核中已经提供了很全面的机制来控制设备,后面博文分享led子系统的实现;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值