linux/arch/h8300/kernel/gpio.c
Al Viro a81ecc1631 h8300: single_open() leaks
Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2013-05-05 00:08:26 -04:00

179 lines
3.6 KiB
C

/*
* linux/arch/h8300/kernel/gpio.c
*
* Yoshinori Sato <ysato@users.sourceforge.jp>
*
*/
/*
* Internal I/O Port Management
*/
#include <linux/stddef.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/init.h>
#define _(addr) (volatile unsigned char *)(addr)
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
#include <asm/regs306x.h>
static volatile unsigned char *ddrs[] = {
_(P1DDR),_(P2DDR),_(P3DDR),_(P4DDR),_(P5DDR),_(P6DDR),
NULL, _(P8DDR),_(P9DDR),_(PADDR),_(PBDDR),
};
#define MAX_PORT 11
#endif
#if defined(CONFIG_H83002) || defined(CONFIG_H8048)
/* Fix me!! */
#include <asm/regs306x.h>
static volatile unsigned char *ddrs[] = {
_(P1DDR),_(P2DDR),_(P3DDR),_(P4DDR),_(P5DDR),_(P6DDR),
NULL, _(P8DDR),_(P9DDR),_(PADDR),_(PBDDR),
};
#define MAX_PORT 11
#endif
#if defined(CONFIG_H8S2678)
#include <asm/regs267x.h>
static volatile unsigned char *ddrs[] = {
_(P1DDR),_(P2DDR),_(P3DDR),NULL ,_(P5DDR),_(P6DDR),
_(P7DDR),_(P8DDR),NULL, _(PADDR),_(PBDDR),_(PCDDR),
_(PDDDR),_(PEDDR),_(PFDDR),_(PGDDR),_(PHDDR),
_(PADDR),_(PBDDR),_(PCDDR),_(PDDDR),_(PEDDR),_(PFDDR),
_(PGDDR),_(PHDDR)
};
#define MAX_PORT 17
#endif
#undef _
#if !defined(P1DDR)
#error Unsuppoted CPU Selection
#endif
static struct {
unsigned char used;
unsigned char ddr;
} gpio_regs[MAX_PORT];
extern char *_platform_gpio_table(int length);
int h8300_reserved_gpio(int port, unsigned int bits)
{
unsigned char *used;
if (port < 0 || port >= MAX_PORT)
return -1;
used = &(gpio_regs[port].used);
if ((*used & bits) != 0)
return 0;
*used |= bits;
return 1;
}
int h8300_free_gpio(int port, unsigned int bits)
{
unsigned char *used;
if (port < 0 || port >= MAX_PORT)
return -1;
used = &(gpio_regs[port].used);
if ((*used & bits) != bits)
return 0;
*used &= (~bits);
return 1;
}
int h8300_set_gpio_dir(int port_bit,int dir)
{
int port = (port_bit >> 8) & 0xff;
int bit = port_bit & 0xff;
if (ddrs[port] == NULL)
return 0;
if (gpio_regs[port].used & bit) {
if (dir)
gpio_regs[port].ddr |= bit;
else
gpio_regs[port].ddr &= ~bit;
*ddrs[port] = gpio_regs[port].ddr;
return 1;
} else
return 0;
}
int h8300_get_gpio_dir(int port_bit)
{
int port = (port_bit >> 8) & 0xff;
int bit = port_bit & 0xff;
if (ddrs[port] == NULL)
return 0;
if (gpio_regs[port].used & bit) {
return (gpio_regs[port].ddr & bit) != 0;
} else
return -1;
}
#if defined(CONFIG_PROC_FS)
static char *port_status(int portno)
{
static char result[10];
static const char io[2]={'I','O'};
char *rp;
int c;
unsigned char used,ddr;
used = gpio_regs[portno].used;
ddr = gpio_regs[portno].ddr;
result[8]='\0';
rp = result + 7;
for (c = 8; c > 0; c--,rp--,used >>= 1, ddr >>= 1)
if (used & 0x01)
*rp = io[ ddr & 0x01];
else
*rp = '-';
return result;
}
static int gpio_proc_show(struct seq_file *m, void *v)
{
static const char port_name[]="123456789ABCDEFGH";
int c;
for (c = 0; c < MAX_PORT; c++) {
if (ddrs[c] == NULL)
continue;
seq_printf(m, "P%c: %s\n", port_name[c], port_status(c));
}
return 0;
}
static int gpio_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, gpio_proc_show, PDE_DATA(inode));
}
static const struct file_operations gpio_proc_fops = {
.open = gpio_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static __init int register_proc(void)
{
return proc_create("gpio", S_IRUGO, NULL, &gpio_proc_fops) != NULL;
}
__initcall(register_proc);
#endif
void __init h8300_gpio_init(void)
{
memcpy(gpio_regs,_platform_gpio_table(sizeof(gpio_regs)),sizeof(gpio_regs));
}