一、GPIO的初始化
• 在内核源码目录下使用命令“ls drivers/gpio/*.o”,可以看到“gpioexynos4”被编译进了内核.通过搜索*.o文件,可以知道内核编译内哪些文件。针对的看可以简化很多。
– 生成.o文件代表最终被编译进了内核
– 除了menuconfig配置文件,还可以通过.o文件来判定该文件是否编译进了
ls drivers/gpio/*.o
内核
• 在“gpio-exynos4.c”文件最下面一行
– core_initcall(exynos4_gpiolib_init);
– core_initcall代表在linux初始化过程中会调用
– 初始化函数是在源码目录下“include/linux/init.h”文件中定义的,该头文件中定义了一系列的初始化函数,在linux启动的过程中会按等级
/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
*/
#define pure_initcall(fn) __define_initcall("0",fn,0) #define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
init.h文件相关部分
• 初始化函数调用了“exynos4_gpiolib_init”
• 通过软件source insight查找到exynos4_gpiolib_init函数的定义
• 在该函数中引用了chip = exynos4_gpio_common_4bit结构体
• 查找到结构体exynos4_gpio_common_4bit
• 可以看到结构体中有S5P_VA_XXXX的基地址定义,VA一般用来代表虚拟地址
以有带有label= "GPL2"的结构体为例
结构体exynos4_gpio_common_4bit
• .base = (S5P_VA_GPIO2 + 0x100)
– 表示偏移地址和虚拟地址相加
• .eint_offset = 0x20
– 表示中断部分,介绍中断的时候再讲(IO口可以配置为中断模式)
• .group = 22
– 给GPIO分组
• chip.base = EXYNOS4_GPL2(0),
– 宏定义EXYNOS4_GPL2(0)赋值给初始化函数
• chip.ngpio = EXYNOS4_GPIO_L2_NR
– 表示这一小组中有几个GPIO
• chip.label = "GPL2",
– 程序员需要关心的标志
• 宏定义EXYNOS4_GPL2(0)分析
– EXYNOS4_GPL2(_nr) (EXYNOS4_GPIO_L2_START + (_nr))
– 枚举GPIO
– EXYNOS4_GPIO_L2_START= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L1)
– EXYNOS4_GPIO_NEXT宏定义
– #define EXYNOS4_GPIO_NEXT(__gpio) \ ((__gpio##_START) + (__gpio##_NR)
+ CONFIG_S3C_GPIO_SPACE + 1)
• GPIO的数量EXYNOS4_GPIO_L2_NR
– 可以通过手册查到
• S5P_VA_GPIO2
虚拟地址
• 查找S5P_VA_GPIO2宏定义,可以看到所有的GPIO被分为4个bank,这
个和datasheet上面是一致的。
– S5P_VA_GPIO1
– S5P_VA_GPIO2 S3C_ADDR(0x02240000)
– S5P_VA_GPIO3
– S5P_VA_GPIO4
• 查找到S3C_ADDR宏定义
– #define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
• 查找到S3C_ADDR_BASE宏定义,这是一个虚拟地址,可以看出,地址
范围超出了1G或者2G内存的范围
– #define S3C_ADDR_BASE 0xF6000000
虚拟地址和物理地址映射
– 虚拟地址一般很好查找,一般在平台相关gpio的文件中就可以找到宏定义
• 在source insight中搜索关键字“S5P_VA_GPIO2”,看看那里用到了这个宏定义。搜索时间会比较长,1-5分钟吧。
• 搜索出来之后,可以看到除了gpio-exynos4.c文件中使用,cpu-exynos中也使用了,这是一个平台文件
• 结构体解释
– .virtual = (unsigned long)S5P_VA_GPIO2,表示虚拟地址
– .pfn = __phys_to_pfn(EXYNOS4_PA_GPIO2),表示物理地址
– .length = SZ_4K,表示映射的宽度
– .type = MT_DEVICE,
• 查找到宏定义EXYNOS4_PA_GPIO2
#define EXYNOS4_PA_GPIO2 0x11000000
这个物理地址0x11000000就是
• 初始化过程简单描述
– 平台文件分别定义好物理地址和虚拟地址
– 物理地址和虚拟地址之间映射
• 在初始化中,引入了程序员需要使用的GPIO宏定义,并将宏定义装入chip结构体中
GPIO的调用函数
• 例如头文件gpio-cfg.h中s3c_gpio_cfgpin函数。这个函数是给GPIO做配置,第一个参数是宏EXYNOS4_GPL2(0),第二个是配置的状态参数
– 配置头文件在arm/arm/plat-samsung/include/plat/gpio-cfg.h
• 查找该函数,可以看到进入函数就会调用chip结构体
– s3c_gpiolib_getchip,这个函数通过pin调用之后,会返回s3c_gpios[chip] 的参数
– exynos4_gpio_common_4bit[]和s3c_gpios都是结构体s3c_gpio_chip类型的数据
– 然后计算偏移地址等等一系列操作,这一部分是linux内核以及三星平台完成的,具体细节不用管。
• 也就是我们控制GPIO的时候,可以通过GPIO的一些处理函数加上类似EXYNOS4_GPL2(0)的宏定义,就可以操作GPIO
• 后面再具体介绍GPIO操作中,常用函数的使用
• 不是说好的分页大小要一样,怎么GPIO经过mmu处理的时候,又有SZ_256又有SZ_4K?
– 实际上CPU查找地址的时候,仍旧是通过内存。mmu本身不保存具体的数据,主要是提供一个虚拟地址和物理地址的表格,表格中还有字段的长度。这个分页和mmu没什么关系,是CPU内存以及物理地址之间通信使用
的概念。这个只是一个抽象的概念,理解mmu只是一个表格,CPU对GPIO的操作就很好理解了。
常见问题
• 内部寄存器不是很快么,CPU为什么不直接读取?
– 内部寄存器是很快,但是相对于CPU还是非常慢。CPU处理数据是将内存中一大段一大段处理,如果单个的读取内部寄存器的值,对CPU是极大的浪费。把内部寄存器也看成“特殊的物理地址”即可。
• 只讲了虚拟地址和物理地址对应数组,怎么没介绍哪里调用了?
– 大家可以看一下函数ioremap,linux会调用这个函数来实现gpio的映射关系
– 今天讲的已经够多够深入了,大家只要能够理解这么一层意思就可以了,这个东西对我们实际写驱动的帮助其实不是那么大!
• 如果我还是理解不了“对宏定义EXYNOS4_GPL2(0)的操作就是对4412芯片管脚AC21寄存器的操作”,怎么办?
– 记住这个结论,能够将宏变量EXYNOS4_GPL2(0)和GPL这一组GPIO的第0位寄存器联想起来。
– 后面跟着我依葫芦画瓢,不影响大家实际写程序,有兴趣再回过头理解
二、LED驱动
原理简单介绍
• 三极管(NPN锗管)
– “电流控制电流源”
– 三极端CE间的电阻可变,可以把Rce看成一个可调电阻,可调电阻的变量是电源
• IO管脚拉高之后,BE之间达到一定电流,可变电阻Rce就从无限大降低到大概几百欧姆。
– 高电平灯亮,低电平灯灭
头文件
• Linux中申请GPIO的头文件
– include/linux/gpio.h
• 三星平台的GPIO配置函数头文件
– arch/arm/plat-samsung/include/plat/gpio-cfg.h
– 包括三星所有处理器的配置函数
• 三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件
– arch/arm/mach-exynos/include/mach/gpio.h
– GPIO管脚拉高拉低配置参数等等
– 配置参数的宏定义应该在arch/arm/plat-samsung/include/plat/gpio-cfg.h文件中
• 三星平台4412平台,GPIO宏定义头文件。已经包含在头文件gpio.h中
– arch/arm/mach-exynos/include/mach/gpio-exynos4.h
– 包括4412处理器所有的GPIO的宏定义
#include <linux/module.h>
#include <linux/init.h> /* device register header file, include device and driver struct
* register and remove function */
#include <linux/platform_device.h>
/* register misc device header file */
#include <linux/miscdevice.h>
/* register deivce node file operations struct */
#include <linux/fs.h> /* linux gpio header file */
#include <linux/gpio.h>
/* samsung gpio config file */
#include <plat/gpio-cfg.h>
/* exynos gpio config header file */
#include <mach/gpio.h>
/* 4412 gpio header file */
#include <mach/gpio-exynos4.h> #define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl" MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET"); static int hello_open(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "hello open\n");
return ;
} static int hello_release(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "hello release\n");
return ;
} static long hello_ioctl(struct file *file, unsigned int cmd ,unsigned long arg)
{
printk(KERN_EMERG "cmd is %u, arg is %lu\n", cmd, arg); if(cmd > || arg > ) {
printk(KERN_EMERG "cmd and arg is 0 or 1\n");
return ;
} gpio_set_value(EXYNOS4_GPL2(), cmd); return ;
} static struct file_operations hello_ops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.unlocked_ioctl = hello_ioctl,
}; static struct miscdevice hello_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &hello_ops,
}; static int hello_probe(struct platform_device *pdv)
{
int ret;
printk(KERN_EMERG "\tinitialized\n"); /* set up gpio */
ret = gpio_request(EXYNOS4_GPL2(), "LEDS");
if(ret < ) {
printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");
return ret;
} s3c_gpio_cfgpin(EXYNOS4_GPL2(), S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPL2(), ); /* register */
misc_register(&hello_dev); return ;
} static int hello_remove(struct platform_device *pdv)
{
printk(KERN_EMERG "\tremove\n");
misc_deregister(&hello_dev);
return ;
} static void hello_shutdown(struct platform_device *pdv)
{ } static int hello_suspend(struct platform_device *pdv, pm_message_t state)
{
return ;
} static int hello_resume(struct platform_device *pdv)
{
return ;
} struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
}; static int hello_init(void)
{
int DriverState; printk(KERN_EMERG "Hello world enter!\n");
DriverState = platform_driver_register(&hello_driver); printk(KERN_EMERG "\tDriverState is %d\n", DriverState);
return ;
} static void hello_exit(void)
{
printk(KERN_EMERG "Hello world exit!\n");
platform_driver_unregister(&hello_driver);
} module_init(hello_init);
module_exit(hello_exit);
leds_module
linuxGPIO申请函数和赋值函数
– gpio_request
– gpio_set_value
• 三星平台配置GPIO函数
– s3c_gpio_cfgpin
• GPIO配置输出模式的宏变量
– S3C_GPIO_OUTPUT
• 使用这些函数和宏变量,将devicenode_linux_module.c改写为leds.c
• 将内核中的LED驱动去掉,重新编译内核,烧写到开发板。
• 将invoke_hello.c改写为invoke_leds.c
• 简单修改编译文件,编译
– arm-none-linux-gnueabi-gcc -o invoke_leds invoke_leds.c -static
在使用invoke_leds时,内需需要重新编译,去掉led的模块。
[root@iTOP-]# insmod hello.ko
[ 149.082155] Hello world enter!
[ 149.084082] initialized
[ 149.089124] DriverState is [root@iTOP-]# ls /dev/h*
/dev/hello_ctl [root@iTOP-]# ./invok_leds
[ 342.437287] hello open
[ 342.438727] cmd is , arg is
APP open /dev/hello_ctl success
[ 345.441525] cmd is , arg is
[ 348.443325] cmd is , arg is
[ 351.445052] cmd is , arg is
[ 351.446632] hello release
三、GPIO复用和程序简单分析
去掉占用GPIO的驱动
去掉占用调用的GPIO驱动,包括leds,buzzer,camera ov5640,WIFI mt6620
VIDEO_OV5640
– Device Drivers
– Multimedia support(MEDIA_SUPPORT [=y])
– Video capture adapters(VIDEO_CAPTURE_DRIVERS [=y])(去掉)
MTK_COMBO_CHIP_MT6620
– Device Drivers
– MediaTek Connectivity Combo Chip Config
– MediaTek Connectivity Combo Chip Support (MTK_COMBO [=y])(去掉)
– Select Chip (<choice> [=y])
Enable LEDS config
– Device Drivers
– Character devices
– Enable LEDS config
Enable BUZZER config
– Device Drivers
– Character devices
– Enable BUZZER config
static int led_gpios[] = {
EXYNOS4_GPL2(), EXYNOS4_GPK(), /* Led IO 2个 */
EXYNOS4_GPD0(), /* BUZZER IO 1个 */ EXYNOS4_GPX1(), EXYNOS4_GPX1(),EXYNOS4_GPX1(),EXYNOS4_GPX1(), /* 矩阵健盘8个 */
EXYNOS4_GPX3(),EXYNOS4_GPX2(),EXYNOS4_GPX2(),EXYNOS4_GPX3(), EXYNOS4212_GPJ1(),EXYNOS4_GPL0(),EXYNOS4_GPL0(),EXYNOS4212_GPJ1(), /* 摄像头14个 */
EXYNOS4212_GPJ1(),EXYNOS4212_GPJ1(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),
EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),
EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(), EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPK3(), /* WIFI 7个 */
EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPC1(),
};
编译简单程序测试
• 将leds.c修改为gpios.c,可以控制32个GPIO
• 修改Makefile文件
• 将invoke_leds.c修改为invoke_gpios.c
• 编译应用
– arm-none-linux-gnueabi-gcc -o invoke_gpios invoke_gpios.c -static
• 在开发板上加载测试
#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 "hello_ctl"
#define DEVICE_NAME "hello_gpio" MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET"); /*led的两个IO,网络是KP_COL0,VDD50_EN*/
/*蜂鸣器的1个IO,网络是MOTOR_PWM*/ /*矩阵键盘的8个IO,网络是CHG_FLT,HOOK_DET,CHG_UOK,XEINT14_BAK,
GM_INT1,6260_GPIO1,CHG_COK,XEINT29/KP_ROW13/ALV_DBG25*/ /*摄像头的14个IO,网络是CAM_MCLK,CAM2M_RST,CAM2M_PWDN,
CAM_D5,CAM_D7,CAM_D6,CAM_D4,CAM_D3,CAM_D2,CAM_D1,
CAM_PCLK,CAM_D0,CAM_VSYNC,CAM_HREF。
I2C_SDA7,I2C_SCL7也是可以设置为GPIO,不过总线一般不要去动它*/ /*WIFI模块的7个IO,WIFI_D3,WIFI_CMD,WIFI_D1,WIFI_CLK,WIFI_D0,WIFI_D2,GPC1_1*/
/*串口RX和TX等也是可以设置为GPIO,一般不要动它*/ /*数组中有32个引出到端子或者模块的IO,还有类似sd卡等也是可以作为GPIO,
其它引到连接器但是没有使用的GPIO等等*/
/*SCP管脚编号和POP的稍微有点不同,下面是SCP的*/
static int led_gpios[] = {
EXYNOS4_GPL2(),EXYNOS4_GPK1(),
EXYNOS4_GPD0(), EXYNOS4_GPX1(),EXYNOS4_GPX1(),EXYNOS4_GPX1(),EXYNOS4_GPX1(),
EXYNOS4_GPX3(),EXYNOS4_GPX2(),EXYNOS4_GPX2(),EXYNOS4_GPX3(), EXYNOS4212_GPJ1(),EXYNOS4_GPL0(),EXYNOS4_GPL0(),EXYNOS4212_GPJ1(),
EXYNOS4212_GPJ1(),EXYNOS4212_GPJ1(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),
EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(),
EXYNOS4212_GPJ0(),EXYNOS4212_GPJ0(), EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPK3(),
EXYNOS4_GPK3(),EXYNOS4_GPK3(),EXYNOS4_GPC1(),
}; #define LED_NUM ARRAY_SIZE(led_gpios) static long hello_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
printk("cmd is %d,arg is %d\n",cmd,arg); switch(cmd)
{
case :
case :
if (arg > LED_NUM) {
return -EINVAL;
} gpio_set_value(led_gpios[arg], cmd);
break; default:
return -EINVAL;
} gpio_set_value(led_gpios[], ); return ;
} static int hello_release(struct inode *inode, struct file *file){
printk(KERN_EMERG "hello release\n");
return ;
} static int hello_open(struct inode *inode, struct file *file){
printk(KERN_EMERG "hello open\n");
return ;
} static struct file_operations hello_ops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.unlocked_ioctl = hello_ioctl,
}; static struct miscdevice hello_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &hello_ops,
}; static int hello_probe(struct platform_device *pdv){
int ret,i; printk(KERN_EMERG "\tinitialized\n"); for(i=; i<LED_NUM; i++)
{
ret = gpio_request(led_gpios[i], "LED");
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], );
}
} gpio_set_value(led_gpios[], ); misc_register(&hello_dev);
if(ret<)
{
printk("leds:register device failed!\n");
goto exit;
}
return ; exit:
misc_deregister(&hello_dev);
return ret;
return ;
} static int hello_remove(struct platform_device *pdv){
int i; printk(KERN_EMERG "\tremove\n"); for(i=; i<LED_NUM; i++)
{
gpio_free(led_gpios[i]);
} misc_deregister(&hello_dev);
return ;
} static void hello_shutdown(struct platform_device *pdv){ ;
} static int hello_suspend(struct platform_device *pdv,pm_message_t pmt){ return ;
} static int hello_resume(struct platform_device *pdv){ return ;
} struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
}; static int hello_init(void)
gpios.c
invok_gpios.c
#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 GPIOS 32 int main(int argc , char **argv){
int fd,i,cmd=;
char *hello_node = "/dev/hello_gpio";
char *cmd0 = "";
char *cmd1 = ""; printf("argv[0] is %s;argv[1] is %s;",argv[],argv[]); if(strcmp(argv[], cmd0) == ){
cmd = ;
printf("cmd is 0!\n");
}
if(strcmp(argv[], cmd1) == ){
cmd = ;
printf("cmd is 1!\n");
} /*O_RDWR只读打开,O_NDELAY非阻塞方式*/
if((fd = open(hello_node,O_RDWR|O_NDELAY))<){
printf("APP open %s failed!\n",hello_node);
}
else{
printf("APP open %s success!\n",hello_node);
for(i=;i<GPIOS;i++){
ioctl(fd,cmd,i);
printf("APP ioctl %s ,cmd is %d,i is %d!\n",hello_node,cmd,i);
}
} close(fd);
}
invok_gpios
字符类GPIOS,LED驱动编写
字符驱动程序:
#include <linux/init.h>
#include <linux/module.h> /* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h> /* include device_create class file */
#include <linux/device.h> #include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
#include <mach/gpio-exynos4.h> #define DEVICE_NAME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000 MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET"); int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR; /* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR); static struct class *my_class; struct reg_dev {
char *data;
unsigned long size;
struct cdev cdev;
};
struct reg_dev *my_devices; /* open */
static int chardevnode_open(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "chardevnode open is success!\n");
return ;
} /* close */
static int chardevnode_release(struct inode *indoe, struct file *file)
{
printk(KERN_EMERG "chardevnode release is success!\n");
return ;
} /* io control */
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d\n", cmd, arg); if(cmd > || arg> ) {
printk(KERN_EMERG "cmd and arg is 0 or 1");
return ;
}
switch(arg) {
case :
gpio_set_value(EXYNOS4_GPL2(), cmd);
break;
case :
gpio_set_value(EXYNOS4_GPK1(), cmd);
break;
default:
printk(KERN_EMERG "cmd and arg is 0 or 1");
break;
} return ;
} /* read */
static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
{
return ;
} /* write */
static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
{
return ;
} /* lseek */
static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
{
return ;
} struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = chardevnode_open,
.release = chardevnode_release,
.unlocked_ioctl = chardevnode_ioctl,
.read = chardevnode_read,
.write = chardevnode_write,
.llseek = chardevnode_llseek,
}; /* GPL2_0 */
static int led1_init(void)
{
int ret;
printk(KERN_EMERG "Gpio led 1 init\n"); ret = gpio_request(EXYNOS4_GPL2(), "LEDS");
if(ret < ) {
printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");
return ret;
} s3c_gpio_cfgpin(EXYNOS4_GPL2(), S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPL2(), ); return ;
} /* GPK1_1 */
static int led2_init(void)
{
int ret;
printk(KERN_EMERG "GPIO led 2 init\n"); ret = gpio_request(EXYNOS4_GPK1(), "LEDS2");
if(ret < ) {
printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) fialed\n");
return ret;
}
s3c_gpio_cfgpin(EXYNOS4_GPK1(), S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPK1(), ); return ;
} static void reg_init_cdev(struct reg_dev *dev, int index)
{
int err;
int devno = MKDEV(numdev_major, numdev_minor+index); cdev_init(&dev->cdev, &my_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_fops; err = cdev_add(&dev->cdev, devno, );
if(err) {
printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);
} else {
printk(KERN_EMERG "cdev_add %d is success!\n", (numdev_minor+index));
}
} static int hello_init(void)
{
int ret, i;
dev_t num_dev; printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);
printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor); if(numdev_major) {
num_dev = MKDEV(numdev_major, numdev_minor);
ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
} else {
ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
numdev_major = MAJOR(num_dev);
printk(KERN_EMERG "register req major number is %d\n", numdev_major);
} if(ret < ) {
printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);
unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
return ret;
} my_class = class_create(THIS_MODULE, DEVICE_NAME);
my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
if(!my_devices) {
ret = -ENOMEM;
printk(KERN_EMERG "kmalloc fialed!\n");
goto fail;
}
memset(my_devices, , DEVICE_MINOR_NUM*sizeof(struct reg_dev));
for(i=;i<DEVICE_MINOR_NUM;i++) {
my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);
memset(my_devices[i].data, , REGDEV_SIZE); /* data address */
/* register device to system */
reg_init_cdev(&my_devices[i], i);
/* create device node */
device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);
} led1_init();
led2_init();
printk(KERN_EMERG "Hello World enter!\n");
return ; fail:
unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
return ret;
} static void hello_exit(void)
{
int i;
dev_t num_dev = MKDEV(numdev_major, numdev_minor);
printk(KERN_EMERG "Hello World exit!\n");
for(i=;i<DEVICE_MINOR_NUM;i++) {
cdev_del(&my_devices[i].cdev);
/* release memory*/
device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));
} /* release my class*/
class_destroy(my_class);
/* release kfre */
kfree(my_devices);
unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
} module_init(hello_init);
module_exit(hello_exit);
char_driver_leds.c
然后是应用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h> int main(int argc, char *argv[])
{
int fd0, fd1;
char *hello_node0 = "/dev/chardevnode0";
char *hello_node1 = "/dev/chardevnode1";
if(argc > ) {
printf("please input cmd and arg\n");
}
/* O_RDWR只读打开, O_NDELAY非阻塞方式 */
fd0 = open(hello_node0, O_RDWR|O_NDELAY);
if(fd0 < ) {
printf("APP open %s failed\n", hello_node0);
exit(EXIT_FAILURE);
} else {
printf("APP open %s success\n", hello_node0);
ioctl(fd0, atoi(argv[]), atoi(argv[]));
}
/* O_RDWR只读打开, O_NDELAY非阻塞方式 */
/*
fd1 = open(hello_node0, O_RDWR|O_NDELAY);
if(fd1 < 0) {
printf("APP open %s failed\n", hello_node0);
exit(EXIT_FAILURE);
} else {
printf("APP open %s success\n", hello_node0);
ioctl(fd1, 1, 6);
}
*/
close(fd0);
close(fd1);
}
invoke_cahr_driver.c
然后是makefile
TARGET_NAME = char_driver_leds
APP_NAME = invoke_char_gpios
obj-m += $(TARGET_NAME).o KDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3. PWD ?= $(shell pwd) all:app
make -C $(KDIR) M=$(PWD) modules app:$(APP_NAME)
arm-none-linux-gnueabi-gcc $(APP_NAME).c -o $(APP_NAME) -static clean:
rm -rf *.o *.ko *.mod.c *.symvers *.order *.cmd .$(TARGET_NAME)* $(APP_NAME)
Makefile
测试结果:
[root@iTOP-]# insmod char_driver_leds.ko
[ 420.107938] numdev_major is !
[ 420.109549] numdev_minor is !
[ 420.112677] register req major number is
[ 420.125765] cdev_add is success!
[ 420.137424] cdev_add is success!
[ 420.148881] Gpio led init
[ 420.150342] gpio_request EXYNOS4_GPL2() failed
[ 420.154743] GPIO led init
[ 420.165167] Hello World enter!
[root@iTOP-]# ./invoke_char_gpios
please input cmd [ 431.050669] chardevnode open is success!
[ 431.054691] chardevnode release is success!cmd is ,arg is
[ 431.060238] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-]# ./invoke_char_gpios
please input cmd [ 435.289936] chardevnode open is success!
[ 435.294047] chardevnode release is success!cmd is ,arg is
[ 435.299498] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-]# ./invoke_char_gpios
please input cmd [ 440.595232] chardevnode open is success!
[ 440.599237] chardevnode release is success!cmd is ,arg is
and arg
APP open /dev/chardevnode0 success
[ 440.609648] chardevnode release is success!
[root@iTOP-]# ./invoke_char_gpios
please input cmd [ 443.313565] chardevnode open is success!
[ 443.317679] chardevnode release is success!cmd is ,arg is
[ 443.323129] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success [root@iTOP-]# rmmod char_driver_leds
[ 468.722834] Hello World exit!
测试结果