美文网首页
三 . 树莓派A20 GPIO驱动程序编写

三 . 树莓派A20 GPIO驱动程序编写

作者: wit_yuan | 来源:发表于2017-07-16 16:04 被阅读0次

    1 硬件结构

    我使用的是风火轮出品的树莓派A20板子,同样也买了一个DVK521扩展的底板,可以看到有8个LED灯,关于GPIO,点亮LED是最好的驱动编写例子。

    先看DVK521板子上的LED灯硬件原理:

    LED灯原理图

    LED灯原理比较简单,安装该图来看,需要将PE4~PE11置高即可,从这里看来,A20的IO口输出电流能力还是比较强。

    2 驱动编写

    2.1 GPIO寄存器的相关知识

    按照以前S3C2440,S3C6410等板子,找到寄存器很简单,只需要翻遍手册,然后使用内核提供的ioremap将硬件地址映射到内核的虚拟地址上,即可达到通过控制虚拟地址达到控制硬件地址的效果。

    但是,在A20上,控制方法已经改成了script_parser_fetch()等等API操作方法,这表示描述方法有所不同,但是具体思路仍然相同。要注意的是,也可以使用以前的控制方法。

    在《树莓派3主控芯片手册V1.0(A20datasheet).pdf》(点击链接)文档的P19页上,
    A20将通用的GPIO当成了PIO,我截取出PIO的地址范围,如下图所示:

    PIO地址范围

    再跳到P240,找到1.19 Port Controller这一章,我们只需要关注的是Port E(PE):12 input/output port。

    GPIO引脚

    在这一章里,说明了一些知识点:
    1.上面列的GPIO引脚中,除了Port S做位DRAM来用,其他的没有复用的功能的,都可以用作普通IO口。
    2.A20上有32个外部PIO中断。

    由于是初次使用该款芯片,而GPIO是最基本的功能,所以,还是继续贴出P247上的关于GPIO的配置寄存器内容,方便后面的查找:

    GPIO配置寄存器
    GPIO数据寄存器等等

    2.2 配置寄存器

    我在MarsBoard-A20-Linux-SDK-V1.2源码目录下,找到了sun4i-keypad.c文件,在sw_keypad_init()中找到了API函数script_parser_fetch(),根据这个函数,我在gpio-sunxi.c中找到了操作gpio的许多API函数,例如sunxi_gpio_to_irq(),sunxi_gpio_set()等等。

    最终,将所有的点,可以挂接到一个结构体上:struct gpio_chip。

    到这里,对于GPIO的内容,就到头了,分析再多,那就变成了一篇驱动程序分析的文章了。

    在gpio-sunxi.c的sunxi_gpio_probe()函数里,通过注释,知道该函数中有在找script.bin文件。

    2.2.1 找到fex2bin工具,sys_config.fex,script.bin的目录

    先找到fex2bin工具,sys_config.fex,script.bin在哪个目录下:

    wityuan@ubuntu:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2$ ls tools/sunxi-tools/
    adb-devprobe.sh  fel-copy.c      jtag-loop.c      nand-part-main.c  script_fex.c
    bin              fel-gpio        jtag-loop.lds    nand-part-main.o  script_fex.h
    bin2fex          fel-pio.c       jtag-loop.S      phoenix_info.c    script.h
    boot_head.lds    fel-pio.lds     Makefile         pio               script+lcd.bin
    boot_head.S      fel-sdboot.c    nand-common.h    pio.c             script_lcd.fex
    bootinfo         fel-sdboot.lds  nand-part        README            script_uboot.c
    bootinfo.c       fex2bin         nand-part-a10.h  script.bin        script_uboot.h
    common.h         fexc            nand-part-a10.o  script_bin.c      sys_config.fex
    COPYING          fexc.c          nand-part-a20.h  script_bin.h      usb-boot
    fel              fexc.h          nand-part-a20.o  script.c
    fel.c            include         nand-part.c      script.fex
    wityuan@ubuntu:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2$ 
    

    2.2.2 找到开发板上的script.bin文件位置

    在开发板上使用如下命令可以找到script.bin文件:

    root@marsboard:~# ls /mnt/
    root@marsboard:~# mount /dev/nanda /mnt/
    root@marsboard:~# ls /mnt/
    boot.axf            drv_de.drv    font32.sft  os_show     sprite.axf
    boot.ini            drv_hdmi.drv  linux       prvt.axf    uEnv.txt
    boot_signature.axf  font24.sft    magic.bin   script.bin  uImage
    root@marsboard:~# 
    

    2.2.3 修改sys_config.fex文件

    从sys_config.fex文件中,可以看到,里面描述的都是io口的功能。

    我把里面最基本的功能描述,摘录如下:

    ;A20 PAD application
    ;-------------------------------------------------------------------------------
    ; 说明:
    ;   1. 脚本中的字符串区分大小写,用户可以修改"="后面的数值,但是不要修改前面的字符串
    ;   2. 新增主键和子键的名称必须控制在32个字符以内,不包括32个
    ;   3. 所以的注释以“;”开始,单独占据一行
    ;   4. 注释不可和配置项同行,例如:主键和子健后面不能添加任何形式的注释
    ;
    ; gpio的描述形式:Port:端口+组内序号<功能分配><内部电阻状态><驱动能力><输出电平状态>
    ;           例如:port:PA0<0><default><default><default>
    ;-------------------------------------------------------------------------------
    

    现在在硬件上,有PE4~PE11这些管脚需要设置。

    1. 用什么参数怎么去设置一个IO口?

    要去寻求这个问题的答案,内核肯定会有相应的依据,我在script.h中找到了结构体:
    struct sunxi_property_gpio_value。

    摘录如下:

    struct sunxi_property_gpio_value {
        u32 port;      /*表明是哪个端口,如Port A,Port B等等*/
        u32 port_num;  /*表明是哪个端口号,例如PE14的14*/
        s32 mul_sel;   /*表明的是功能分配,例如复用功能还是普通IO口*/
        s32 pull;      /*表明的是内部上拉状态*/
        s32 drv_level; /*表明的是驱动能力*/
        s32 data;      /*表明的是输出电平状态*/
    };
    

    每一项含义清楚了之后,我们就可以一个个的去设置了,那么,还是要回到手册:《树莓派3主控芯片手册V1.0(A20datasheet).pdf》上的P249。

    根据手册上的说明:
    mul_sel:
    0--输入
    1--输出
    2~7--复用功能

    pull:
    0--上拉/下拉禁止
    1--上拉使能
    2--下拉使能

    drv_level:
    0--level 0
    1--level 1
    2--level 2
    3--level 3

    data:
    0--输出电平为0
    1--输出电平为1

    可以在这个网站上找到详细描述:链接

    现在,可以在sys_config.fex文件中添加PE4~PE11的信息:

    [led_test_para]
    led_test_enable     = 1
    led1                = port:PE4<1><1><default><default>
    led2                = port:PE5<1><1><default><default>
    led3                = port:PE6<1><1><default><default>
    led4                = port:PE7<1><1><default><default>
    led5                = port:PE8<1><1><default><default>
    led6                = port:PE9<1><1><default><default>
    led7                = port:PE10<1><1><default><default>
    led8                = port:PE11<1><1><default><default>
    

    修改完成后,需要将该文件更新为script.bin,操作方法如下:
    先看怎么用fex2bin这个工具:

    wityuan@ubuntu:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ ./fex2bin  -v
    ./fex2bin: from fex:<stdin> to bin:<stdout>
    

    之后,我们先备份源文件,然后再生成自己的script.bin文件。

    wityuan@ubuntu:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ cp script.bin script_bak.bin
    wityuan@ubuntu:~/Downloads/MarsBoard-A20-Linux-SDK-V1.2/tools/sunxi-tools$ ./fex2bin sys_config.fex script.bin
    

    最后,将A20开发板上的script.bin替换掉。

    2.2.4 获取script.bin的参数

    找到\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\leds\leds-sunxi.c文件,这是一个很好的控制led灯的例子程序,可以参考它来做下面的工作。

    我直接粘贴出来整个简单的gpio.c程序文件:

    #include "linux/init.h"
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/leds.h>
    #include <plat/sys_config.h>
    
    script_gpio_set_t info;
    unsigned gpio_handler;
    
    static int __init led_init(void)
    {
        int err = 0;
        int led_test_enabled = 0;
        int ret = 0;
        
        err = script_parser_fetch("led_test_para", "led_test_enable", &led_test_enabled,
                        sizeof(led_test_enabled)/sizeof(int));
    
        if(!err){
            printk("---script.bin led get ok,value:%d----\n",led_test_enabled);
        }
        else
        {
            printk("---script.bin led get false----\n");    
            return -1;
        }
    
        err = script_parser_fetch("led_test_para", "led1",
                    (int *)&info,
                    sizeof(script_gpio_set_t));
        if (err) {
            printk("----script.bin get io error----\r\n");
            return -1;
        }
        /* reserve gpio for led */
        gpio_handler = gpio_request_ex("led_test_para", "led1");
        if (!gpio_handler) {
            printk("----script.bin can't requst handler----\r\n");
            return -1;
        }
    
        /*set pin as output*/
        ret =  gpio_set_one_pin_io_status(gpio_handler, 1,
                        "led1");
        if (!ret)
            /*set value level 1*/
            ret = gpio_write_one_pin_value(gpio_handler,
                        1, "led1"); 
        return 0;
    }
    
    
    static void __exit  led_exit(void)
    {
        if (gpio_handler)
            gpio_release(gpio_handler, 1);
        printk("---dirver exit---\r\n");
    }
    
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_DESCRIPTION("Driver for empty");
    MODULE_AUTHOR("wit_yuan");
    MODULE_LICENSE("GPL");
    
    

    Makefile文件如下:

    ifeq ($(KERNELRELEASE),)
        KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
        PWD=$(shell pwd)
    
    modules:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
        arm-linux-gnueabihf-gcc -o gpio gpio.c
    
    modules_install:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
    clean:
        rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd
    else
        obj-m:=gpio.o
    endif
    

    然后将gpio.ko文件下载到开发板上,再开发板上执行:

    root@marsboard:~# insmod gpio.ko 
    ---script.bin led get ok,value:1----
    root@marsboard:~# rmmod gpio
    ---dirver exit---
    root@marsboard:~# 
    

    可以看到insmod程序的时候,L1灯亮起,而rmmod程序的时候,L1灯灭。

    3 进阶

    3.1 添加应用层代码

    上面的驱动程序,只是在内核驱动程序层面控制硬件。现在我们需要通过应用层程序来控制底层的硬件。

    直接上代码吧。

    gpio.c代码如下:

    #include "linux/init.h"
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/leds.h>
    #include <plat/sys_config.h>
    #include <linux/major.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    
    
    static script_gpio_set_t info;
    static unsigned gpio_handler;
    static struct class *leds_class;
    static struct device *leds_device;
    static unsigned int leds_major;
    
    static int led_open(struct inode *inode, struct file *filp);
    static ssize_t led_write (struct file *filp, const char __user *buf, size_t len, loff_t *off);
    static int led_close(struct inode *inode, struct file *filp);
    
    
    struct file_operations led_operations = {
        .owner   = THIS_MODULE,
        .open    = led_open,
        .write   = led_write,
        .release = led_close,
    };
    
    static int led_open(struct inode *inode, struct file *filp)
    {
        int err = 0;
        int led_test_enabled = 0;
        int ret = 0;
        
        err = script_parser_fetch("led_test_para", "led_test_enable", &led_test_enabled,
                        sizeof(led_test_enabled)/sizeof(int));
    
        if(!err){
            printk("---script.bin led get ok,value:%d----\n",led_test_enabled);
        }
        else
        {
            printk("---script.bin led get false----\n");    
            return -1;
        }
    
        err = script_parser_fetch("led_test_para", "led1",
                    (int *)&info,
                    sizeof(script_gpio_set_t));
        if (err) {
            printk("----script.bin get io error----\r\n");
            return -1;
        }
        /* reserve gpio for led */
        gpio_handler = gpio_request_ex("led_test_para", "led1");
        if (!gpio_handler) {
            printk("----script.bin can't requst handler----\r\n");
            return -1;
        }
    
    
        return 0;
    }
    
    static ssize_t led_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
    {
        int val;
        gpio_set_one_pin_io_status(gpio_handler, 1,
                            "led1");
        
        copy_from_user(&val,buf,4);
    
        printk("----led val:%d----\r\n",val);
        
        switch(val){
            case 0:
                gpio_write_one_pin_value(gpio_handler,
                            0, "led1"); 
                break;
            case 1:
                gpio_write_one_pin_value(gpio_handler,
                            1, "led1"); 
                break;
            default:
    
                break;
        }
        return 0;
    }
    
    static int led_close(struct inode *inode, struct file *filp)
    {
    
        printk("----led close----\r\n");
    
    
        return 0;
    }
    
    
    
    static int __init led_init(void)
    {
        leds_major = register_chrdev(0, "led_chrdev", &led_operations);
    
        leds_class = class_create(THIS_MODULE, "leds_class");
    
        if(!leds_class){
            unregister_chrdev(leds_major, "led_chrdev");
            printk("----leds_chrdev error----\r\n");
            return -1;
        }
        leds_device = device_create(leds_class, NULL, MKDEV(leds_major,0),
                              NULL, "leds_device");
        if(!leds_device){
            class_destroy(leds_class);
            unregister_chrdev(leds_major, "led_chrdev");
            printk("----leds_device error----\r\n");
            return -1;
        }
    
        printk("----leds init ok----\r\n");
        return 0;
    }
    
    
    static void __exit  led_exit(void)
    {
        if (gpio_handler)
            gpio_release(gpio_handler, 1);
        
        device_destroy(leds_class, MKDEV(leds_major, 0));
        class_destroy(leds_class);
        unregister_chrdev(leds_major, "led_chrdev");
    
        printk("---driver exit---\r\n");
    }
    
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_DESCRIPTION("Driver for empty");
    MODULE_AUTHOR("wit_yuan");
    MODULE_LICENSE("GPL");
    

    led_test.c代码如下:

    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    
    int main(int argc,char *argv[])
    {
        int fd;
        int val;
        fd = open("/dev/leds_device",O_RDWR);
        if(fd < 0){
            printf("---open file error----\r\n");
            return -1;
        }
    
        if(argc != 2){
            printf("usage %s on|off\r\n",argv[0]);
            return -1;
        }
    
        if(strncmp(argv[1],"on",2) == 0){
            val = 1;
            write(fd,&val,1);
        }
        else
        {
            val = 0;
            write(fd,&val,1);
        }
        return 0;
    }
    

    Makefile代码如下:

    ifeq ($(KERNELRELEASE),)
        KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
        PWD=$(shell pwd)
    
    modules:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
        arm-linux-gnueabihf-gcc -o gpio gpio.c
    
    modules_install:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
    clean:
        rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd
    else
        obj-m:=gpio.o
    
    endif
    

    测试:

    root@marsboard:~# ./led_test on
    ---script.bin led get ok,value:1----
    ----led val:1----
    ---led close----
    
    root@marsboard:~# ./led_test off
    ---script.bin led get ok,value:1----
    ----led val:0----
    ----led close----
    

    3.2 多LED灯控制

    夜深了,睡觉,待续吧。。。

    3.3 使用cdev注册设备

    3.4 另外一种控制方式:

    sys_config.fex下的GPIO内容信息如下:

    GPIO控制表

    如果需要控制gpio_pin_2,则:

    root@marsboard:~# echo 2 > /sys/class/gpio/export 
    root@marsboard:~# 
    root@marsboard:~# ls /sys/class/gpio/            
    export  gpio1_ph9  gpio2_pi15  gpiochip1  unexport
    root@marsboard:~#  echo out > /sys/class/gpio/gpio2_pi15/direction
    root@marsboard:~# echo 1  > /sys/class/gpio/gpio2_pi15/value 
    root@marsboard:~# echo 0  > /sys/class/gpio/gpio2_pi15/value
    

    通过这种方式,就可以控制LED灯的亮灭。

    4 树莓派A20的键盘复位驱动程序

    该复位程序需要做到:复位引脚拉低至少20ms,再等400ms左右将引脚拉为高电平才可行。

    4.1 添加配置信息

    在MarsBoard-A20-Linux-SDK-V1.1/tools/sunxi-tools下找到sys_config.fex文件,然后添加内容:

    [gpio_whb_para]
    gpio_whb_used = 1
    gpio_whb_num = 1
    gpio_whb_pin_1 = port:PH6<1><default><default><1>
    gpio_whb_name_1 = "keypad_reset"
    

    如下图所示:

    添加配置信息

    接着:

    $ ./fex2bin sys_config.fex script.bin 
    

    在A20上:

    $ mount /dev/nanda /mnt
    

    然后将script.bin文件上传到A20的/mnt目录中。

    4.2 修改Kconfig和Makefile内容

    在linux-sunxi/drivers/misc/目录下,修改Kconfig和Makefile内容。
    先修改Kconfig文件内容:

    config GPOI_SUNXI_WHB
        tristate "GPIO Support for sunxi platform (whb add)"
        depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)
        help
          This option enables support for gpio connected
          lines on the Allwinner SOCs (sun4i/sun5i).
          The gpios must be defined in [gpio_whb_para] section of sysconfig.fex
    

    然后修改Makefile文件内容:

    obj-$(CONFIG_GPOI_SUNXI_WHB)        += gpio_sunxi_whb.o
    

    接着:

    $ make menuconfig
    

    可以看到如下图所示内容:

    选中后效果图

    验证是否已经在内核中是选中状态,可在linux-sunxi根目录下查看.config文件,如下图所示:

    宏已经选中

    4.3 编写驱动程序

    命名驱动程序名称为:gpio_sunxi_whb.c,最终需要放到目录:linux-sunxi/drivers/misc/下。

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/device.h>
    #include <linux/fs.h>
    #include <plat/sys_config.h>
    #include <linux/miscdevice.h>
    #include <linux/uaccess.h>
    #include <linux/slab.h>
    
    #define GPIO_WHB_DEBUG
    
    #ifdef GPIO_WHB_DEBUG
    #define gpio_whb_debug(fmt, ...) printk(KERN_INFO "[GPIO_WHB][BUG][%d]"fmt, __LINE__, ##__VA_ARGS__)
    #else
    #define gpio_whb_debug(fmt, ...)
    #endif /* KEYBOARD_I2C7290_DEBUG */
    
    #define gpio_whb_error(fmt, ...) printk(KERN_INFO "[GPIO_WHB][ERR]"fmt"\n", ##__VA_ARGS__)
    
    #define DRV_NAME "gpio_whb"
    
    #define GPIO_WHB_IOCTL_GET  0
    #define GPIO_WHB_IOCTL_SET  1
    
    struct sunxi_gpio_whb_data {
        unsigned gpio_handler;
        script_gpio_set_t info;
        char pin_name[16];
        char gpio_name[64];
    };
    
    struct gpio_whb_ioctl_parameter {
        int gpio;
        int val;
    };
    
    static int gpio_whb_num = 0;
    static struct sunxi_gpio_whb_data *psunxi_gpios_whb = NULL;
    static struct miscdevice gpio_whb_misc = {0};
    
    /* Get gpio pin value */
    static int sunxi_gpio_get_value(unsigned gpio)
    {
        int  ret;
        user_gpio_set_t gpio_info[1];
    
        if (gpio >= gpio_whb_num)
            return -1;
    
        ret = gpio_get_one_pin_status(psunxi_gpios_whb[gpio].gpio_handler,
                    gpio_info, psunxi_gpios_whb[gpio].pin_name, 1);
    
        return gpio_info->data;
    }
    
    /* Set pin value (output mode) */
    static void sunxi_gpio_set_value(unsigned gpio, int value)
    {
        int ret ;
    
        if (gpio >= gpio_whb_num)
            return;
    
        gpio_whb_debug("SET:pin_name:%s, value:%d", psunxi_gpios_whb[gpio].pin_name, value);
        ret = gpio_write_one_pin_value(psunxi_gpios_whb[gpio].gpio_handler,
                        value, psunxi_gpios_whb[gpio].pin_name);
    
        return;
    }
    
    /* Set pin direction -> out (mul_sel = 1), pin_data -> value */
    static int sunxi_direction_output(unsigned gpio, int value)
    {
        int ret;
    
        if (gpio >= gpio_whb_num)
            return -1;
    
        ret =  gpio_set_one_pin_io_status(psunxi_gpios_whb[gpio].gpio_handler, 1,
                        psunxi_gpios_whb[gpio].pin_name);
        if (!ret)
            ret = gpio_write_one_pin_value(psunxi_gpios_whb[gpio].gpio_handler,
                        value, psunxi_gpios_whb[gpio].pin_name);
    
        return ret;
    }
    
    /* Check if gpio num requested and valid */
    static int sunxi_gpio_is_valid(unsigned gpio)
    {
        if (gpio >= gpio_whb_num)
            return -1;
    
        if (psunxi_gpios_whb[gpio].gpio_handler)
            return 0;
    
        return -1;
    }
    
    
    static int gpio_whb_open(struct inode *inode, struct file *file)
    {
        int i = 0;
    
        //所有IO设置为输出并置1
        /*
        for (i = 0; i < gpio_whb_num; ++i) {
            sunxi_direction_output(i, 1);
        }
        */
        for (i = 0; i < gpio_whb_num; ++i) {
            gpio_whb_debug("Open: Set GPIO %d is 1\n", i);
            sunxi_gpio_set_value(i, 1);
        }
    
        return 0;
    }
    
    static int gpio_whb_close(struct inode *inode, struct file *file)
    {
        int i = 0;
    
        //所有IO置1(因为键盘复位时是低电平,所以关闭时需要置1)
        for (i = 0; i < gpio_whb_num; ++i) {
            gpio_whb_debug("Close: Set GPIO %d is 1\n", i);
            sunxi_gpio_set_value(i, 1);
        }
    
    
        return 0;
    }
    
    /*
    static ssize_t gpio_whb_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
    {
        return 0;
    }
    */
    
    static long gpio_whb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        struct gpio_whb_ioctl_parameter para = {0};
    
        copy_from_user(&para, (void *)arg, sizeof(struct gpio_whb_ioctl_parameter));
        gpio_whb_debug("Cmd: %d, GPIO: %d\n", cmd, para.gpio);
    
        if (sunxi_gpio_is_valid(para.gpio) != 0) {
            gpio_whb_error("gpio invalid");
    
            return -1;
        }
    
        switch (cmd) {
        case GPIO_WHB_IOCTL_GET:
            para.val = sunxi_gpio_get_value(para.gpio);
            copy_to_user((void *)arg, &para, sizeof(struct gpio_whb_ioctl_parameter));
            break;
        case GPIO_WHB_IOCTL_SET:
            sunxi_gpio_set_value(para.gpio, para.val);
            break;
        default:
            return -1;
        }
    
        return 0;
    }
    
    static const struct file_operations gpio_whb_fops = {
        .owner              = THIS_MODULE,
        .open               = gpio_whb_open,
        .release            = gpio_whb_close,
        .unlocked_ioctl     = gpio_whb_ioctl,
    //  .read      = gpio_whb_read,
    //  .write     = gpio_whb_write,
    };
    
    static int __init sunxi_gpio_whb_init(void)
    {
        int i, err;
        int gpio_whb_used = 0;
        struct sunxi_gpio_whb_data *gpio_whb_i;
        char key[20];
        struct miscdevice *misc = NULL;
    
        /* parse script.bin for [leds_para] section
           leds_used/leds_num/leds_pin_x/leds_name_x */
    
        gpio_whb_debug("gpio_whb driver init\n");
        err = script_parser_fetch("gpio_whb_para", "gpio_whb_used", &gpio_whb_used, sizeof(gpio_whb_used)/sizeof(int));
        if (err) {
            /* Not error - just info */
            gpio_whb_error("sunxi gpio_whb can't find script data '[gpio_whb_para]' 'gpio_whb_used'\n");
    
            return err;
        }
    
        if (!gpio_whb_used) {
            gpio_whb_error("gpio_whb_used is false. Skip leds initialization\n");
            err = 0;
            return err;
        }
    
        err = script_parser_fetch("gpio_whb_para", "gpio_whb_num", &gpio_whb_num, sizeof(gpio_whb_num)/sizeof(int));
        if (err) {
            gpio_whb_error("script_parser_fetch '[gpio_whb_para]' 'gpio_whb_num' error\n");
            return err;
        }
    
        if (!gpio_whb_num) {
            gpio_whb_error("gpio_whb_num is none. Skip leds initialization\n");
            err = 0;
            return err;
        }
    
        gpio_whb_debug("gpio_whb_num = %d", gpio_whb_num);
        
    
        /* allocate memory for leds gpio data and platform device data */
        psunxi_gpios_whb = kzalloc(sizeof(struct sunxi_gpio_whb_data) * gpio_whb_num, GFP_KERNEL);
        if (!psunxi_gpios_whb) {
            gpio_whb_error("%s kzalloc failed\n", __func__);
            err = -ENOMEM;
            goto exit;
        }
    
        gpio_whb_i = psunxi_gpios_whb;
    
        /* parse leds gpio/name script data */
        for (i = 0; i < gpio_whb_num; i++) {
    
            /* make next script entry name */
            sprintf(gpio_whb_i->pin_name, "gpio_whb_pin_%d", i+1);
    
            /* fetch next led name */
            sprintf(key, "gpio_whb_name_%d", i + 1);
            err = script_parser_fetch("gpio_whb_para", key,
                          (int *)gpio_whb_i->gpio_name,
                          sizeof(gpio_whb_i->gpio_name)/sizeof(int));
            if (err) {
                gpio_whb_error("script_parser_fetch '[gpio_whb_para]' '%s' error\n", key);
                goto exit;
            }
    
            /* fetch next led gpio information */
            sprintf(key, "gpio_whb_pin_%d", i + 1);
            err = script_parser_fetch("gpio_whb_para", key,
                        (int *)&gpio_whb_i->info,
                        sizeof(script_gpio_set_t));
    
            if (err) {
                gpio_whb_error("script_parser_fetch '[gpio_whb_para]' '%s' error\n", key);
                break;
            }
    
            gpio_whb_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_whb_i->info.gpio_name, gpio_whb_i->info.port, gpio_whb_i->info.port_num);
    
            /* reserve gpio for led */
            gpio_whb_i->gpio_handler = gpio_request_ex("gpio_whb_para", key);
            if (!gpio_whb_i->gpio_handler) {
                gpio_whb_error("can't request '[gpio_whb_para]' '%s', already used ?", key);
                break;
            }
    
            gpio_whb_i++;
        }
    
        misc = &gpio_whb_misc;
    
        misc->minor = MISC_DYNAMIC_MINOR;
        misc->name = DRV_NAME;
        misc->fops = &gpio_whb_fops;
        err = misc_register(misc);
        if (err) {
            gpio_whb_error("Unable to register a misc device\n");
    
            goto exit;
        }
        gpio_whb_debug("Register a misc device Ok\n");
    
        return 0;
    
    exit:
        if (err != -ENOMEM) {
            for (i = 0; i < gpio_whb_num; i++) {
                if (psunxi_gpios_whb[i].gpio_handler)
                    gpio_release(psunxi_gpios_whb[i].gpio_handler, 1);
            }
    
            kfree(psunxi_gpios_whb);
            
            return err;
        }
    
        return err;
    }
    
    static void __exit sunxi_gpio_whb_exit(void)
    {
        int i = 0;
        struct miscdevice *misc = NULL;
    
        misc = &gpio_whb_misc;
        misc_deregister(misc);
        
        for (i = 0; i < gpio_whb_num; i++) {
            if (psunxi_gpios_whb[i].gpio_handler)
                gpio_release(psunxi_gpios_whb[i].gpio_handler, 1);
        }
    
        kfree(psunxi_gpios_whb);
        
        return;
        
    }
    
    module_init(sunxi_gpio_whb_init);
    module_exit(sunxi_gpio_whb_exit);
    
    MODULE_ALIAS("platform:gpio_whb-sunxi");
    MODULE_DESCRIPTION("sunxi gpio(whb) driver");
    MODULE_AUTHOR("wang hb <xxx@xxx.com>");
    MODULE_LICENSE("GPL");
    

    4.4 验证驱动加载是否成功

    在树莓派A20下,使用如下命令:

    root@marsboard:~# ll /dev/gpio_whb
    crw------- 1 root root 10, 59 Jan  1 00:46 /dev/gpio_whb
    root@marsboard:~#
    

    这说明驱动确实加载进去了。

    5 SPI使用GPIO切换选择

    将代码命名为spi-gpio.c,如果在编入内核,可以存放在如上所示,也就是linux-sunxi/drivers/misc/spi-gpio.c,如下所示:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/device.h>
    #include <linux/fs.h>
    #include <plat/sys_config.h>
    #include <linux/miscdevice.h>
    #include <linux/uaccess.h>
    #include <linux/slab.h>
    
    #define GPIO_DEBUG
    
    #ifdef GPIO_DEBUG
    #define gpio_debug(fmt, ...) printk(KERN_INFO "[%s][BUG][%d]"fmt, __FILE__,__LINE__, ##__VA_ARGS__)
    #else
    #define gpio_debug(fmt, ...)
    #endif  
    
    #define gpio_error(fmt, ...) printk(KERN_INFO "[%s][ERR]"fmt"\n", __FILE__,##__VA_ARGS__)
    
    #define DRV_NAME "A20_GPIO"
    
    #define GPIO_IOCTL_GET  0
    #define GPIO_IOCTL_SET  1
    
    struct sunxi_gpio_data {
      unsigned gpio_handler;
      script_gpio_set_t info;
      char pin_name[16];
      char gpio_name[64];
    };
    
    struct gpio_ioctl_parameter {
      int gpio;
      int val;
    };
    
    static int gpio_num = 0;
    static struct sunxi_gpio_data *psunxi_gpio = NULL;
    static struct miscdevice gpio_misc = {0};
    
    /* Set pin value (output mode) */
    static void sunxi_gpio_set_value(unsigned gpio, int value)
    {
      int ret ;
      if (gpio >= gpio_num)
          return;
      gpio_debug("SET:pin_name:%s, value:%d", psunxi_gpio[gpio].pin_name, value);
      ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,
                      value, psunxi_gpio[gpio].pin_name);
      return;
    }
    
    /* Set pin direction -> out (mul_sel = 1), pin_data -> value */
    static int sunxi_direction_output(unsigned gpio, int value)
    {
      int ret;
    
      if (gpio >= gpio_num)
          return -1;
      ret =  gpio_set_one_pin_io_status(psunxi_gpio[gpio].gpio_handler, 1,
                      psunxi_gpio[gpio].pin_name);
      if (!ret)
          ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,
                      value, psunxi_gpio[gpio].pin_name);
    
      return ret;
    }
    
    static int gpio_open(struct inode *inode, struct file *file)
    {
      int i = 0;
      for (i = 0; i < gpio_num; ++i) {
          gpio_debug("Open: Set GPIO %d is 1\n", i);
          sunxi_gpio_set_value(i, 1);
      }
      return 0;
    }
    
    static ssize_t gpio_write (struct file *filep, const char __user *buf, size_t len, loff_t *off)
    {
      unsigned char value;
      int ret;
      if(len != 1){
          printk("---len error-------\r\n");
          return -1;
      }
      ret = copy_from_user(&value,buf,len);
      switch(value){
          case 0:
              sunxi_direction_output(0,0);
              sunxi_direction_output(1,0);
              sunxi_direction_output(2,0);
          break;
          case 1:
              sunxi_direction_output(0,1);
              sunxi_direction_output(1,0);
              sunxi_direction_output(2,0);
          break;
          case 2:
              sunxi_direction_output(0,0);
              sunxi_direction_output(1,1);
              sunxi_direction_output(2,0);
          break;
          case 3:
              sunxi_direction_output(0,1);
              sunxi_direction_output(1,1);
              sunxi_direction_output(2,0);
          break;
          case 4:
              sunxi_direction_output(0,0);
              sunxi_direction_output(1,0);
              sunxi_direction_output(2,1);
          break;
          case 5:
              sunxi_direction_output(0,1);
              sunxi_direction_output(1,0);
              sunxi_direction_output(2,1);
          break;
          case 6:
              sunxi_direction_output(0,0);
              sunxi_direction_output(1,1);
              sunxi_direction_output(2,1);
          break;
          case 7:
              sunxi_direction_output(0,1);
              sunxi_direction_output(1,1);
              sunxi_direction_output(2,1);
          break;
          default:
              printk("%s,value error\r\n",__FILE__);
          break;
      }
      return len; 
    }
    
    static int gpio_close(struct inode *inode, struct file *file)
    {
      int i = 0;
      for (i = 0; i < gpio_num; ++i) {
          gpio_debug("Close: Set GPIO %d is 1\n", i);
          sunxi_gpio_set_value(i, 1);
      }
      return 0;
    }
    
    static const struct file_operations A20_gpio_fops = {
      .owner              = THIS_MODULE,
      .open                = gpio_open,
      .release             = gpio_close,
      .write                = gpio_write,
    };
    
    static int __init sunxi_gpio_init(void)
    {
      int i, err;
      int gpio_used = 0;
      struct sunxi_gpio_data *gpio_i;
      struct miscdevice *misc = NULL;
    
      gpio_debug("gpio driver init\n");
      err = script_parser_fetch("A20_SPI_GPIO_para", "gpio_used", &gpio_used, sizeof(gpio_used)/sizeof(int));
      if (err) {
          /* Not error - just info */
          gpio_error("sunxi gpio can't find script data '[gpio_para]' 'gpio_used'\n");
    
          return err;
      }
    
      if (!gpio_used) {
          gpio_error("gpio_used is false. Skip leds initialization\n");
          err = 0;
          return err;
      }
    
      err = script_parser_fetch("A20_SPI_GPIO_para", "gpio_num", &gpio_num, sizeof(gpio_num)/sizeof(int));
      if (err) {
          gpio_error("script_parser_fetch '[gpio_whb_para]' 'gpio_whb_num' error\n");
          return err;
      }
    
      if (3!=gpio_num) {
          gpio_error("gpio_num is not 3. Skip gpio initialization\n");
          err = 0;
          return err;
      }
    
      gpio_debug("gpio_num = %d", gpio_num);
      
    
      /* allocate memory for leds gpio data and platform device data */
      psunxi_gpio = kzalloc(sizeof(struct sunxi_gpio_data) * gpio_num, GFP_KERNEL);
      if (!psunxi_gpio) {
          gpio_error("%s kzalloc failed\n", __func__);
          err = -ENOMEM;
          goto exit;
      }
      gpio_i = psunxi_gpio;
      /* parse leds gpio/name script data */
      for (i = 0; i < gpio_num; i++) {
          /* make next script entry name */
          sprintf(gpio_i->pin_name, "A20_GPIO%d", i+1);
          err = script_parser_fetch("A20_SPI_GPIO_para", gpio_i->pin_name,
                        (int *)gpio_i->gpio_name,
                        sizeof(gpio_i->gpio_name)/sizeof(int));
          if (err) {
              gpio_error("script_parser_fetch '[A20_SPI_GPIO_para]' '%s' error\n", gpio_i->pin_name);
              goto exit;
          }
          gpio_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_i->info.gpio_name, gpio_i->info.port, gpio_i->info.port_num);
          /* reserve gpio for led */
          gpio_i->gpio_handler = gpio_request_ex("A20_SPI_GPIO_para", gpio_i->pin_name);
          if (!gpio_i->gpio_handler) {
              gpio_error("can't request '[gpio_whb_para]' '%s', already used", gpio_i->pin_name);
              break;
          }
          gpio_i++;
      }
    
      for (i = 0; i < gpio_num; ++i) {
          sunxi_gpio_set_value(i, 1);
      }
      
      misc = &gpio_misc;
      misc->minor = MISC_DYNAMIC_MINOR;
      misc->name = DRV_NAME;
      misc->fops   = &A20_gpio_fops;
      err = misc_register(misc);
      if (err) {
          gpio_error("Unable to register a misc device\n");
          goto exit;
      }
      gpio_debug("Register a misc device Ok\n");
      return 0;
    exit:
      if (err != -ENOMEM) {
          for (i = 0; i < gpio_num; i++) {
              if (psunxi_gpio[i].gpio_handler)
                  gpio_release(psunxi_gpio[i].gpio_handler, 1);
          }
          kfree(psunxi_gpio);  
          return err;
      }
      return err;
    }
    
    static void __exit sunxi_gpio_exit(void)
    {
      int i = 0;
      struct miscdevice *misc = NULL;
    
      misc = &gpio_misc;
      misc_deregister(misc);
      
      for (i = 0; i < gpio_num; i++) {
          if (psunxi_gpio[i].gpio_handler)
              gpio_release(psunxi_gpio[i].gpio_handler, 1);
      }
      kfree(psunxi_gpio);
      return;
    }
    module_init(sunxi_gpio_init);
    module_exit(sunxi_gpio_exit);
    MODULE_ALIAS("platform:spi_fpga_dsp_gpio_wityuan-sunxi");
    MODULE_DESCRIPTION("sunxi spi_fpga_gpio(wityuan) driver");
    MODULE_AUTHOR("wityuan <xxx@xxx.com>");
    MODULE_LICENSE("GPL");
    

    Makefile文件为:

    ifeq ($(KERNELRELEASE),)
        KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.2/linux-sunxi
        PWD=$(shell pwd)
    modules:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
        arm-linux-gnueabihf-gcc -o spi-gpio spi-gpio.c
    
    modules_install:
        $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
    clean:
        rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd
    else
        obj-m:=spi-gpio.o
    endif
    

    测试程序命名为spi-gpio_test.c,内容如下:

    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    int main(int argc,char *argv[])
    {
        int fd;
        int val;
        unsigned char write_val;
        fd = open("/dev/A20_GPIO",O_RDWR);
        if(fd < 0){
            printf("---open file error----\r\n");
            return -1;
        }
    
        if(argc != 2){
            printf("usage %s (0|1|2|3|4|5|6|7|)\r\n",argv[0]);
            return -1;
        }
    
        write_val = (unsigned char)atoi(argv[1]);
    
        if((write_val>7) | (write_val<0)){
            printf("---value is valid---\r\n");
            return -1;
        }
        
        write(fd,&write_val,1);
    
        while(1);
        
        return 0;
    }
    

    即可通过如下方式进行测试:

    $ ./spi-gpio_test 0
    $ ./spi-gpio_test 1
    $ ./spi-gpio_test 2
    $ ./spi-gpio_test 3
    $ ./spi-gpio_test 4
    $ ./spi-gpio_test 5
    $ ./spi-gpio_test 6
    $ ./spi-gpio_test 7
    

    最终,我们往内核添加时,要将spi-gpio.c放到linux-sunxi/drivers/misc下。
    并且在Kconfig和Makefile中添加内容:
    Kconfig:

    config SPI_GPIO_YUAN
        tristate "(wit_yuan add)SPI GPIO SELECT Support for sunxi platform"
        depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)
        help
          This option enables support for gpio connected
          lines on the Allwinner SOCs (sun4i/sun5i).
          The gpios must be defined in [A20_SPI_GPIO_para] section of sysconfig.fex
    

    Makefile:

    obj-$(CONFIG_SPI_GPIO_YUAN)        += spi-gpio.o
    

    sys_config.fex配置为:

    [A20_SPI_GPIO_para]
    gpio_used =  1
    gpio_num       =  3
    A20_GPIO1   =  port:PH0<1><1><default><default> 
    A20_GPIO2   =  port:PH1<1><1><default><default> 
    A20_GPIO3   =  port:PH1<1><1><default><default> 
    

    最终,查找是否有设备:

    $ ls /dev/A20_GPIO
    

    6 AD,RCA,MIC等使用IO脉冲切换

    AD引脚配置
    [AD_Switch_para]
    AD_Switch_used =  1
    gpio_num       =  2
    sw1            =  port:PH0<1><1><default><default> 
    sw2            =  port:PH1<1><1><default><default> 
    

    接着,在linux-sunxi/drivers/misc/目录下,修改Kconfig和Makefile内容。
    先修改Kconfig文件内容:

    config A20_sw
        tristate "A20_sw for sunxi platform (wityuan add)"
        depends on (ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I)
        help
          This option enables support for gpio connected
          lines on the Allwinner SOCs (sun4i/sun5i).
          The gpios must be defined in section of sysconfig.fex
    

    然后修改Makefile文件内容:

    obj-$(CONFIG_A20_sw)        += A20_sw.o
    

    接着:

    $ make menuconfig
    

    找到该选项并且选中:

    选中A20_sw

    最后,我们需要将驱动程序,名称为:gpio_sunxi_whb.c,放到目录:linux-sunxi/drivers/misc/下。
    源码如下:

    #include <linux/module.h>
    
    #include <linux/init.h>
    
    #include <linux/kernel.h>
    
    #include <linux/device.h>
    
    #include <linux/fs.h>
    
    #include <plat/sys_config.h>
    
    #include <linux/miscdevice.h>
    
    #include <linux/uaccess.h>
    
    #include <linux/slab.h>
    
    
    
    #define GPIO_DEBUG
    
    
    
    #ifdef GPIO_DEBUG
    
    #define gpio_debug(fmt, ...) printk(KERN_INFO "[%s][BUG][%d]"fmt, __FILE__,__LINE__, ##__VA_ARGS__)
    
    #else
    
    #define gpio_debug(fmt, ...)
    
    #endif  
    
    
    
    #define gpio_error(fmt, ...) printk(KERN_INFO "[%s][ERR]"fmt"\n", __FILE__,##__VA_ARGS__)
    
    
    
    #define DRV_NAME "A20_SW"
    
    
    
    struct sunxi_gpio_data {
    
      unsigned gpio_handler;
    
      script_gpio_set_t info;
    
      char pin_name[16];
    
      char gpio_name[64];
    
    };
    
    
    
    
    
    typedef struct{
    
        int id;
    
        int value;
    
    }SW_T;
    
    
    
    
    
    
    
    static int gpio_num = 0;
    
    static struct sunxi_gpio_data *psunxi_gpio = NULL;
    
    static struct miscdevice gpio_misc = {0};
    
    
    
    /* Set pin value (output mode) */
    
    static void sunxi_gpio_set_value(unsigned gpio, int value)
    
    {
    
      int ret ;
    
      if (gpio >= gpio_num)
    
          return;
    
      gpio_debug("SET:pin_name:%s, value:%d", psunxi_gpio[gpio].pin_name, value);
    
      ret = gpio_write_one_pin_value(psunxi_gpio[gpio].gpio_handler,
    
                      value, psunxi_gpio[gpio].pin_name);
    
      return;
    
    }
    
    
    
    
    
    static int gpio_open(struct inode *inode, struct file *file)
    
    {
    
      int i = 0;
    
      for (i = 0; i < gpio_num; ++i) {
    
          gpio_debug("Open: Set GPIO %d is 1\n", i);
    
          sunxi_gpio_set_value(i, 0);
    
      }
    
      return 0;
    
    }
    
    
    
    static ssize_t gpio_write (struct file *filep, const char __user *buf, size_t len, loff_t *off)
    
    {
    
      SW_T sw_t;
    
      int ret;
    
      if(len != (sizeof(sw_t))){
    
          printk("---len error-------\r\n");
    
          return -1;
    
      }
    
      ret = copy_from_user(&sw_t,buf,len);
    
    
    
      printk("---sw_t.id:%d, sw_t.value:%d-------\r\n",sw_t.id,sw_t.value);
      
      if(sw_t.id == 0){
            sunxi_gpio_set_value(0, sw_t.value);
            sunxi_gpio_set_value(1, 0);
      }
      else {
            sunxi_gpio_set_value(0, 0);
            sunxi_gpio_set_value(1, sw_t.value);    
      }
        
    
     
    
      return len; 
    
    }
    
    
    
    static int gpio_close(struct inode *inode, struct file *file)
    
    {
    
    
    
    #if 0
    
      int i = 0;
    
      for (i = 0; i < gpio_num; ++i) {
    
          gpio_debug("Close: Set GPIO %d is 1\n", i);
    
          sunxi_gpio_set_value(i, 0);
    
      }
    
    #endif
    
    
    
      return 0;
    
    }
    
    
    
    static const struct file_operations A20_gpio_fops = {
    
      .owner              = THIS_MODULE,
    
      .open                = gpio_open,
    
      .release             = gpio_close,
    
      .write                = gpio_write,
    
    };
    
    
    
    static int __init sunxi_gpio_init(void)
    
    {
    
      int i, err;
    
      int gpio_used = 0;
    
      struct sunxi_gpio_data *gpio_i;
    
      struct miscdevice *misc = NULL;
    
    
    
      gpio_debug("gpio driver init\n");
    
      err = script_parser_fetch("AD_Switch_para", "AD_Switch_used", &gpio_used, sizeof(gpio_used)/sizeof(int));
    
      if (err) {
    
          /* Not error - just info */
    
          gpio_error("sunxi gpio can't find script data '[gpio_para]' 'gpio_used'\n");
    
    
    
          return err;
    
      }
    
    
    
      if (!gpio_used) {
    
          gpio_error("gpio_used is false. Skip leds initialization\n");
    
          err = 0;
    
          return err;
    
      }
    
    
    
      err = script_parser_fetch("AD_Switch_para", "gpio_num", &gpio_num, sizeof(gpio_num)/sizeof(int));
    
      if (err) {
    
          gpio_error("script_parser_fetch '[AD_Switch_para]' error\n");
    
          return err;
    
      }
    
    
    
      if (2!=gpio_num) {
    
          gpio_error("gpio_num is not 2. Skip gpio initialization\n");
    
          err = 0;
    
          return err;
    
      }
    
    
    
      gpio_debug("gpio_num = %d", gpio_num);
    
      /* allocate memory for leds gpio data and platform device data */
    
      psunxi_gpio = kzalloc(sizeof(struct sunxi_gpio_data) * gpio_num, GFP_KERNEL);
    
      if (!psunxi_gpio) {
    
          gpio_error("%s kzalloc failed\n", __func__);
    
          err = -ENOMEM;
    
          goto exit;
    
      }
    
      gpio_i = psunxi_gpio;
    
      /* parse leds gpio/name script data */
    
      for (i = 0; i < gpio_num; i++) {
    
          /* make next script entry name */
    
          sprintf(gpio_i->pin_name, "sw%d", i+1);
    
          err = script_parser_fetch("AD_Switch_para", gpio_i->pin_name,
    
                        (int *)gpio_i->gpio_name,
    
                        sizeof(gpio_i->gpio_name)/sizeof(int));
    
          if (err) {
    
              gpio_error("script_parser_fetch '[AD_Switch_para]' '%s' error\n", gpio_i->pin_name);
    
              goto exit;
    
          }
    
          gpio_debug("gpio_name:%s, port:%d,port_num:%d\n", gpio_i->info.gpio_name, gpio_i->info.port, gpio_i->info.port_num);
    
          /* reserve gpio for led */
    
          gpio_i->gpio_handler = gpio_request_ex("AD_Switch_para", gpio_i->pin_name);
    
          if (!gpio_i->gpio_handler) {
    
              gpio_error("can't request '[AD_Switch_para]' '%s', already used", gpio_i->pin_name);
    
              break;
    
          }
    
          gpio_i++;
    
      }
    
    
    
      for (i = 0; i < gpio_num; ++i) {
    
          sunxi_gpio_set_value(i, 0);
    
      }
    
      
    
      misc = &gpio_misc;
    
      misc->minor = MISC_DYNAMIC_MINOR;
    
      misc->name = DRV_NAME;
    
      misc->fops   = &A20_gpio_fops;
    
      err = misc_register(misc);
    
      if (err) {
    
          gpio_error("Unable to register a misc device\n");
    
          goto exit;
    
      }
    
      gpio_debug("Register a misc device Ok\n");
    
      return 0;
    
    exit:
    
      if (err != -ENOMEM) {
    
          for (i = 0; i < gpio_num; i++) {
    
              if (psunxi_gpio[i].gpio_handler)
    
                  gpio_release(psunxi_gpio[i].gpio_handler, 1);
    
          }
    
          kfree(psunxi_gpio);  
    
          return err;
    
      }
    
      return err;
    
    }
    
    
    
    static void __exit sunxi_gpio_exit(void)
    
    {
    
      int i = 0;
    
      struct miscdevice *misc = NULL;
    
    
    
      misc = &gpio_misc;
    
      misc_deregister(misc);
    
      
    
      for (i = 0; i < gpio_num; i++) {
    
          if (psunxi_gpio[i].gpio_handler)
    
              gpio_release(psunxi_gpio[i].gpio_handler, 1);
    
      }
    
      kfree(psunxi_gpio);
    
      return;
    
    }
    
    module_init(sunxi_gpio_init);
    
    module_exit(sunxi_gpio_exit);
    
    MODULE_ALIAS("platform:sw0,sw1 select");
    
    MODULE_DESCRIPTION("sw0,sw1(wityuan) driver");
    
    MODULE_AUTHOR("wityuan <xxx@xxx.com>");
    
    MODULE_LICENSE("GPL");
    

    测试程序sw_test.c为:

    #include "stdio.h"
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    typedef struct{
    
        int id;
    
        int value;
    
    }SW_T;
    
    
    int main(int argc,char *argv[])
    {
        int fd;
        int val;
        SW_T sw_t;
        fd = open("/dev/A20_SW",O_RDWR);
        if(fd < 0){
            printf("---open file error----\r\n");
            return -1;
        }
    
        if(argc != 2){
            printf("usage %s on|off\r\n",argv[0]);
            return -1;
        }
    
        val = atoi(argv[1]);
        
        sw_t.id = val;
        sw_t.value = 1;
        write(fd,&sw_t,sizeof(SW_T));
        //sleep(1);
        usleep(200000);
            
        sw_t.id = val;
        sw_t.value = 0;
        write(fd,&sw_t,sizeof(SW_T));
        usleep(200000);
        while(1);
        return 0;
    }
    

    7 参考文件

    1.\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\drivers\leds\leds-sunxi.c
    2.\marsboard\marsboard-a20-linux-sdk-v1.2\linux-sunxi\gpio\gpio-sunxi.c

    相关文章

      网友评论

          本文标题:三 . 树莓派A20 GPIO驱动程序编写

          本文链接:https://www.haomeiwen.com/subject/wintkxtx.html