如何移植linux的windows gpio接口编程

EmbeddedSystem开发笔记(2)
为了避免编写Linux设备驱动操作底层硬件设备的GPIO接口,本文提供了采用/dev/mem设备和mmap函数将GPIO物理地址映射到用户层进行操作的方式,通过在S3C2416和Linux 2.6.39.3系统上测试和工程实践应用表明该方案提供了一种简单可靠的用户层操作物理硬件设备的方式,具有较好的工程应用价值。
最近在S3C2416的嵌入式Linux设备上开发一个智慧泊车相关的设备,其中需要操作底层硬件设备的一个GPIO接口,由于只需要操作一个GPIO接口,因此选择了采用/dev/mem设备和mmap函数在用户层操作GPIO接口的方法,在实现过程中因为没有很好的理解mmap的offset参数的页对齐机制,浪费了大概半天调试时间,因此留下记录。本文编写了在用户层操作GPIO的接口函数,为用户提供了采用访问物理内存的方法访问底层设备硬件的方法。
mmap函数声明如下:
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
addr:指定文件或其他对象被映射到进程空间中的地址,一般设置为NULL,将选择地址的任务留给内核完成;
len:指定映射的进程控件的长度,一般设置为系统的页大小,原因是offset:只能是页地址对齐的地址,为了访问整个页地址内的偏移,因此建议设置为系统页大小,后文提供的接口文件中使用4096的页大小;
prot:指定共享内存的访问权限,可以为PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问);
flags:指定对象的共享方式,可选的值为MAP_SHARED, MAP_PRIVATE, MAP_FIXED;
fd:映射到内存空间的文件描述符,对于设备文件一般为打开/dev/mem文件的返回的文件描述符;
offset:按页面大小对齐的页地址;
项目中使用的时S3C2416和Linux 2.6.39.3系统,系统的页面大小为4096字节,因此提供的GPIO操作函数接口代码如下。
gpio_ctrl.h文件
#ifndef __GPIO_CTRL_H__
#define __GPIO_CTRL_H__
#include &stdio.h&
#include &stdlib.h&
#include &sys/mman.h&
#include &fcntl.h&
#include &unistd.h&
#define REG(addr)
(*(volatile unsigned int *)(addr))
typedef enum _gpio_mode_t
GPIO_INPUT = 0x00,
GPIO_OUTPUT = 0x01,
GPIO_EINT = 0x02,
GPIO_RESERVED = 0x03,
}gpio_mode_t;
typedef enum _gpio_state_t
GPIO_STATE_LOW = 0x00,
GPIO_STATE_HIGH = 0x01,
}gpio_state_t;
typedef enum _gpio_pull_t
GPIO_PULL_DISABLE = 0x00,
GPIO_PULL_DOWN = 0x01,
GPIO_PULL_UP = 0x02,
GPIO_PULL_NOT_AVAILABLE = 0x03,
}gpio_pull_t;
typedef enum _gpio_pin_t
GPIO_PIN_0 = 0,
GPIO_PIN_1 = 1,
GPIO_PIN_2 = 2,
GPIO_PIN_3 = 3,
GPIO_PIN_4 = 4,
GPIO_PIN_5 = 5,
GPIO_PIN_6 = 6,
GPIO_PIN_7 = 7,
}gpio_pin_t;
void gpio_clock_init();
void gpio_set_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset, gpio_mode_t tMode);
gpio_mode_t gpio_get_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset);
void gpio_set_pin_state(void * pRegAddr, gpio_pin_t nPinOffset, gpio_state_t tState);
gpio_state_t gpio_get_pin_state(void * pRegAddr, gpio_pin_t nPinOffset);
void gpio_set_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset, gpio_pull_t tPull);
gpio_pull_t gpio_get_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset);
gpio_ctrl.c文件
#include "gpio_ctrl.h"
#define PAGE_SHIFT
#define PAGE_SIZE
(1 && PAGE_SHIFT)
#define PAGE_MASK
(~(PAGE_SIZE - 1))
#define PAGE_OFFSET_MASK (PAGE_SIZE - 1)
#define MMAP_LENGTH PAGE_SIZE
void gpio_clock_init()
void gpio_set_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset, gpio_mode_t tMode)
int nFd = -1;
void * pGpioConAddr = NULL;
unsigned int nGpioConVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioConAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioConAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioConAddr == NULL || pGpioConAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioConVal = REG(pGpioConAddr);
nGpioConVal &= ~(3 && nShiftBits);
nGpioConVal |= ((unsigned int)tMode) && nShiftB
REG((unsigned int)pGpioConAddr) = nGpioConV
nGpioConVal = REG(pGpioConAddr);
if(pGpioConAddr)
msync(pGpioConAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioConAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
gpio_mode_t gpio_get_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset)
int nFd = -1;
void * pGpioConAddr = NULL;
unsigned int nGpioConVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioConAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioConAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioConAddr == NULL || pGpioConAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioConVal = REG(pGpioConAddr);
nGpioConVal &= (3 && nShiftBits);
nGpioConVal &&= nShiftB
if(pGpioConAddr)
munmap(pGpioConAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
return nGpioConV
void gpio_set_pin_state(void * pRegAddr, gpio_pin_t nPinOffset, gpio_state_t tState)
int nFd = -1;
void * pGpioStateAddr = NULL;
unsigned int nGpioStateVal = 0;
unsigned int nShiftBits = (unsigned int)nPinO
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioStateAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioStateAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioStateAddr == NULL || pGpioStateAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioStateVal = REG(pGpioStateAddr);
nGpioStateVal &= ~(1 && nShiftBits);
nGpioStateVal |= ((unsigned int)tState) && nShiftB
REG((unsigned int)pGpioStateAddr) = nGpioStateV
if(pGpioStateAddr)
msync(pGpioStateAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioStateAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
gpio_state_t gpio_get_pin_state(void * pRegAddr, gpio_pin_t nPinOffset)
int nFd = -1;
void * pGpioStateAddr = NULL;
unsigned int nGpioStateVal = 0;
unsigned int nShiftBits = (unsigned int)nPinO
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioStateAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioStateAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioStateAddr == NULL || pGpioStateAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioStateVal = REG(pGpioStateAddr);
nGpioStateVal &= (1 && nShiftBits);
nGpioStateVal &&= nShiftB
if(pGpioStateAddr)
munmap(pGpioStateAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
return nGpioStateV
void gpio_set_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset, gpio_pull_t tPull)
int nFd = -1;
void * pGpioPullAddr = NULL;
unsigned int nGpioPullVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioPullAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioPullAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioPullAddr == NULL || pGpioPullAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioPullVal = REG(pGpioPullAddr);
nGpioPullVal &= ~(3 && nShiftBits);
nGpioPullVal |= ((unsigned int)tPull) && nShiftB
REG((unsigned int)pGpioPullAddr) = nGpioPullV
if(pGpioPullAddr)
msync(pGpioPullAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioPullAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
gpio_pull_t gpio_get_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset)
int nFd = -1;
void * pGpioPullAddr = NULL;
unsigned int nGpioPullVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd & 0)
perror("open /dev/mem device failed.\n");
pGpioPullAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioPullAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioPullAddr == NULL || pGpioPullAddr == MAP_FAILED)
perror("gpio mmap failed.\n");
nGpioPullVal = REG(pGpioPullAddr);
nGpioPullVal &= (3 && nShiftBits);
nGpioPullVal &&= nShiftB
if(pGpioPullAddr)
msync(pGpioPullAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioPullAddr, MMAP_LENGTH);
if(nFd & 0)
close(nFd);
return nGpioPullV
工程中使用GPG4对上文中提供的GPIO接口函数进行了测试,测试代码
main.c文件
#include &stdio.h&
#include "gpio_ctrl.h"
int main(int argc, char * * argv, char * * env)
gpio_state_t nGPIOS
gpio_mode_t nGPIOM
gpio_pull_t nGPIOP
unsigned char
gpio_set_pin_mode((void *)(GPGCON_ADDR), GPIO_PIN_4, GPIO_INPUT);
nGPIOMode = gpio_get_pin_mode((void *)(GPGCON_ADDR), GPIO_PIN_4);
printf("%s,L%d, in %s, current gpio mode is:nGPIOMode=%d.\n", __FILE__, __LINE__, __func__, nGPIOMode);
gpio_set_pin_pull((void *)(GPGUDP_ADDR), GPIO_PIN_4, GPIO_PULL_UP);
nGPIOPull = gpio_get_pin_pull((void *)(GPGUDP_ADDR), GPIO_PIN_4);
printf("%s,L%d, in %s, current gpio pull is:nGPIOPull=%d.\n", __FILE__, __LINE__, __func__, nGPIOPull);
nGPIOState = gpio_get_pin_state((void *)(GPGDAT_ADDR), GPIO_PIN_4);
printf("%s,L%d,in %s, current GPG_4 pin state=%d.\n", __FILE__, __LINE__, __func__, (int)(nGPIOState));
usleep(1000000);
工程的Makefile文件如下:
CROSS=arm-linux-
CC=$(CROSS)gcc
FLAGS=-lpthread -wall
objects = main.o gpio_ctrl.o
gpio_test:$(objects)
$(CC) -o gpio_test $(objects)
-f gpio_test *.o
本文提供的pudn工程源码链接如下:
本文提供了采用/dev/mem和mmap方式在用户态操作物理层资源和对象的方式,与采用编写驱动方式操作GPIO相比简化了物理层资源访问流程,提高了工程研发效率,本文为嵌入式系统开发人员在应用层访问物理层资源对象提供了较好的参考。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4680次
排名:千里之外
原创:42篇
(2)(1)(2)(16)(8)(2)(5)(1)(4)(5)君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
ARM Linux下访问GPIO端口
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口使用GPIO,当然可以自己编写驱动,比如之前的第34节,也可以使用Kernel中的platform方法,它对资源的管理更加方便。为了理解platform工作的过程,需要先来看看mainline里面的帮助文档:
Documentation/driver-model/platform.txt
====================================
(头文件linux/platform_device.h里面包含了platform的driver model interface,比较重要的结构体是platform_device和platform_driver。platform其实是一种pseudo-bus,而并不是真正的总线,使用它能够实现connect devices on busses with minimal infrastructure。区别于PCI或者USB总线,platform更见简单。)
Platform device
~~~~~~~~~~~~~~~~
Platform devices are devices that typically apear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is directly addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of so but its registers will still be directly addressable.
(platform device的特点是什么?它们都可以从CPU BUS上直接寻址,比如GPIO、UART、I2C、SPI等等外设,它们的寄存器可以通过对应地址来读取。因此也可以知道,platform与硬件关系密切。)
Platform devices are given a name, used in driver binding, and a list of resources such as addresses and IRQs.
struct platform_device {
&&&&&&& const char&&&&& *
&&&&&&& u32&&&&&&&&&&&&
&&&&&&& struct device&&
&&&&&&& u32&&&&&&&&&&&& num_
&&&&&&& struct resource *
(来看一个platform_device的例子(arch/arm/mach-omap2/board-omap3stalker.c):)
(基本上platform_device都定义在了arch目录下,这是其中有关GPIO的一例,它一共定义了4个用作LED的GPIO,存放在gpio_leds中:)
static struct gpio_led_platform_data gpio_led_info = {
&&&&&&& .leds&&&&&&&&&& = gpio_leds,
&&&&&&& .num_leds&&&&&& = ARRAY_SIZE(gpio_leds),
static struct platform_device leds_gpio = {
&&&&&&& .name&& = &leds-gpio&,
&&&&&&& .id&&&& = -1,
&&&&&&& .dev&&& = {
&&&&&&&&&&&&&&& .platform_data& = &gpio_led_info,
&&&&&&& },
Platform drivers
~~~~~~~~~~~~~~~~
Platform drivers follow the standard driver model conventiaon, where discovery/enumeration is handled outside the drivers, and drivers provide probe() and remove() methods. They support power management and shutdown notifications using the standard conventions.
struct platform_driver {
&&&&&&& int (*probe)(struct platform_device *);
&&&&&&& int (*remove)(struct platform_device *);
&&&&&&& void (*shutdown)(struct platform_device *);
&&&&&&& int (*suspend)(struct platform_device *, pm_message_t state);
&&&&&&& int (*suspend_late)(struct platform_device *, pm_message_t state);
&&&&&&& int (*resume_early)(struct platform_device *);
&&&&&&& int (*resume)(struct platform_device *);
&&&&&&& struct device_
(再来看一个platform_driver的例子(drivers/leds/leds-gpio.c):)
(它对probe、remove,以及driver赋值:)
static struct platform_driver gpio_led_driver = {
&&&&&&& .probe&&&&&&&&& = gpio_led_probe,
&&&&&&& .remove&&&&&&&& = gpio_led_remove,
&&&&&&& .driver&&&&&&&& = {
&&&&&&&&&&&&&&& .name&& = &leds-gpio&,
&&&&&&&&&&&&&&& .owner& = THIS_MODULE,
&&&&&&&&&&&&&&& .of_match_table = of_match_ptr(of_gpio_leds_match),
&&&&&&& },
Note that probe() should in general verify that the specified device har sometimes platform setup code can't be sure. The probing can use device resources, including clocks, and device platform_data.
Platform drivers register themselves the normal way:
&&&&&&& int platform_driver_register(struct platform_driver *drv);
(注册了platform设备之后,等待触发事件发生就好了,它会自动执行probe(),在dts文件中有触发事件的定义。至于触发事件怎么和驱动联系起来,在以后的驱动编写中再学,因此GPIO并不是热插拔设备,这里直接初始化顺带probe就行。)
Or, in common situations where the device is known not to be hot-pluggable, the probe() routine can live in an init section to reduce the driver's runtime memory footprint:
&&&&&&& int platform_driver_probe(struct platform_driver *drv,
&&&&&&&&&&&&&&&&&&&&&&&&& int (*probe)(struct platform_device *))
Device Enumeration
~~~~~~~~~~~~~~~~~~
As a rule, platform specific (and often board-specific) setup code will register platform devices:
&&&&&&& int platform_device_register(struct platform_device *pdev);
&&&&&&& int platform_add_devices(struct platform_device **pdevs, int ndev);
The general rule is to register only those devices that actually exist, but in some cases extra devices might be registered. For example, a kernel might be configured to work with an external network adapter that might not be pupulated on all boards, or likewise to work with an integrated controller that some boards might not hook up to any peripherals.
In some cases, boot firmware will export tables describing the devices that are populated on a given board. Without such tables, often the only way for system setup code to set up the correct devices is to build a kernel for a specific target board. Such board-specific kernels are common with embedded and custom systems development.
In many cases, the memory and IRQ resources associated with the platform device are not enough to let the device's driver work. Board setup code will often provide additional information using the device's platform_data field to hold additional information.
(所以,虽然driver目录下删掉了各种各样乱七八糟的board,但并不是说board这个概念就在内核中消失了,因为普遍适用的外设并不能完全搞定某一特殊的board,所以在arch/arm/mach-omap2目录下仍然有各种各样的board文件,它存放了有关platform的内容。)
Embedded systems frequently need one or more clocks for platform devices, which are normally kept off until they'are actively needed (to save power). System setup also associates those clocks with the device, so that that calls to clk_get(&pdev-&dev, clock_name) return them as needed.
(嵌入式设备为了降低功率,通常会关掉不使用设备的时钟,时钟也是作为设备来管理的,因此可以使用clk_get(&pdev-&dev, clock_name)来获得其它设备所需要的时钟。)
Legacy Drivers: Device Probing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Device Naming and Driver Binding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The platform_device.dev.bus_id is the canonical name for the devices.
It's built from two components:
&* platform_device.name ... which is also used to for driver matching.
&* platform_device.id ... the device instance number, or else &-1& to indicate there's only one.
There are concatenated, so name/id &serial/0& indacates bus_id &serial.0&, and &serial&/3 indicates bus_id &serial.3&; both would use the platform_driver named &serial&. While &my_rtc&/-1 would be bus_id &my_rtc& (no instance is) and use the platform_driver called &my_rtc&.
(这就是为什么platform_device和platform_driver的名字是一样的,它们不是为了便于用户理解,而是必须设置成一样的:&leds-gpio&,否则platform设备是找不到对应的驱动的。)
Early Platform Devices and Drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For further informatin, see linux/platform/platform_device.h.
========================================================================
前面螺里啰嗦的写了那么多,主要是了解platform的概念,现在再来看看Kernel里面的代码:
====================================
首先是arch/arm/boot/dts/maria-am335x-common.dtsi文件,之前在阅读booting-without-of.txt时了解到,dtb和driver产生联系的关键在于compatibal参数。maria-am335x-common.dtsi文件中有关led的部分,关键的compatible参数是这么设置的:
&&&&&&& leds {
&&&&&&&&&&&&&&& pinctrl-names = &default&;
&&&&&&&&&&&&&&& pinctrl-0 = &&core_led&;
&&&&&&&&&&&&&&& compatible = &gpio-leds&;
&&&&&&&&&&&&&&& led@1 {
&&&&&&&&&&&&&&&&&&&&&&& label = &maria-am335x:core&;
&&&&&&&&&&&&&&&&&&&&&&& gpios = &&gpio3 8 GPIO_ACTIVE_HIGH&;
&&&&&&&&&&&&&&&&&&&&&&& default-state = &on&;
&&&&&&&&&&&&&&& };
&&&&&&& };
所以第二步是找到gpio-leds的驱动文件,使用grep命令就行:
grep -rn gpio-leds drivers/
此时就会发现包含这个compatible参数的驱动文件为:
drivers/leds/leds-gpio.c
在理解drivers/leds/leds-gpio.c之前,先来看看Makefile和Kconfig文件。
在Makefile中:
obj-$(CONFIG_LEDS_GPIO)&&&&&&&&&&&&&&&& += leds-gpio.o
在Kconfig中:
config LEDS_GPIO
&&&&&&& tristate &LED Support for GPIO connected LEDs&
&&&&&&& depends on LEDS_CLASS
&&&&&&& depends on GPIOLIB
&&&&&&& help
&&&&&&&&& This option enables support for the LEDs connected to GPIO
&&&&&&&&& outputs. To be useful the particular board must have LEDs
&&&&&&&&& and they must be connected to the GPIO lines.& The LEDs must be
&&&&&&&&& defined as platform devices and/or OpenFirmware platform devices.
&&&&&&&&& The code to use these bindings can be selected below.
因此在make menuconfig时可以找到对应的选项:
Device Drivers -& LED Support -& LED Support for GPIO connected LEDS
可以看到它默认是*号,意为已经编译进了内核。
来看看下面的代码:
static const struct of_device_id of_gpio_leds_match[] = {
&&&&&&& { .compatible = &gpio-leds&, },
&&&&&&& {},
static struct platform_driver gpio_led_driver = {
&&&&&&& .probe&&&&&&&&& = gpio_led_probe,
&&&&&&& .remove&&&&&&&& = gpio_led_remove,
&&&&&&& .driver&&&&&&&& = {
&&&&&&&&&&&&&&& .name&& = &leds-gpio&,
&&&&&&&&&&&&&&& .owner& = THIS_MODULE,
&&&&&&&&&&&&&&& .of_match_table = of_match_ptr(of_gpio_leds_match),
&&&&&&& },
module_platform_driver(gpio_led_driver);
of_match_ptr函数将设备of_gpio_leds_match与驱动gpio_led_driver联系起来了。
module_platform_driver用来替代module_init()和module_exit()功能,为了理解它到底执行了什么功能,这里在上根据identifier search来一步一步观察:
http://lxr./source/include/linux/platform_device.h#L219
==================================
219 #define module_platform_driver(__platform_driver) \
220&&&&&&&& module_driver(__platform_driver, platform_driver_register, \
221&&&&&&&&&&&&&&&&&&&&&&&& platform_driver_unregister)
从这个宏可以知道,module_platform_driver被定义为module_driver,它以struct platform_drive为参数,去执行platform_driver_register和platform_driver_unregister这两个函数。
http://lxr./source/include/linux/device.h#L1219
==================================
1219 #define module_driver(__driver, __register, __unregister, ...) \
1220 static int __init __driver##_init(void) \
1222&&&&&&&& return __register(&(__driver) , ##__VA_ARGS__); \
1224 module_init(__driver##_init); \
1225 static void __exit __driver##_exit(void) \
1227&&&&&&&& __unregister(&(__driver) , ##__VA_ARGS__); \
1229 module_exit(__driver##_exit);
观察module_driver宏,是不是发现它非常眼熟呢?它定义了模块装载函数和模块移除函数。##用来作字符串连接,__VA_ARGS_则用来替代变参。通过在drivers/leds/leds-gpio.c中的函数:
static int gpio_led_probe(struct platform_device *pdev)
添加打印信息,就会发现它已经被执行了:
[&&& 2.060000.mmc supply vmmc not found, using dummy regulator
[&&& 2.221323] omap_hsmmc .mmc: could not set regulator OCR (-22)
[&&& 2.231593] omap_hsmmc .mmc: could not set regulator OCR (-22)
[&&& 2.275880] Maria: ====================================
[&&& 2.284377] ledtrig-cpu: registered to indicate activity on CPUs
[&&& 2.292102] usbcore: registered new interface driver usbhid
[&&& 2.298125] usbhid: USB HID core driver
帮助文件也看了,代码也观察了,platform的概念和示例都有了了解,那它和普通的driver有什么区别呢?
========================================================================
/questions//what-is-the-difference-between-platform-driver-and-normal-device-driver
1. Platform devices are inherently not discoverable, i.e. the hardware cannot say &Hey! I'm present!& to the software.
2. Platform devices are bound to drivers by matching names.
3. Platform devices should be registered very early during system boot. Because they are often critical to the rest of the system (platform) and its drivers.
So basically, the question &is it a platform device or a standard device?& is more a question of which bus it uses. To work with a particular platform device, you have to:
1. register a platform driver that will manage this device. It should define a unique name.
2. register your platform device, defining the same name as driver.
终于到了它的应用阶段:
========================================================================
make ARCH=arm CROSS_COMPILE=/opt/gcc-linaro-arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j8 menuconfig
System Type -& TI OMAP/AM/DM/DRA Family -& TI AM33XX
原来是所有的CPU类型都选择了,现在只保留AM33XX,重新编译就会发现,arch/arm/mach-omap2这个目录下会少很多.o文件。观察board-*文件,发现此时只有board-generic.c被编译出来。
去掉board-generic.c有关其他CPU架构的内容,保留CONFIG_SOC_AM33XX架构的内容,此时会发现这个文件剩下的内容非常少。
还记得dts的内容吗?实际上board-generic.c里面是没有platform_device这个结构体的,因为它不需要,有下面的这个dts就好了,leds-gpio.c会根据dts的内容创建platform_device。
compatible = &gpio-leds&;
&&&&&&& label = &maria-am335x:core&;
&&&&&&& gpios = &&gpio3 8 GPIO_ACTIVE_HIGH&;
&&&&&&& default-state = &on&;
在下面这个函数里面添加调试代码:
static int gpio_led_probe(struct platform_device *pdev)
添加调试代码的工作就不一一细述了,总之就是各种printk,这里添来那里添,每个函数每个步骤都来添,直到大概明白这个函数里面发生了什么事情。膜拜下万能的prink~~
[&&& 2.288198] Maria: ====================================
[&&& 2.293747] ====& gpio_leds_create_of enter
[&&& 2.298299] ====& gpio_leds_create_of start
[&&& 2.302743] ====& count = 1
[&&& 2.305838] ====& for_each_available_child_of_node
[&&& 2.311158] ====& state = on
[&&& 2.314210] ====& label = maria-am335x:core
[&&& 2.318698] ====& gpio = 104
[&&& 2.321751] ====& trigger = (null)
[&&& 2.325356] ====& create_gpio_led enter
[&&& 2.329469] ====& create_gpio_led
[&&& 2.332980] ====& devm_gpio_request
[&&& 2.336880] ====& name = maria-am335x:core
[&&& 2.341229] ====& gpio_direction_output
[&&& 2.345374] ====& INIT_WORK
[&&& 2.348407] ====& led_classdev_register enter maria-am335x:core
[&&& 2.356411] ====& Registered led device maria-am335x:core ****
[&&& 2.362688] ====& platform_set_drvdata enter
[&&& 2.367275] &&&&&&==========================
gpio_led_set函数用来执行GPIO操作:
led_dat-&cdev.brightness_set = gpio_led_
那schedule_work什么时候会被执行呢?当下面这个变量被设置的时候:
led_dat-&cdev.brightness_set;
所以将Kernel重新编译后启动:
root@ok335x:~# cd /sys/devices/leds/leds/maria-am335x\:core/
root@ok335x:/sys/devices/leds/leds/maria-am335x:core# echo 1 && brightness
root@ok335x:/sys/devices/leds/leds/maria-am335x:core# echo 0 && brightness
核心板上面的LED灯就会随着echo进去的值亮灭啦!
好吧,其实这里对platform设备的理解还不够深刻,除了echo之外也不知道其他的使用方式。但是这些内容还是留到以后正式学习platform的时候再看吧,这里只需要实现基本的操作,以及搞明白为什么打开第(34)节的模块时GPIO会busy,因此系统已经把这个GPIO作为platform设备啦~
下一节的内容是Ethernet。
旗下网站:
与非门科技(北京)有限公司 All Rights Reserved.
京ICP证:070212号
北京市公安局备案编号: 京ICP备:号

我要回帖

更多关于 gpio接口和串口的区别 的文章

 

随机推荐