三 . 樹莓派A20 GPIO驅(qū)動程序編寫


1 硬件結(jié)構(gòu)

我使用的是風(fēng)火輪出品的樹莓派A20板子,同樣也買了一個DVK521擴(kuò)展的底板,可以看到有8個LED燈,關(guān)于GPIO,點(diǎn)亮LED是最好的驅(qū)動編寫例子。

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

LED燈原理圖

LED燈原理比較簡單,安裝該圖來看,需要將PE4~PE11置高即可,從這里看來,A20的IO口輸出電流能力還是比較強(qiáng)。

2 驅(qū)動編寫

2.1 GPIO寄存器的相關(guān)知識

按照以前S3C2440,S3C6410等板子,找到寄存器很簡單,只需要翻遍手冊,然后使用內(nèi)核提供的ioremap將硬件地址映射到內(nèi)核的虛擬地址上,即可達(dá)到通過控制虛擬地址達(dá)到控制硬件地址的效果。

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

在《樹莓派3主控芯片手冊V1.0(A20datasheet).pdf》(點(diǎn)擊鏈接)文檔的P19頁上,
A20將通用的GPIO當(dāng)成了PIO,我截取出PIO的地址范圍,如下圖所示:

PIO地址范圍

再跳到P240,找到1.19 Port Controller這一章,我們只需要關(guān)注的是Port E(PE):12 input/output port。

GPIO引腳

在這一章里,說明了一些知識點(diǎn):
1.上面列的GPIO引腳中,除了Port S做位DRAM來用,其他的沒有復(fù)用的功能的,都可以用作普通IO口。
2.A20上有32個外部PIO中斷。

由于是初次使用該款芯片,而GPIO是最基本的功能,所以,還是繼續(xù)貼出P247上的關(guān)于GPIO的配置寄存器內(nèi)容,方便后面的查找:

GPIO配置寄存器

GPIO數(shù)據(jù)寄存器等等

2.2 配置寄存器

我在MarsBoard-A20-Linux-SDK-V1.2源碼目錄下,找到了sun4i-keypad.c文件,在sw_keypad_init()中找到了API函數(shù)script_parser_fetch(),根據(jù)這個函數(shù),我在gpio-sunxi.c中找到了操作gpio的許多API函數(shù),例如sunxi_gpio_to_irq(),sunxi_gpio_set()等等。

最終,將所有的點(diǎn),可以掛接到一個結(jié)構(gòu)體上:struct gpio_chip。

到這里,對于GPIO的內(nèi)容,就到頭了,分析再多,那就變成了一篇驅(qū)動程序分析的文章了。

在gpio-sunxi.c的sunxi_gpio_probe()函數(shù)里,通過注釋,知道該函數(shù)中有在找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 找到開發(fā)板上的script.bin文件位置

在開發(fā)板上使用如下命令可以找到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. 腳本中的字符串區(qū)分大小寫,用戶可以修改"="后面的數(shù)值,但是不要修改前面的字符串
;   2. 新增主鍵和子鍵的名稱必須控制在32個字符以內(nèi),不包括32個
;   3. 所以的注釋以“;”開始,單獨(dú)占據(jù)一行
;   4. 注釋不可和配置項同行,例如:主鍵和子健后面不能添加任何形式的注釋
;
; gpio的描述形式:Port:端口+組內(nèi)序號<功能分配><內(nèi)部電阻狀態(tài)><驅(qū)動能力><輸出電平狀態(tài)>
;           例如:port:PA0<0><default><default><default>
;-------------------------------------------------------------------------------

現(xiàn)在在硬件上,有PE4~PE11這些管腳需要設(shè)置。

  1. 用什么參數(shù)怎么去設(shè)置一個IO口?

要去尋求這個問題的答案,內(nèi)核肯定會有相應(yīng)的依據(jù),我在script.h中找到了結(jié)構(gòu)體:
struct sunxi_property_gpio_value。

