古人智慧

Just Do it!
上士聞道,勤而行之;中士聞道,若存若亡;下士聞道,大笑之。不笑,不足以爲道。
~ 道德經 41

搜尋此網誌

Translation

2015年6月2日 星期二

[Banana Pro] Device driver study - LED control

There is my first device driver in Banana Pro board.

My idea is using this device driver module to control the LED board as below:


Here is the build process and source code:

Setup the build env:
Step 1. Download and rebuild the bsp 
git clone https://github.com/LeMaker/lemaker-bsp.git
refer to my another bolg - Banana Pro] Building u-boot, script.bin and linux-kernel


I strong suggest rebuild the BSP on PC, and replace the uboot, kernel and modules to SD card, this is an important process to make sure the version of driver match with kernel. Otherwise, you may got fail when you insert your driver to kernel by insmod command.

Step 2.  Copy the linux kernel tree from PC to Banana board(using scp)
PC side -> Banana side 
scp -r ~/lemarker-bsp/linux-sunxi/ regis@192.168.0.111:/home/regis/ddv/3.4.103/

Programming:
Source code: led_drv.c

/*
 * led_drv:
 *      LED device driver test program for banana Pro
 *      Copyright (c) 2015 Regis Hsu
 *
 *      Thanks to code samples from Gordon Henderson
 ***********************************************************************
 *
 *    led_drv is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    led_drv is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with wiringPi.
 *    If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <asm/io.h>


// BananaPro GPIO I/O memory address
#define GPIO_BASE (0x01C20800)

#define LED_DRV_MAJOR 31
/*
    IOCTL command
*/
#define LED_ON  1
#define LED_OFF 0

static volatile uint32_t *gpio ;

MODULE_LICENSE("Dual BSD/GPL");

