zoukankan      html  css  js  c++  java
  • 4412 GPIO初始化

    一、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被分为4bank,这
    个和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.hs3c_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是极大的浪费。把内部寄存器也看成特殊的物理地址即可。
    只讲了虚拟地址和物理地址对应数组,怎么没介绍哪里调用了?
    大家可以看一下函数ioremaplinux会调用这个函数来实现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
    ");
            return 0;
    }
    
    static int hello_release(struct inode *inode, struct file *file)
    {
            printk(KERN_EMERG "hello release
    ");
            return 0;
    }
    
    static long hello_ioctl(struct file *file, unsigned int cmd ,unsigned long arg)
    {
            printk(KERN_EMERG "cmd is %u, arg is %lu
    ", cmd, arg);
    
            if(cmd > 1 || arg > 1) {
                    printk(KERN_EMERG "cmd and arg is 0 or 1
    ");
                    return 0;
            }
    
            gpio_set_value(EXYNOS4_GPL2(0), cmd);
    
            return 0;
    }
    
    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 "	initialized
    ");
    
            /* set up gpio */
            ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");
            if(ret < 0) {
                    printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed
    ");
                    return ret;
            }
    
            s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);
    
            gpio_set_value(EXYNOS4_GPL2(0), 0);
    
            /* register */
            misc_register(&hello_dev);
    
            return 0;
    }
    
    static int hello_remove(struct platform_device *pdv)
    {
            printk(KERN_EMERG "	remove
    ");
            misc_deregister(&hello_dev);
            return 0;
    }
    
    static void hello_shutdown(struct platform_device *pdv)
    {
    
    }
    
    static int hello_suspend(struct platform_device *pdv, pm_message_t state)
    {
            return 0;
    }
    
    static int hello_resume(struct platform_device *pdv)
    {
            return 0;
    }
    
    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!
    ");
            DriverState = platform_driver_register(&hello_driver);
    
            printk(KERN_EMERG "	DriverState is %d
    ", DriverState);
        return 0;
    }
    
    static void hello_exit(void)
    {
        printk(KERN_EMERG "Hello world exit!
    ");
            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-4412]# insmod hello.ko                                                                          
    [  149.082155] Hello world enter!
    [  149.084082]  initialized
    [  149.089124]  DriverState is 0
    
    [root@iTOP-4412]# ls /dev/h*                                                                               
    /dev/hello_ctl
    
    [root@iTOP-4412]# ./invok_leds                                                                             
    [  342.437287] hello open
    [  342.438727] cmd is 1, arg is 1
    APP open /dev/hello_ctl success
    [  345.441525] cmd is 0, arg is 1
    [  348.443325] cmd is 1, arg is 1
    [  351.445052] cmd is 0, arg is 1
    [  351.446632] hello release

    三、GPIO复用和程序简单分析

     去掉占用GPIO的驱动

    去掉占用调用的GPIO驱动,包括ledsbuzzercamera ov5640WIFI 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(0), EXYNOS4_GPK(1),    /* Led IO 2个 */
        EXYNOS4_GPD0(0),              /* BUZZER IO 1个 */
    
        EXYNOS4_GPX1(0), EXYNOS4_GPX1(3),EXYNOS4_GPX1(5),EXYNOS4_GPX1(6),    /* 矩阵健盘8个 */
        EXYNOS4_GPX3(0),EXYNOS4_GPX2(6),EXYNOS4_GPX2(7),EXYNOS4_GPX3(5),
    
        EXYNOS4212_GPJ1(3),EXYNOS4_GPL0(1),EXYNOS4_GPL0(3),EXYNOS4212_GPJ1(0),      /* 摄像头14个 */
        EXYNOS4212_GPJ1(2),EXYNOS4212_GPJ1(1),EXYNOS4212_GPJ0(7),EXYNOS4212_GPJ0(6),
        EXYNOS4212_GPJ0(5),EXYNOS4212_GPJ0(4),EXYNOS4212_GPJ0(0),EXYNOS4212_GPJ0(3),
       EXYNOS4212_GPJ0(1),EXYNOS4212_GPJ0(2),
    
        EXYNOS4_GPK3(6),EXYNOS4_GPK3(1),EXYNOS4_GPK3(4),EXYNOS4_GPK3(0),      /* WIFI 7个 */
        EXYNOS4_GPK3(3),EXYNOS4_GPK3(5),EXYNOS4_GPC1(1),
    };

     

    编译简单程序测试

    leds.c修改为gpios.c,可以控制32GPIO
    修改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(0),EXYNOS4_GPK1(1),
        EXYNOS4_GPD0(0),
        
        EXYNOS4_GPX1(0),EXYNOS4_GPX1(3),EXYNOS4_GPX1(5),EXYNOS4_GPX1(6),
        EXYNOS4_GPX3(0),EXYNOS4_GPX2(6),EXYNOS4_GPX2(7),EXYNOS4_GPX3(5),
        
        EXYNOS4212_GPJ1(3),EXYNOS4_GPL0(1),EXYNOS4_GPL0(3),EXYNOS4212_GPJ1(0),
        EXYNOS4212_GPJ1(2),EXYNOS4212_GPJ1(1),EXYNOS4212_GPJ0(7),EXYNOS4212_GPJ0(6),
        EXYNOS4212_GPJ0(5),EXYNOS4212_GPJ0(4),EXYNOS4212_GPJ0(0),EXYNOS4212_GPJ0(3),
        EXYNOS4212_GPJ0(1),EXYNOS4212_GPJ0(2),
        
        EXYNOS4_GPK3(6),EXYNOS4_GPK3(1),EXYNOS4_GPK3(4),EXYNOS4_GPK3(0),
        EXYNOS4_GPK3(3),EXYNOS4_GPK3(5),EXYNOS4_GPC1(1),
    };
    
    #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
    ",cmd,arg);
        
        switch(cmd)
        {
            case 0:
            case 1:
                if (arg > LED_NUM) {
                    return -EINVAL;
                }
    
                gpio_set_value(led_gpios[arg], cmd);
                break;
    
            default:
                return -EINVAL;
        }
        
        gpio_set_value(led_gpios[2], 0);
        
        return 0;
    }
    
    static int hello_release(struct inode *inode, struct file *file){
        printk(KERN_EMERG "hello release
    ");
        return 0;
    }
    
    static int hello_open(struct inode *inode, struct file *file){
        printk(KERN_EMERG "hello open
    ");
        return 0;
    }
    
    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 "	initialized
    ");
        
        for(i=0; i<LED_NUM; i++)
        {
            ret = gpio_request(led_gpios[i], "LED");
            if (ret) {
                printk("%s: request GPIO %d for LED failed, ret = %d
    ", DRIVER_NAME,
                        i, ret);
                }
            else{
                s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
                gpio_set_value(led_gpios[i], 1);            
            }
        }
    
        gpio_set_value(led_gpios[2], 0);
        
        misc_register(&hello_dev);
        if(ret<0)
        {
            printk("leds:register device failed!
    ");
            goto exit;
        }
        return 0;
    
    exit:
        misc_deregister(&hello_dev);
        return ret;
        return 0;
    }
    
    static int hello_remove(struct platform_device *pdv){
        int i;
        
        printk(KERN_EMERG "	remove
    ");
        
        for(i=0; i<LED_NUM; i++)
        {
            gpio_free(led_gpios[i]);
        }
        
        misc_deregister(&hello_dev);
        return 0;
    }
    
    static void hello_shutdown(struct platform_device *pdv){
        
        ;
    }
    
    static int hello_suspend(struct platform_device *pdv,pm_message_t pmt){
        
        return 0;
    }
    
    static int hello_resume(struct platform_device *pdv){
        
        return 0;
    }
    
    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=2;
        char *hello_node = "/dev/hello_gpio";
        char *cmd0 = "0"; 
        char *cmd1 = "1";
        
        printf("argv[0] is %s;argv[1] is %s;",argv[0],argv[1]);
        
        if(strcmp(argv[1], cmd0) == 0){
            cmd = 0;
            printf("cmd is 0!
    ");
        }
        if(strcmp(argv[1], cmd1) == 0){
            cmd = 1;
            printf("cmd is 1!
    ");
        }
        
    /*O_RDWR只读打开,O_NDELAY非阻塞方式*/    
        if((fd = open(hello_node,O_RDWR|O_NDELAY))<0){
            printf("APP open %s failed!
    ",hello_node);
        }
        else{
            printf("APP open %s success!
    ",hello_node);
            for(i=0;i<GPIOS;i++){
                ioctl(fd,cmd,i);
                printf("APP ioctl %s ,cmd is %d,i is %d!
    ",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!
    ");
        return 0;
    }
    
    /* close */
    static int chardevnode_release(struct inode *indoe, struct file *file)
    {
        printk(KERN_EMERG "chardevnode release is success!
    ");
        return 0;
    }
    
    /* 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
    ", cmd, arg);
    
        if(cmd >1 || arg> 1) {
            printk(KERN_EMERG "cmd and arg is 0 or 1");
            return 0;
        }
        switch(arg) {
            case 0:
                gpio_set_value(EXYNOS4_GPL2(0), cmd);
                break;
            case 1:
                gpio_set_value(EXYNOS4_GPK1(1), cmd);
                break;
            default:
                printk(KERN_EMERG "cmd and arg is 0 or 1");
                break;
        }
    
        return 0;
    }
    
    /* read */
    static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
    {
        return 0;
    }
    
    /* write */
    static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
    {
        return 0;
    }
    
    /* lseek */
    static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
    {
        return 0;
    }
    
    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
    ");
    
        ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");
        if(ret < 0) {
            printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed
    ");
            return ret;
        }
    
        s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);
    
        gpio_set_value(EXYNOS4_GPL2(0), 0);
    
        return 0; 
    }
    
    /* GPK1_1 */
    static int led2_init(void)
    {
        int ret;
        printk(KERN_EMERG "GPIO led 2 init
    ");
    
        ret = gpio_request(EXYNOS4_GPK1(1), "LEDS2");
        if(ret < 0) {
            printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) fialed
    ");
            return ret;
        }
        s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT);
    
        gpio_set_value(EXYNOS4_GPK1(1), 0);
    
        return 0;
    }
    
    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, 1);
        if(err) {
            printk(KERN_EMERG "cdev_add %d is fail! %d
    ", index, err);
        } else {
            printk(KERN_EMERG "cdev_add %d is success!
    ", (numdev_minor+index));
        }
    }
    
    static int hello_init(void)
    {
        int ret, i;
        dev_t num_dev;
        
        printk(KERN_EMERG "numdev_major is %d!
    ", numdev_major);
        printk(KERN_EMERG "numdev_minor is %d!
    ", 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
    ", numdev_major);
        }
    
        if(ret < 0) {
            printk(KERN_EMERG "register_chrdev_region req %d is failed
    ", 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!
    ");
            goto fail;
        }
        memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
        for(i=0;i<DEVICE_MINOR_NUM;i++) {
            my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);        
            memset(my_devices[i].data, 0, 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!
    ");
        return 0;
    
    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!
    ");
        for(i=0;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 > 2) {
                    printf("please input cmd and arg
    ");
            }
            /* O_RDWR只读打开, O_NDELAY非阻塞方式 */
            fd0 = open(hello_node0, O_RDWR|O_NDELAY);
            if(fd0 < 0) {
                    printf("APP open %s failed
    ", hello_node0);
                    exit(EXIT_FAILURE);
            } else {
                    printf("APP open %s success
    ", hello_node0);
                    ioctl(fd0, atoi(argv[1]), atoi(argv[2]));
            }
            /* O_RDWR只读打开, O_NDELAY非阻塞方式 */
    /*
            fd1 = open(hello_node0, O_RDWR|O_NDELAY);
            if(fd1 < 0) {
                    printf("APP open %s failed
    ", hello_node0);
                    exit(EXIT_FAILURE);
            } else {
                    printf("APP open %s success
    ", 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.0
    
    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-4412]# insmod char_driver_leds.ko                                                               
    [  420.107938] numdev_major is 0!
    [  420.109549] numdev_minor is 0!
    [  420.112677] register req major number is 248
    [  420.125765] cdev_add 0 is success!
    [  420.137424] cdev_add 1 is success!
    [  420.148881] Gpio led 1 init
    [  420.150342] gpio_request EXYNOS4_GPL2(0) failed
    [  420.154743] GPIO led 2 init
    [  420.165167] Hello World enter!
    [root@iTOP-4412]# ./invoke_char_gpios 1 0                                                                  
    please input cmd [  431.050669] chardevnode open is success!
    [  431.054691] chardevnode release is success!cmd is 1,arg is 0
    [  431.060238] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    [root@iTOP-4412]# ./invoke_char_gpios 1 1                                                                  
    please input cmd [  435.289936] chardevnode open is success!
    [  435.294047] chardevnode release is success!cmd is 1,arg is 1
    [  435.299498] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    [root@iTOP-4412]# ./invoke_char_gpios 0 0                                                                  
    please input cmd [  440.595232] chardevnode open is success!
    [  440.599237] chardevnode release is success!cmd is 0,arg is 0
    and arg
    APP open /dev/chardevnode0 success
    [  440.609648] chardevnode release is success!
    [root@iTOP-4412]# ./invoke_char_gpios 0 1                                                                  
    please input cmd [  443.313565] chardevnode open is success!
    [  443.317679] chardevnode release is success!cmd is 0,arg is 1
    [  443.323129] chardevnode release is success!
    and arg
    APP open /dev/chardevnode0 success
    
    [root@iTOP-4412]# rmmod char_driver_leds                                                                   
    [  468.722834] Hello World exit!
    测试结果
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    part11-1 Python图形界面编程(Python GUI库介绍、Tkinter 组件介绍、布局管理器、事件处理)
    part10-3 Python常见模块(正则表达式)
    Cyclic Nacklace HDU
    模拟题 Right turn SCU
    状态DP Doing Homework HDU
    Dp Milking Time POJ
    区间DP Treats for the Cows POJ
    DP Help Jimmy POJ
    Dales and Hills Gym
    Kids and Prizes Gym
  • 原文地址:https://www.cnblogs.com/ch122633/p/9452383.html
Copyright © 2011-2022 走看看