ref: a7b1ab8236b7ccd41e96e9e4008cbc3213294724
dir: /devgpio.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
enum{
GPIO_SYSCONFIG = 0x10/4,
SOFTRESET = 1<<1,
GPIO_SYSSTATUS = 0x114/4,
RESETDONE = 1<<0,
GPIO_OE = 0x134/4,
GPIO_DATAOUT = 0x13c/4,
GPIO_CLEARDATAOUT = 0x190/4,
GPIO_SETDATAOUT = 0x194/4,
};
enum{
Qroot,
Qgpio,
Qdir,
Qdata,
Qctl,
Qevent,
};
static Dirtab rootdir = { "#G", { Qroot, 0, QTDIR }, 0, 0555 };
static Dirtab gpiodir = { "gpio", { Qgpio, 0, QTDIR }, 0, 0555 };
static Dirtab pinfiles[] = {
"data", { Qdata, 0, QTFILE }, 0, 0666,
"ctl", { Qctl, 0, QTFILE }, 0, 0666,
"event", { Qevent, 0, QTFILE }, 0, 0444,
};
static void
gpioinit(void)
{
int i;
for(i = 0; i < 4; i++){
gpio[i][GPIO_SYSCONFIG] = SOFTRESET;
while((gpio[i][GPIO_SYSSTATUS] & RESETDONE) != 0);
gpio[i][GPIO_OE] = 0;
}
}
static void
gpioshutdown(void)
{
}
static Chan*
gpioattach(char *spec)
{
return devattach('G', spec);
}
static int
gpiogen(Chan *c, char*, Dirtab*, int, int s, Dir *db)
{
Qid qid;
if(s == DEVDOTDOT){
switch((ulong)c->qid.path&0xff){
case Qroot:
case Qgpio:
qid.type = QTDIR;
qid.path = Qroot;
qid.vers = 0;
devdir(c, qid, 0, 0, eve, 0555, db);
return 1;
case Qdir:
qid.type = QTDIR;
qid.path = Qgpio;
qid.vers = 0;
devdir(c, qid, 0, 0, eve, 0555, db);
return 1;
case Qdata:
case Qctl:
case Qevent:
qid.type = QTDIR;
qid.path = (c->qid.path&0xff00)|Qdir;
qid.vers = 0;
devdir(c, qid, 0, 0, eve, 0555, db);
return 1;
}
return -1;
}
if(c->qid.path == Qroot){
if(s == 0){
qid.type = QTDIR;
qid.path = Qgpio;
qid.vers = 0;
devdir(c, qid, "gpio", 0, eve, 0555, db);
return 1;
}
return -1;
}
if(c->qid.path == Qgpio){
if(s < 128){
qid.type = QTDIR;
qid.path = s<<8|Qdir;
qid.vers = 0;
snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
devdir(c, qid, up->genbuf, 0, eve, 0555, db);
return 1;
}
return -1;
}
if((c->qid.path & 0xff) == Qdir){
qid.type = QTFILE;
qid.path = (c->qid.path&0xff00);
qid.vers = 0;
switch(s){
case 0:
qid.path |= Qdata;
devdir(c, qid, "data", 0, eve, 0666, db);
return 1;
case 1:
qid.path |= Qctl;
devdir(c, qid, "ctl", 0, eve, 0666, db);
return 1;
case 2:
qid.path |= Qevent;
devdir(c, qid, "event", 0, eve, 0444, db);
return 1;
}
return -1;
}
qid.type = QTFILE;
qid.path = c->qid.path+s;
qid.vers = 0;
switch((ulong)c->qid.path & 0xff){
case Qdata:
devdir(c, qid, "data", 0, eve, 0666, db);
return 1;
case Qctl:
devdir(c, qid, "ctl", 0, eve, 0666, db);
return 1;
case Qevent:
devdir(c, qid, "event", 0, eve, 0666, db);
return 1;
}
return -1;
}
static Walkqid*
gpiowalk(Chan *c, Chan *nc, char** name, int nname)
{
return devwalk(c, nc, name, nname, 0, 0, gpiogen);
}
static int
gpiostat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, 0, 0, gpiogen);
}
static Chan*
gpioopen(Chan *c, int omode)
{
c = devopen(c, omode, 0, 0, gpiogen);
return c;
}
static void
gpioclose(Chan*)
{
}
static long
gpioread(Chan *c, void *a, long n, vlong off)
{
ulong pin, reg, i;
pin = (ulong)c->qid.path>>8;
reg = pin>>5;
i = pin&0x1f;
switch((ulong)c->qid.path&0xff){
case Qroot:
case Qgpio:
case Qdir:
return devdirread(c, a, n, 0, 0, gpiogen);
case Qdata:
if(n > 2)
n = 2;
if(off > 1)
return 0;
if((gpio[reg][GPIO_DATAOUT] & 1<<i) != 0)
memmove(a, "1\n", n);
else
memmove(a, "0\n", n);
return 2;
case Qctl:
if(n > 8)
n = 8;
if(off > 8)
return 0;
memmove(a, "dig out\n" + off, n - off);
return n - off;
case Qevent:
return 0;
}
error(Egreg);
return -1;
}
static char Ebaddata[] = "bad data message";
static long
gpiowrite(Chan *c, void *va, long, vlong)
{
char *a;
ulong pin, reg, i;
if(c->qid.type & QTDIR)
error(Eisdir);
a = va;
pin = (ulong)c->qid.path>>8;
reg = pin>>5;
i = pin&0x1f;
switch((ulong)c->qid.path&0xff){
case Qdata:
if(a[0] == '1'){
gpio[reg][GPIO_SETDATAOUT] = 1<<i;
return 1;
}
if(a[0] == '0'){
gpio[reg][GPIO_CLEARDATAOUT] = 1<<i;
return 1;
}
error(Ebaddata);
return -1;
}
error(Egreg);
return -1;
}
Dev gpiodevtab = {
'G',
"gpio",
devreset,
gpioinit,
gpioshutdown,
gpioattach,
gpiowalk,
gpiostat,
gpioopen,
devcreate,
gpioclose,
gpioread,
devbread,
gpiowrite,
devbwrite,
devremove,
devwstat,
};