forsyth20121205-174063a80cc13 years agoHistory
227 lines · 3.5 KB · C
RawDownload
/* * i2c * * Copyright © 1998, 2003 Vita Nuova Limited. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h" typedef struct I2Cdir I2Cdir; enum{	Qdir,	Qdata,	Qctl,}; staticDirtab i2ctab[]={	".",	{Qdir, 0, QTDIR},	0,	0555,	"i2cdata",		{Qdata, 0},	256,	0660,	"i2cctl",		{Qctl, 0},		0,	0660,}; struct I2Cdir {	Ref;	I2Cdev;	Dirtab	tab[nelem(i2ctab)];}; static voidi2creset(void){	i2csetup(0);} static Chan*i2cattach(char* spec){	char *s;	ulong addr;	I2Cdir *d;	Chan *c; 	addr = strtoul(spec, &s, 16);	if(*spec == 0 || *s || addr >= (1<<10))		error("invalid i2c address");	d = malloc(sizeof(I2Cdir));	if(d == nil)		error(Enomem);	d->ref = 1;	d->addr = addr;	d->salen = 0;	d->tenbit = addr >= 128;	memmove(d->tab, i2ctab, sizeof(d->tab));	sprint(d->tab[1].name, "i2c.%lux.data", addr);	sprint(d->tab[2].name, "i2c.%lux.ctl", addr); 	c = devattach('J', spec);	c->aux = d;	return c;} static Walkqid*i2cwalk(Chan* c, Chan *nc, char **name, int nname){	Walkqid *wq;	I2Cdir *d; 	d = c->aux;	wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen);	if(wq != nil && wq->clone != nil && wq->clone != c)		incref(d);	return wq;} static inti2cstat(Chan* c, uchar *dp, int n){	I2Cdir *d; 	d = c->aux;	return devstat(c, dp, n, d->tab, nelem(d->tab), devgen);} static Chan*i2copen(Chan* c, int omode){	I2Cdir *d; 	d = c->aux;	return devopen(c, omode, d->tab, nelem(d->tab), devgen);} static voidi2cclose(Chan *c){	I2Cdir *d; 	d = c->aux;	if(decref(d) == 0)		free(d);} static longi2cread(Chan *c, void *a, long n, vlong offset){	I2Cdir *d;	char *s, *e;	ulong len; 	d = c->aux;	switch((ulong)c->qid.path){	case Qdir:		return devdirread(c, a, n, d->tab, nelem(d->tab), devgen);	case Qdata:		len = d->tab[1].length;		if(offset+n >= len){			n = len - offset;			if(n <= 0)				return 0;		}		n = i2crecv(d, a, n, offset);		break;	case Qctl:		s = smalloc(READSTR);		if(waserror()){			free(s);			nexterror();		}		e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length);		if(d->salen)			e = seprint(e, s+READSTR, "subaddress %d\n", d->salen);		if(d->tenbit)			seprint(e, s+READSTR, "a10\n");		n = readstr(offset, a, n, s);		poperror();		free(s);		return n;	default:		n=0;		break;	}	return n;} static longi2cwrite(Chan *c, void *a, long n, vlong offset){	I2Cdir *d;	long len;	Cmdbuf *cb; 	USED(offset);	switch((ulong)c->qid.path){	case Qdata:		d = c->aux;		len = d->tab[1].length;		if(offset+n >= len){			n = len - offset;			if(n <= 0)				return 0;		}		n = i2csend(d, a, n, offset);		break;	case Qctl:		cb = parsecmd(a, n);		if(waserror()){			free(cb);			nexterror();		}		if(cb->nf < 1)			error(Ebadctl);		d = c->aux;		if(strcmp(cb->f[0], "subaddress") == 0){			if(cb->nf > 1){				len = strtol(cb->f[1], nil, 0);				if(len <= 0)					len = 0;				if(len > 4)					cmderror(cb, "subaddress too long");			}else				len = 1;			d->salen = len;		}else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){			len = strtol(cb->f[1], nil, 0);			if(len < 0)				cmderror(cb, "size is negative");			d->tab[1].length = len;		}else if(strcmp(cb->f[0], "a10") == 0)			d->tenbit = 1;		else			cmderror(cb, "unknown control request");		poperror();		free(cb);		break;	default:		error(Ebadusefd);	}	return n;} Dev i2cdevtab = {	'J',	"i2c", 	i2creset,	devinit,	devshutdown,	i2cattach,	i2cwalk,	i2cstat,	i2copen,	devcreate,	i2cclose,	i2cread,	devbread,	i2cwrite,	devbwrite,	devremove,	devwstat,};