| /* Copyright (c) 2020 Google Inc | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * #template.  Dummy device with a devdir table. | 
 |  */ | 
 |  | 
 | #include <ns.h> | 
 | #include <kmalloc.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 |  | 
 | struct dev template_devtab; | 
 |  | 
 | static char *devname(void) | 
 | { | 
 | 	return template_devtab.name; | 
 | } | 
 |  | 
 | enum { | 
 | 	Qdir, | 
 | 	Qctl, | 
 | }; | 
 |  | 
 | static struct dirtab XXX_dir[] = { | 
 | 	{".", {Qdir, 0, QTDIR}, 0, DMDIR | 0555}, | 
 | 	{"ctl", {Qctl, 0, QTFILE}, 0, 0666}, | 
 | }; | 
 |  | 
 | static struct chan *XXX_attach(char *spec) | 
 | { | 
 | 	return devattach(devname(), spec); | 
 | } | 
 |  | 
 | static struct walkqid *XXX_walk(struct chan *c, struct chan *nc, char **name, | 
 | 			       unsigned int nname) | 
 | { | 
 | 	return devwalk(c, nc, name, nname, XXX_dir, ARRAY_SIZE(XXX_dir), | 
 | 		       devgen); | 
 | } | 
 |  | 
 | static size_t XXX_stat(struct chan *c, uint8_t *db, size_t n) | 
 | { | 
 | 	return devstat(c, db, n, XXX_dir, ARRAY_SIZE(XXX_dir), devgen); | 
 | } | 
 |  | 
 | static struct chan *XXX_open(struct chan *c, int omode) | 
 | { | 
 | 	return devopen(c, omode, XXX_dir, ARRAY_SIZE(XXX_dir), devgen); | 
 | } | 
 |  | 
 | static void XXX_close(struct chan *c) | 
 | { | 
 | 	/* If you do anything after open, only undo it for COPEN chans */ | 
 | 	if (!(c->flag & COPEN)) | 
 | 		return; | 
 | } | 
 |  | 
 | static size_t XXX_read(struct chan *c, void *ubuf, size_t n, off64_t offset) | 
 | { | 
 | 	switch (c->qid.path) { | 
 | 	case Qdir: | 
 | 		return devdirread(c, ubuf, n, XXX_dir, ARRAY_SIZE(XXX_dir), | 
 | 				  devgen); | 
 | 	case Qctl: | 
 | 		return readstr(offset, ubuf, n, "XXX"); | 
 | 	default: | 
 | 		panic("Bad Qid %p!", c->qid.path); | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | #define XXX_CTL_USAGE "start|stop|print|reset" | 
 |  | 
 | static void XXX_ctl_cmd(struct chan *c, struct cmdbuf *cb) | 
 | { | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	if (cb->nf < 1) | 
 | 		error(EFAIL, XXX_CTL_USAGE); | 
 |  | 
 | 	if (waserror()) { | 
 | 		nexterror(); | 
 | 	} | 
 | 	if (!strcmp(cb->f[0], "start")) { | 
 | 		; | 
 | 	} else if (!strcmp(cb->f[0], "stop")) { | 
 | 		; | 
 | 	} else if (!strcmp(cb->f[0], "print")) { | 
 | 		; | 
 | 	} else if (!strcmp(cb->f[0], "reset")) { | 
 | 		; | 
 | 	} else { | 
 | 		error(EFAIL, XXX_CTL_USAGE); | 
 | 	} | 
 | 	poperror(); | 
 | } | 
 |  | 
 | static size_t XXX_write(struct chan *c, void *ubuf, size_t n, off64_t unused) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct cmdbuf *cb = parsecmd(ubuf, n); | 
 |  | 
 | 	if (waserror()) { | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 | 	switch (c->qid.path) { | 
 | 	case Qctl: | 
 | 		XXX_ctl_cmd(c, cb); | 
 | 		break; | 
 | 	default: | 
 | 		error(EFAIL, "Unable to write to %s", devname()); | 
 | 	} | 
 | 	kfree(cb); | 
 | 	poperror(); | 
 | 	return n; | 
 | } | 
 |  | 
 | struct dev template_devtab __devtab = { | 
 | 	.name = "template", | 
 | 	.reset = devreset, | 
 | 	.init = devinit, | 
 | 	.shutdown = devshutdown, | 
 | 	.attach = XXX_attach, | 
 | 	.walk = XXX_walk, | 
 | 	.stat = XXX_stat, | 
 | 	.open = XXX_open, | 
 | 	.create = devcreate, | 
 | 	.close = XXX_close, | 
 | 	.read = XXX_read, | 
 | 	.bread = devbread, | 
 | 	.write = XXX_write, | 
 | 	.bwrite = devbwrite, | 
 | 	.remove = devremove, | 
 | 	.wstat = devwstat, | 
 | 	.power = devpower, | 
 | 	.chaninfo = devchaninfo, | 
 | }; |