本次提供一個方法是在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);
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);
沒有留言:
張貼留言