摘錄如下:

struct sunxi_property_gpio_value {
    u32 port;      /*表明是哪個端口,如Port A,Port B等等*/
    u32 port_num;  /*表明是哪個端口號,例如PE14的14*/
    s32 mul_sel;   /*表明的是功能分配,例如復(fù)用功能還是普通IO口*/
    s32 pull;      /*表明的是內(nèi)部上拉狀態(tài)*/
    s32 drv_level; /*表明的是驅(qū)動能力*/
    s32 data;      /*表明的是輸出電平狀態(tài)*/
};

每一項含義清楚了之后,我們就可以一個個的去設(shè)置了,那么,還是要回到手冊:《樹莓派3主控芯片手冊V1.0(A20datasheet).pdf》上的P249。

根據(jù)手冊上的說明:
mul_sel:
0--輸入
1--輸出
2~7--復(fù)用功能

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

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

data:
0--輸出電平為0
1--輸出電平為1

可以在這個網(wǎng)站上找到詳細(xì)描述:鏈接

現(xiàn)在,可以在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開發(fā)板上的script.bin替換掉。

2.2.4 獲取script.bin的參數(shù)

找到\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文件下載到開發(fā)板上,再開發(fā)板上執(zhí)行:

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 進(jìn)階

3.1 添加應(yīng)用層代碼

上面的驅(qū)動程序,只是在內(nèi)核驅(qū)動程序?qū)用婵刂朴布,F(xiàn)在我們需要通過應(yīng)用層程序來控制底層的硬件。

直接上代碼吧。

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燈控制

夜深了,睡覺,待續(xù)吧。。。

3.3 使用cdev注冊設(shè)備

3.4 另外一種控制方式:

sys_config.fex下的GPIO內(nèi)容信息如下:

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的鍵盤復(fù)位驅(qū)動程序

該復(fù)位程序需要做到:復(fù)位引腳拉低至少20ms,再等400ms左右將引腳拉為高電平才可行。

4.1 添加配置信息

在MarsBoard-A20-Linux-SDK-V1.1/tools/sunxi-tools下找到sys_config.fex文件,然后添加內(nèi)容:

[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內(nèi)容

在linux-sunxi/drivers/misc/目錄下,修改Kconfig和Makefile內(nèi)容。
先修改Kconfig文件內(nèi)容:

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文件內(nèi)容:

obj-$(CONFIG_GPOI_SUNXI_WHB)        += gpio_sunxi_whb.o

接著:

$ make menuconfig

可以看到如下圖所示內(nèi)容:

選中后效果圖

驗(yàn)證是否已經(jīng)在內(nèi)核中是選中狀態(tài),可在linux-sunxi根目錄下查看.config文件,如下圖所示:

宏已經(jīng)選中

4.3 編寫驅(qū)動程序

命名驅(qū)動程序名稱為: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設(shè)置為輸出并置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(因?yàn)殒I盤復(fù)位時是低電平,所以關(guān)閉時需要置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 驗(yàn)證驅(qū)動加載是否成功

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

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

這說明驅(qū)動確實(shí)加載進(jìn)去了。

5 SPI使用GPIO切換選擇

將代碼命名為spi-gpio.c,如果在編入內(nèi)核,可以存放在如上所示,也就是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,內(nèi)容如下:

#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;
}

即可通過如下方式進(jìn)行測試:

$ ./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

最終,我們往內(nèi)核添加時,要將spi-gpio.c放到linux-sunxi/drivers/misc下。
并且在Kconfig和Makefile中添加內(nèi)容:
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> 

最終,查找是否有設(shè)備:

$ 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內(nèi)容。
先修改Kconfig文件內(nèi)容:

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文件內(nèi)容:

obj-$(CONFIG_A20_sw)        += A20_sw.o

接著:

$ make menuconfig

找到該選項并且選中:

選中A20_sw

最后,我們需要將驅(qū)動程序,名稱為: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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內(nèi)容