/*add for BananaPro by LeMaker team*/ 
uint32_t readl_1(uint32_t addr)
{
        uint32_t val = 0;
        uint32_t mmap_base = (addr & ~MAP_MASK);
        uint32_t mmap_seek = ((addr - mmap_base) >> 2);
        val = ioread32(gpio + mmap_seek);
        printk("func:%s phyaddr:0x%x val:0x%x\n",__func__, gpio + mmap_seek, va$
        return val;
}

void writel_1(uint32_t val, uint32_t addr)
{
        uint32_t mmap_base = (addr & ~MAP_MASK);
        uint32_t mmap_seek = ((addr - mmap_base) >> 2);
        printk("func:%s phyaddr:0x%x val:0x%x\n",__func__, gpio + mmap_seek, va$
        iowrite32(val, gpio + mmap_seek); 
}

int get_gpio_mode(int pin)
{
        uint32_t regval = 0;
        int bank = pin >> 5;
        int index = pin - (bank << 5);
        int offset = ((index - ((index >> 3) << 3)) << 2);
        uint32_t reval=0;
        uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + ((index >> 3) << 2);
        printk("func:%s pin:%d,  bank:%d index:%d phyaddr:0x%x\n",__func__, pin$
        regval = readl_1(phyaddr);
        return reval;
}

void set_gpio_mode(int pin,int mode)
{
        uint32_t regval = 0;
        int bank = pin >> 5;
        int index = pin - (bank << 5);
        int offset = ((index - ((index >> 3) << 3)) << 2);
        uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + ((index >> 3) << 2);
        printk("func:%s pin:%d, MODE:%d bank:%d index:%d phyaddr:0x%x\n",__func$
        regval = readl_1(phyaddr);
        printk("read reg val: 0x%x offset:%d\n",regval,offset);
        if(INPUT == mode)
        {
                regval &= ~(7 << offset);
                writel_1(regval, phyaddr);
                // for debug
                regval = readl_1(phyaddr);
                printk("Input mode set over reg val: 0x%x\n",regval);
        }
        else if(OUTPUT == mode)
        {
                regval &= ~(7 << offset);
                regval |=  (1 << offset);
                printk("Output mode ready set val: 0x%x\n",regval);
                writel_1(regval, phyaddr);
                // for debug
                regval = readl_1(phyaddr);
                printk("Output mode set over reg val: 0x%x\n",regval);
        } 
        else 
        {
                printk("line:%dpin number error\n",__LINE__);
        }

        return ;
}

void digitalWrite(int pin, int value)
         uint32_t regval = 0;
         int bank = pin >> 5;
         int index = pin - (bank << 5);
         uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + 0x10; // +0x10 -> d$
        printk("func:%s pin:%d, value:%d bank:%d index:%d phyaddr:0x%x\n",__fun$
        regval = readl_1(phyaddr);

        printk("befor write reg val: 0x%x,index:%d\n",regval,index);
        if(0 == value)
        {
                regval &= ~(1 << index);
                writel_1(regval, phyaddr);
                // for debug
                regval = readl_1(phyaddr);
               printk("LOW val set over reg val: 0x%x\n",regval);
        }
        else
        {
                regval |= (1 << index);
                writel_1(regval, phyaddr);
                // for debug
                regval = readl_1(phyaddr);
                printk("HIGH val set over reg val: 0x%x\n",regval);
        }
}

int digitalRead(int pin)
        uint32_t regval = 0;
        int bank = pin >> 5;
        int index = pin - (bank << 5);
        uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + 0x10; // +0x10 -> da$
        printk("func:%s pin:%d,bank:%d index:%d phyaddr:0x%x\n",__func__, pin,b$
        regval = readl_1(phyaddr);
        regval = regval >> index;
        regval &= 1;
        printk("***** read reg val: 0x%x,bank:%d,index:%d,line:%d\n",regval,ban$
        return regval;
}

int led_open (struct inode *pnode, struct file *pfile)
{
        printk("enter %s!\n", __FUNCTION__);
        // remap the physical address to virtual address
        gpio = ioremap(GPIO_BASE, 2*1024);
        return 0;
}

ssize_t led_read(struct file *pfile, char __user *buffer, size_t lenght, loff_t$
{
        printk("enter %s!\n", __FUNCTION__);
        return 0;
}

ssize_t led_write(struct file *pfile, const char __user *buffer, size_t lenght,$
{
        printk("enter %s!\n", __FUNCTION__);
        return lenght;
}
long led_ioctl(struct file *pfile, unsigned int cmd, unsigned long data)
{
    switch (cmd)
    {
        case LED_ON :
            printk ("%s %d!\n", __FUNCTION__, cmd);
            set_gpio_mode(data, OUTPUT);
            digitalWrite (data, HIGH) ;
            break;
        case LED_OFF:
            printk ("%s %d!\n", __FUNCTION__, cmd);
            set_gpio_mode(data, OUTPUT);
            digitalWrite (data, LOW) ;
            break;
        default :
            printk ("%s CMD Error!\n", __FUNCTION__);
            break;
    }
        return 0;
}


static struct file_operations io_dev_fops = {
    .owner = THIS_MODULE,
    .read = led_read,
    .write = led_write,
    .open = led_open,
    .release = led_close,
    .unlocked_ioctl = led_ioctl,
};


int __init led_init(void)
{
        register_chrdev(LED_DRV_MAJOR, "led",&io_dev_fops);
        printk("enter %s!\n", __FUNCTION__);
        return 0;
}

void __exit led_exit(void)
{
        unregister_chrdev(LED_DRV_MAJOR, "led");
        printk("enter %s!\n", __FUNCTION__);
}

module_init(led_init);
module_exit(led_exit);


And, create a make file names "Makefile"
Makefile
obj-m := led_drv.o

It is almost done for the coding!!!
Let's start build the device driver module~~~

regis@lemaker:~/ddr/led$ make -C /home/regis/ddv/3.4.103 M=$PWD modules
make: Entering directory `/home/regis/ddv/3.4.103'
  Building modules, stage 2.
  MODPOST 1 modules
make: Leaving directory `/home/regis/ddv/3.4.103'
regis@lemaker:~/ddr/led$


ok, now, write a test program to test this device driver:

ddr_test.c

/*
 * ddr_test:
 *      LED device driver test program for banana Pro
 *      Copyright (c) 2015 Regis Hsu
 *
 *      Thanks to code samples from Gordon Henderson
 ***********************************************************************
 *
 *    led_drv is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    led_drv is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with wiringPi.
 *    If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

#define DEVICE_NAME "/dev/led_drv"
#define LED_ON  1
#define LED_OFF 0

// the PIN7 of BananaPro J6 output is related 
// the port PH2 of Allwinner A20 GPIO (Port Group"H") 
// it is 32 x 7 + 2 = 226

#define PIN_NO 226

int main(void)
{
    int fd;
    int ret;
    int i;

    printf("\nstart led driver test\n\n");

    fd = open(DEVICE_NAME, O_RDWR);
    printf("fd = %d\n",fd);
    if (fd == -1)
    {
        printf("open device %s error\n",DEVICE_NAME);
    }
    else
    {
        //while(1)
        for (i=0; i<128; i++)
        {
                printf("i=%d _ON\n", i);
                //fgetc(stdin);
                ioctl(fd,LED_ON, PIN_NO);
                sleep(.8);
                printf("i=%d _OFF\n", i);
                //fgetc(stdin);
                ioctl(fd,LED_OFF, PIN_NO);
                sleep(.8);
        }
        ret = close(fd);
       printf ("ret=%d\n",ret);
        printf ("close led driver test\n");
    }
    return 0;
}// end main

Finally, finish the test code and compile it.

regis@lemaker:~/ddr/led$ gcc ddr_test.c -o ddr_test


It is the show time!!!!!
How to test the device driver?

Step 1:
create a device node:
regis@lemaker:~/ddr/led$ sudo mknod -m 666 /dev/led_drv c 31 0

step 2:
load the driver to system to link with the device node
regis@lemaker:~/ddr/led$ sudo insmod led_drv.ko

check where is the led_drv
regis@lemaker:~/ddr/led$ lsmod
Module                  Size  Used by
led_drv                 5049  0 
dm_crypt               17240  0 
rfcomm                 58954  0 
bnep                   14608  2 
bluetooth             266122  10 bnep,rfcomm
ap6210                624941  0 
mali_drm                2607  0 
drm                   215174  1 mali_drm
mali                  114752  0 
ump                    58497  1 mali

step 3:
regis@lemaker:~/ddr/led$ ./ddr_test 

start led driver test

fd = 3
i=0 _ON
i=0 _OFF
i=1 _ON
i=1 _OFF
.......
i=126 _ON
i=126 _OFF
i=127 _ON
i=127 _OFF
ret=0
close led driver test
regis@lemaker:~/ddr/led$ 

You will see the LED flash 128 times.


/////////////////////////////////////////////////////////////////
Some information for reference:
Find an available device node number by this way,

1. Install the Code::Blocks IDE tools
 regis@lemaker:~/ddr/led$  sudo apt-get install codeblocks    

2. List down the device node
regis@lemaker:~/ddr/led$  cat /proc/devices 
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 81 video4linux
 89 i2c
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
241 mali
242 ump
243 hdmi
244 lcd
245 roccat
246 hidraw
247 pa_chrdev
248 ace_chrdev
249 usbmon
250 g2d_chrdev
251 disp
252 bsg
253 media
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
253 device-mapper
254 mdp

沒有留言:

張貼留言