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!
    测试结果
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    朱刘算法---有向图的最小生成树
    527D Clique Problem 判断一维线段没有两辆相交的最大线段数量
    Tex中的引号
    DAY 96 flask05
    DAY 95 flask04
    DAY 94 flask03
    DAY 93 flask02
    DAY 92 flask01
    DAY 91 爬虫05
    DAY 90 爬虫04
  • 原文地址:https://www.cnblogs.com/ch122633/p/9452383.html
Copyright © 2011-2022 走看看