我们今天的目标是将itop4412开发板上的两个led灯控制起来。先看一下原理图:
– include/linux/gpio.h
主要提供一些GPIO操作函数:
– arch/arm/plat-samsung/include/plat/gpio-cfg.h
– 包括三星所有处理器的配置函数
– arch/arm/mach-exynos/include/mach/gpio.h
– GPIO管脚拉高拉低配置参数等等
– 配置参数的宏定义应该在arch/arm/plat-samsung/include/plat/gpio-cfg.h文件中
– arch/arm/mach-exynos/include/mach/gpio-exynos4.h
– 包括4412处理器所有的GPIO的宏定义
• linuxGPIO申请函数和赋值函数
– gpio_request
– gpio_set_value
• 三星平台配置GPIO函数
– s3c_gpio_cfgpin
• GPIO配置输出模式的宏变量
– S3C_GPIO_OUTPUT
#include <linux/init.h> #include <linux/module.h> /*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/ #include <linux/platform_device.h> /*注册杂项设备头文件*/ #include <linux/miscdevice.h> /*注册设备节点的文件结构体*/ #include <linux/fs.h> /*Linux中申请GPIO的头文件*/ #include <linux/gpio.h> /*三星平台的GPIO配置函数头文件*/ /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/ #include <plat/gpio-cfg.h> #include <mach/gpio.h> /*三星平台4412平台,GPIO宏定义头文件*/ #include <mach/gpio-exynos4.h> #define DRIVER_NAME "itop4412_led_ctl" #define DEVICE_NAME "itop4412_led_ctl" #define LED_NUM 2 MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("TOPEET"); static int led_gpios[LED_NUM] = { EXYNOS4_GPL2(0), EXYNOS4_GPK1(1), }; static long itop4412_led_ioctl( struct file *files, unsigned int cmd, unsigned long arg){ printk("cmd is %d,arg is %d\n", cmd, arg); if(cmd > 1){ printk("cmd is 0 or 1\n"); } if(arg >= LED_NUM){ printk("arg is < %d\n",LED_NUM); } gpio_set_value(led_gpios[arg],cmd); return 0; } static int leds_open(struct inode *inode, struct file *file){ printk(KERN_EMERG "leds open\n"); return 0; } static int leds_release(struct inode *inode, struct file *file){ printk(KERN_EMERG "leds release\n"); return 0; } static struct file_operations leds_ops = { .owner = THIS_MODULE, .open = leds_open, .release = leds_release, .unlocked_ioctl = itop4412_led_ioctl, }; static struct miscdevice leds_dev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &leds_ops, }; static int leds_probe(struct platform_device *pdv){ int i; int ret; printk(KERN_EMERG "\t leds enter \n"); for(i = 0; i < LED_NUM; i++) { ret = gpio_request(led_gpios[i], "LED"); // 请求GPIO if (ret) { printk("%s: request GPIO %d for LED failed, ret = %d\n", DRIVER_NAME, i, ret); } else{ s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); gpio_set_value(led_gpios[i], 1); } } misc_register(&leds_dev); if(ret<0){ printk("leds:register device failed!\n"); goto exit; } return 0; exit: misc_deregister(&leds_dev); return ret; } static int leds_remove(struct platform_device *pdv){ printk(KERN_EMERG "\t remove\n"); misc_deregister(&leds_dev); return 0; } static void leds_shutdown(struct platform_device *pdv){ ; } static int leds_suspend(struct platform_device *pdv,pm_message_t pmt){ return 0; } static int leds_resume(struct platform_device *pdv){ return 0; } struct platform_driver leds_driver = { .probe = leds_probe, .remove = leds_remove, .shutdown = leds_shutdown, .suspend = leds_suspend, .resume = leds_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, } }; static int leds_init(void) { int DriverState; printk(KERN_EMERG "leds_init enter!\n"); DriverState = platform_driver_register(&leds_driver); printk(KERN_EMERG "\tDriverState is %d\n",DriverState); return 0; } static void leds_exit(void) { printk(KERN_EMERG "leds_exit exit!\n"); platform_driver_unregister(&leds_driver); } module_init(leds_init); module_exit(leds_exit);
#!/bin/bash #通知编译器我们要编译模块的哪些源码 #这里是编译itop4412_hello.c这个文件编译成中间文件itop4412_hello.o obj-m += leds.o #源码目录变量,这里用户需要根据实际情况选择路径 #作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的 KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0 #当前目录变量 PWD ?= $(shell pwd) #make命名默认寻找第一个目标 #make -C就是指调用执行的路径 #$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0 #$(PWD)当前目录变量 #modules要执行的操作 all: make -C $(KDIR) M=$(PWD) modules #make clean执行的操作是删除后缀为o的文件 clean: rm -rf *.o
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <string.h> #define LEDS 2 int main(int argc, char *argv[]){ int fd, i, cmd = 2, led = 2; char *led_ctl_node = "/dev/itop4412_led_ctl"; char *led0 = "0"; char *led1 = "1"; char *cmd0 = "0"; char *cmd1 = "1"; if(strcmp(argv[1], led0) == 0){ led = 0; printf("led is 0!\n"); } if(strcmp(argv[1], led1) == 0){ led = 1; printf("led is 1!\n"); } if(strcmp(argv[2], cmd0) == 0){ cmd = 0; printf("cmd is 0!\n"); } if(strcmp(argv[2], cmd1) == 0){ cmd = 1; printf("cmd is 1!\n"); } /*O_RDWR只读打开,O_NDELAY非阻塞方式*/ if((fd = open(led_ctl_node, O_RDWR | O_NDELAY)) < 0){ printf("app open %s failed!\n", led_ctl_node); } else{ printf("app open %s success!\n", led_ctl_node); ioctl(fd, cmd, led); printf("app ioctl %s ,led is %d,cmd is %d!\n", led_ctl_node, led, cmd); } close(fd); }
通过arm-none-linux-gnueabi-gcc -o leds_app leds_app.c -static编译一下。生成leds_app。
具体看<linux内核驱动裁剪>。
在arch/arm/mach-exynos/mach-itop4412.c下,添加如下代码,具体看《linux虚拟平台设备注册》