古人智慧

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

搜尋此網誌

Translation

2015年6月7日 星期日

[Banana Pro] Device driver study(device_create, misc_register) - LED control

上次第一個《device driver sample 範例》 需要自己建立 node,這並不是太好的做法。

本次提供一個方法是在driver init的時候,自動建立driver node。
有兩個方式:

1. 使用 device_create
2. 使用 misc_register 

/*
 * 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>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

static dev_t first; // Global variable for the first device number
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class

/*add for BananaPro by LeMaker team*/
// for mmap BananaPro
#define MAX_PIN_NUM (0x40)  //64
#define SUNXI_GPIO_BASE (0x01C20800)
#define MAP_SIZE (4096*2)
#define MAP_MASK (MAP_SIZE - 1)
//sunxi_pwm
#define SUNXI_PWM_BASE (0x01c20e00)
#define SUNXI_PWM_CTRL_REG  (SUNXI_PWM_BASE)
#define SUNXI_PWM_CH0_PERIOD  (SUNXI_PWM_BASE + 0x4)
#define SUNXI_PWM_CH1_PERIOD  (SUNXI_PWM_BASE + 0x8)

#define SUNXI_PWM_CH0_EN (1 << 4)
#define SUNXI_PWM_CH0_ACT_STA (1 << 5)
#define SUNXI_PWM_SCLK_CH0_GATING (1 << 6)
#define SUNXI_PWM_CH0_MS_MODE (1 << 7) //pulse mode
#define SUNXI_PWM_CH0_PUL_START (1 << 8)

#define SUNXI_PWM_CH1_EN (1 << 19)
#define SUNXI_PWM_CH1_ACT_STA (1 << 20)
#define SUNXI_PWM_SCLK_CH1_GATING (1 << 21)
#define SUNXI_PWM_CH1_MS_MODE (1 << 22) //pulse mode
#define SUNXI_PWM_CH1_PUL_START (1 << 23)


#define PWM_CLK_DIV_120 0
#define PWM_CLK_DIV_180 1
#define PWM_CLK_DIV_240 2
#define PWM_CLK_DIV_360 3
#define PWM_CLK_DIV_480 4
#define PWM_CLK_DIV_12K 8
#define PWM_CLK_DIV_24K 9
#define PWM_CLK_DIV_36K 10
#define PWM_CLK_DIV_48K 11
#define PWM_CLK_DIV_72K 12

#define GPIO_PADS_BP (0x00100000)
#define CLOCK_BASE_BP (0x00101000)
// addr should 4K*n
// #define GPIO_BASE_BP (SUNXI_GPIO_BASE)
#define GPIO_BASE_BP (0x01C20000)
#define GPIO_TIMER_BP (0x0000B000)
#define GPIO_PWM_BP (0x01c20000)  //need 4k*n

// Pin modes

#define INPUT 0
#define OUTPUT 1
#define PWM_OUTPUT 2
#define GPIO_CLOCK 3
#define SOFT_PWM_OUTPUT 4
#define SOFT_TONE_OUTPUT 5
#define PWM_TONE_OUTPUT 6


#define LOW 0
#define HIGH 1


#define LED_ID 7

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

static struct semaphore sem;
static int globalvar_count=0;
static DEFINE_SPINLOCK(spin) ;
static int global_var = 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 = *(gpio + mmap_seek);
val = ioread32(gpio + mmap_seek);
printk("func:%s phyaddr:0x%x val:0x%x\n",__func__, gpio + mmap_seek, val);
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, val);
iowrite32(val, gpio + mmap_seek);
//*(gpio + mmap_seek) = val;
}

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 , bank,index,phyaddr);
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__, pin , mode,bank,index,phyaddr);
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);
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);
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 -> data reg
//uint32_t phyaddr = gpio + (bank * 36) + 0x10;
printk("func:%s pin:%d, value:%d bank:%d index:%d phyaddr:0x%x\n",__func__, pin , value,bank,index,phyaddr);
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);
  regval = readl_1(phyaddr);
