基于S3C2440的linux-3.6.6移植——LED驱动【转】
本文转载自:http://www.voidcn.com/blog/lqxandroid2012/article/p-625005.html
目前的linux版本的许多驱动都是基于设备模型,LED也不例外。
简单地说,设备模型就是系统认为所有的设备都是挂接在总线上的,而要使设备工作,就需要相应的驱动。设备模型会产生一个虚拟的文件系统——sysfs,它给用户提供了一个从用户空间去访问内核设备的方法,它在linux里的路径是/sys。如果要写程序访问sysfs,可以像读写普通文件一样来操作/sys目录下的文件。
对于基于s3c2440的开发板来说,linux-3.6.6自动的LED驱动只需改变连接LED的IO端口,及高、低电平响应即可。我的开发板的四个LED连接在了B口的5到8引脚上,当输出低电平时被点亮,与linux自带的LED驱动一致,因此无需做任何改动。
使用menuconfig来配置内核,这里要加上对LED模块的内容,即:
Device Drivers--->
[*]LED Support--->
<*>LED Class Support
<*>LED Support for Samsung S3C24xx GPIO LEDs
编译内核,并把编译好的内核下载到开发板上,运行:
[root@zhaocj /]#ls
bin etc lib proc sys usr
dev home linuxrc sbin temp
[root@zhaocj /]#cd sys
[root@zhaocj /sys]#ls
block class devices fs module
bus dev firmware kernel power
进入sys目录下,我们看到该目录下有一些子目录。
[root@zhaocj /sys]#cd class
[root@zhaocj class]#ls
backlight hidraw leds rtc vc
bdi hwmon mem sound video_output
block i2c-adapter misc spi_master vtconsole
firmware i2c-dev mmc_host spidev watchdog
gpio input mtd tty
graphics lcd net udc
进入class目录,我们会看到在该目录下有一些设备,其中leds就是本次我们要操作的LED。
[root@zhaocj class]#cd leds
[root@zhaocj leds]#ls
backlight led1 led2 led3 led4
在leds目录下,会看到四个LED的目录,这就是开发板上的四个LED。另外backlight目录是关于LCD的背光,与LED无关。
[root@zhaocj leds]#cd led1
[root@zhaocj led1]#ls
brightness max_brightness subsystem
device power uevent
brightness文件就是LED设备,对其进行操作就可完成对LED的控制。
[root@zhaocj led1]#cat brightness
0
可以看出led1当前的状态是关闭。(0表示关闭,1表示打开)
[root@zhaocj led1]#cat >brightness<<eof
> 1
> eof
#[root@zhaocj led1]#
向brightness写1,表示打开LED。这时led1会被点亮。
当然,我们也可以编写用户程序来控制开发板上的四个LED
/**********************
****leds.c**************
**********************/
#include<stdint.h>
上面的程序只做简单测试之用。编译该文件:
arm-linux-gcc -o leds leds.c
把leds文件下载到temp目录下,运行:
[root@zhaocj /temp]# ./leds led2 on
则点亮led2。
[root@zhaocj /temp]# ./leds led2 off
则关闭led2。
下面我就来简单分析一下linux自带的LED子系统。
在mach-zhaocj2440.c文件,创建了LED设备,如下:
/* LEDS */
static struct s3c24xx_led_platdata zhaocj2440_led1_pdata = {
.name = "led1",
.gpio = S3C2410_GPB(5),
.flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
.def_trigger = "heartbeat",
};
static struct s3c24xx_led_platdata zhaocj2440_led2_pdata = {
.name = "led2",
.gpio = S3C2410_GPB(6),
.flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
.def_trigger = "nand-disk",
};
static struct s3c24xx_led_platdata zhaocj2440_led3_pdata = {
.name = "led3",
.gpio = S3C2410_GPB(7),
.flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
.def_trigger = "mmc0",
};
static struct s3c24xx_led_platdata zhaocj2440_led4_pdata = {
.name = "led4",
.gpio = S3C2410_GPB(8),
.flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
.def_trigger = "",
};
定义了四个LED数据,名字分别为led1~led4,这就是我们在leds目录下看到这四个子目录。它们所连接的引脚分别为B口的5~8,这是由S3C2410_GPB()宏定义完成的。标识S3C24XX_LEDF_ACTLOW表示的是低电平有效,S3C24XX_LEDF_TRISTATE表示的三态无效。另外def_trigger表示的是触发控制,如我们对nand进行读写操作时,led2会不停的闪,在这里我们没有用到这个功能,暂时不用理会。
static struct platform_device zhaocj2440_led1= {
.name = "s3c24xx_led",
.id = 1,
.dev = {
.platform_data = &zhaocj2440_led1_pdata,
},
};
static struct platform_device zhaocj2440_led2= {
.name = "s3c24xx_led",
.id = 2,
.dev = {
.platform_data = &zhaocj2440_led2_pdata,
},
};
static struct platform_device zhaocj2440_led3= {
.name = "s3c24xx_led",
.id = 3,
.dev = {
.platform_data = &zhaocj2440_led3_pdata,
},
};
static struct platform_device zhaocj2440_led4= {
.name = "s3c24xx_led",
.id = 4,
.dev = {
.platform_data = &zhaocj2440_led4_pdata,
},
};
上面则创建了总线平台设备,四个LED的设备名称都是s3c24xx_led,子设备id分别从1到4,设备数据则是上面定义的四个LED数据。然后把这四个LED设备再添加到开发板的设备数组中,即:
static struct platform_device *zhaocj2440_devices[]__initdata = {
……
&zhaocj2440_led1,
&zhaocj2440_led2,
&zhaocj2440_led3,
&zhaocj2440_led4,
……
};
最后,在开发板系统初始化过程中,再把设备数组中的设备逐一注册到系统总线上,即:
static void __init zhaocj2440_init(void)
{
……
platform_add_devices(zhaocj2440_devices,ARRAY_SIZE(zhaocj2440_devices));
……
}
这样就完成了LED设备的创建。
光有设备还不能工作,任何一个设备的运行还需要与之相对应的驱动。对于基于s3c24xx的LED来说,它的驱动是在drivers/leds目录下Leds-s3c24xx.c文件内创建的,即:
static struct platform_driver s3c24xx_led_driver = {
.probe = s3c24xx_led_probe,
.remove = s3c24xx_led_remove,
.driver = {
.name = "s3c24xx_led",
.owner = THIS_MODULE,
},
};
设备和驱动是如何匹配的呢?即设备如何找到它所对应的驱动的呢?靠的就是name。我们会发现platform_device和platform_driver都有元素name,它们的内容如果一致,设备和驱动就会配对成功。对于LED来说,它们的name都是s3c24xx_led。当设备和驱动匹配上以后,就要运行probe所指定的函数,简单地说,它就是完成一些初始化工作。当需要移除设备时,就需要运行remove所指定的函数,它完成的任务是注销设备。对于支持热插拔的设备来说,尤为重要。
现在就来说一下s3c24xx_led_probe函数:
static int s3c24xx_led_probe(struct platform_device *dev)
{
structs3c24xx_led_platdata*pdata = dev->dev.platform_data;
structs3c24xx_gpio_led *led;
intret;
/*用于给LED分配内存空间*/
led =devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),
GFP_KERNEL);
if(led == NULL) {
dev_err(&dev->dev,"No memory for device\n");
return-ENOMEM;
}
/*保存LED设备结构*/
platform_set_drvdata(dev,led);
/*给LED结构体赋值,其中s3c24xx_led_set就是具体负责操作LED的函数*/
led->cdev.brightness_set= s3c24xx_led_set;
led->cdev.default_trigger= pdata->def_trigger;
led->cdev.name= pdata->name;
led->cdev.flags|= LED_CORE_SUSPENDRESUME;
led->pdata = pdata;
/*为LED分配io引脚*/
ret =devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED");
if(ret < 0)
returnret;
/*no point in having a pull-up if we are always driving */
/*无需上拉*/
s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE);
/*设置io引脚为输入*/
if(pdata->flags & S3C24XX_LEDF_TRISTATE)
gpio_direction_input(pdata->gpio);
else
gpio_direction_output(pdata->gpio,
pdata->flags& S3C24XX_LEDF_ACTLOW ? 1 :0);
/*register our new led device */
/*注册一个新的LED设备类对象
该函数是在drivers/leds目录下的Led-class.c文件内定义的*/
ret= led_classdev_register(&dev->dev, &led->cdev);
if(ret < 0)
dev_err(&dev->dev,"led_classdev_register failed\n");
return ret;
}
从以上分析可以看出,s3c24xx_led_probe函数主要就是完成LED设备的一些初始化工作。而负责开、关LED任务的是s3c24xx_led_set函数,在该函数内,gpio_set_value(pd->gpio, state);是具体完成为相应引脚置1或清零的任务。
drivers/leds目录下的Led-class.c文件是LED子系统的底层核心文件,它主要负责创建LED类,以及创建设备节点,上面提到的led_classdev_register函数就是在这个文件中定义的。为了更好的理解LED子系统,我们再简单分析一下该文件。
在子系统初始化时,会调用leds_init函数,它的第一段代码:
leds_class = class_create(THIS_MODULE,"leds");
就是创建leds类,也就是我们在sys/class目录下看到的leds目录。另外
leds_class->dev_attrs = led_class_attrs;
是赋予该类的属性。那么我们再来看看led_class_attrs结构的第一句代码:
__ATTR(brightness, 0644, led_brightness_show,led_brightness_store)
其中brightness就是我们对LED具体操作的设备文件名,0644是该文件的权限,led_brightness_show是读文件所调用的函数,led_brightness_store是写文件所调用的函数。读文件也就是读取LED的状态(是关还是开),写文件也就是完成打开LED或关闭LED操作。
最后再分析一下前面提到的led_classdev_register函数。在该函数内首先利用device_create函数创建设备节点,也就是在leds目录下,生成led1~led4这四个目录。另一项重要的任务就是把设备节点添加到leds的链表中。
对linux自带的LED子系统的分析就到这里。我想只要理解了该子系统,那么自己完全可以写出关于GPIO读写操作的任何驱动程序来。
基于S3C2440的linux-3.6.6移植——LED驱动【转】的更多相关文章
- 基于s5pv210嵌入式linux系统sqlite3数据库移植
基于s5pv210嵌入式linux系统sqlite3数据库移植 1.下载源码 http://www.sqlite.org/download.html 最新源码为3080100 2.解压 tar xvf ...
- 芯灵思Sinlinx A64 linux 通过设备树写LED驱动(附参考代码,未测试)
开发平台 芯灵思Sinlinx A64 内存: 1GB 存储: 4GB 详细参数 https://m.tb.cn/h.3wMaSKm 开发板交流群 641395230 全志A64设备树结构体 #inc ...
- 基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
本文将介绍看门狗驱动的实现. 目标平台:TQ2440 CPU:s3c2440 内核版本:2.6.30 1. 看门狗概述 看门狗其实就是一个定时器,当该定时器溢出前必须对看门狗进行"喂狗“,如 ...
- 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九)
作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...
- 基于tiny4412的Linux内核移植 -- PWM子系统学习(八)
作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...
- 基于嵌入式Linux的千兆以太网卡驱动程序设计及测试
一. 引言 千兆以太网是一种具有高带宽和高响应的新网络技术,相关协议遵循IEEE 802.3规范标准.采用和10M以太网相似的帧格式.网络协议和布线系统,基于光纤和短距离同轴电缆的物理层介质,更适用于 ...
- 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接
<Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...
- 基于sys文件系统的LED驱动的移植【原创】
基于RK3188平台LED驱动程序的移植的移植.如有不正确之处,欢迎大家指点. 本文的LED驱动程序不是通过打开设备节点来访问和控制LED的,是通过sys文件系统来控制LED. 板子上有四盏灯以及对应 ...
- 基于335X平台Linux交换芯片驱动开发
基于335X平台Linux交换芯片驱动开发 一.软硬件平台资料 1.开发板:创龙AM3359核心板,网口采用RMII形式. 2.Kernel版本:4.4.12,采用FDT 3.交换芯片MARVEL ...
随机推荐
- Codeforces 835F Roads in the Kingdom (环套树 + DP)
题目链接 Roads in the Kingdom 题意 给出一个环套树的结构,现在要删去这个结构中的一条边,满足所有点依然连通. 删边之后的这个结构是一棵树,求所有删边情况中树的直径的最小值. 显 ...
- Codeforces Round #317 [AimFund Thanks-Round] (Div. 2) Array 模拟
题目链接:http://codeforces.com/contest/572/problem/A 题意 就给你两个数组,问你能不能从A数组中取出k个,B数组中取出m个,使得这k个都大于这m个. 题解 ...
- ios- nil NULL 和 NSNull
因为objective-c的集合对象,比如nsarray, nsdictionary, nsset等,都有可能包含nsnull对象,所以,如果以下代码中的item为nsnull,则会引起程序崩溃. N ...
- Android 中状态栏、标题栏、View的大小及区分
1.获得状态栏的高度(状态栏相对Window的位置): Rect frame = new Rect(); getWindow().getDecorView().getWindowVisibleDisp ...
- 低成本安全硬件(二):RFID on PN532
引言 鉴于硬件安全对于大多数新人是较少接触的,而这方面又非常吸引我,但是部分专业安全研究设备较高的价格使人望而却步.在该系列中,笔者希望对此感兴趣的读者在花费较少金钱的情况下体会到硬件安全的魅力所在. ...
- tomcat7设置usernamepassword
因为tomcat是绿色版.今天想在网页上管理项目,却发现没实username和password.打开tomcat-users.xml文件全都是凝视.如图: 将例如以下代码拷贝到tomcat-users ...
- ARC forbids Objective-C objects in structs or unions
解决方法有二种: 1.在出错的地方加入__unsafe_unretained 2.关闭系统ARC.1.点击project 2.点击Build Setting 3.找到其以下的Objetive ...
- sql的一些知识_数据分组
group by--------按**分组查询 SELECT rank, MIN(weight)as min_weight FROM userinfo GROUP BY rank 对分组的值进行过滤需 ...
- 关于Oracle中sysoper这个系统权限的问题
我们都知道Oracle数据库安装完之后.默认的会有这样几个系统角色或权限.nomal,sysdba,sysoper等等,之前每次登录Oracle的时候.都是直接以conn / as sysdba 的身 ...
- VC++的窗口句柄和窗口ID
原文地址:VC++的窗口句柄和窗口ID作者:放放 句柄是窗口资源的标识,它标识资源在系统中所占用的内存块,应用程序通过窗口句柄对窗口进行操作.除了窗口句柄之外,任何一种资源都有它自己的句柄,比如光标句 ...