printk("LOW val set over reg val: 0x%x\n",regval);
}
else
{
  regval |= (1 << index);
  writel_1(regval, phyaddr);
  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 -> data reg
printk("func:%s pin:%d,bank:%d index:%d phyaddr:0x%x\n",__func__, pin,bank,index,phyaddr);
regval = readl_1(phyaddr);
regval = regval >> index;
regval &= 1;
printk("***** read reg val: 0x%x,bank:%d,index:%d,line:%d\n",regval,bank,index,__LINE__);
return regval;
}


int led_open (struct inode *pnode, struct file *pfile)
{
printk("enter %s!\n", __FUNCTION__);
spin_lock(&spin);
    if (globalvar_count)
    {
        printk("enter %s - Error!!!\n", __FUNCTION__);
        spin_unlock(&spin);
        return -EBUSY;
    }
    globalvar_count++;
    gpio = ioremap(GPIO_BASE_BP, 4*1024);
    spin_unlock(&spin);
return 0;
}

int led_close (struct inode *pnode, struct file *pfile)
{
printk("enter %s!\n", __FUNCTION__);
globalvar_count--;
   iounmap(gpio);

return 0;
}

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

    if (down_interruptible(&sem))
    {
        printk("enter %s down_interruptible - Error!\n", __FUNCTION__);
        return -ERESTARTSYS;
    }

    printk("enter %s down_interruptible - ok!\n", __FUNCTION__);
    up(&sem);
return 0;
}

ssize_t led_write(struct file *pfile, const char __user *buffer, size_t lenght, loff_t *offset)
{
printk("enter %s!\n", __FUNCTION__);
    if (down_interruptible(&sem))
    {
        printk("enter %s down_interruptible - Error!\n", __FUNCTION__);
        return -ERESTARTSYS;
    }
    printk("enter %s down_interruptible - ok!\n", __FUNCTION__);
    up(&sem);

return lenght;
}

long led_ioctl(struct file *pfile, unsigned int cmd, unsigned long data)
{
//printk("enter %s %d!\n", __FUNCTION__, cmd);
    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__);
if (alloc_chrdev_region(&first, 0, 1, "led_drv") < 0)
    {
        return -1;
    }
    if ((cl = class_create(THIS_MODULE, "leds")) == NULL)
    {
        unregister_chrdev_region(first, 1);
        return -1;
    }
    if (device_create(cl, NULL, first, NULL, "led_drv") == NULL)
    {
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return -1;
    }
    cdev_init(&c_dev, &io_dev_fops);
    if (cdev_add(&c_dev, first, 1) == -1)
    {
        device_destroy(cl, first);
        class_destroy(cl);
        unregister_chrdev_region(first, 1);
        return -1;
    }

//init_MUTEX(&sem);
//gpio = (uint32_t *)(SUNXI_GPIO_BASE);
//gpio = ioremap(SUNXI_GPIO_BASE, 64*1024);
//set_gpio_mode(LED_ID, OUTPUT);

return 0;
}

void __exit led_exit(void)
{
    //unregister_chrdev(LED_DRV_MAJOR, "led");
printk("enter %s!\n", __FUNCTION__);
    device_destroy(cl, first);
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
}

//using the way of misc_register
static struct miscdevice hello_dev = {
        /*
         * We don't care what minor number we end up with, so tell the
         * kernel to just pick one.
         */
        MISC_DYNAMIC_MINOR,
        /*
         * Name ourselves /dev/hello.
         */
        "led_drv",
        /*
         * What functions to call when a program performs file
         * operations on the device.
         */
        &io_dev_fops
};

int __init led_init_1(void)
{
    int ret;

    /*
    * Create the "hello" device in the /sys/class/misc directory.
    * Udev will automatically create the /dev/hello device using
    * the default rules.
    */
  printk("enter %s!\n", __FUNCTION__);

    ret = misc_register(&hello_dev);
    if (ret)
        printk(KERN_ERR
            "Unable to register \"led_drv\" misc device\n");

    return ret;
}

void __exit led_exit_1(void)
{
    misc_deregister(&hello_dev);
}


// using the way of device_create
//module_init(led_init);
//module_exit(led_exit);

// using the way of misc_register
module_init(led_init_1);
module_exit(led_exit_1);



沒有留言:

張貼留言