#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"

Dev *devs;

void
devwork(void *v)
{
	Dev *d;
	Buf *b;
	Channel *r;
	uchar buf[BLOCK];

	d = v;
	for(;;){
		qlock(&d->workl);
		while(d->work.wnext == &d->work)
			rsleep(&d->workr);
		b = d->work.wnext;
		b->wnext->wprev = b->wprev;
		b->wprev->wnext = b->wnext;
		b->wnext = b->wprev = nil;
		qunlock(&d->workl);
		if(b->d == nil) /* this is a sync request */
			goto reply;
		if(b->off >= d->size){
			b->error = Einval;
			goto reply;
		}
		b->error = nil;
		if(b->op & BWRITE){
			memset(buf, 0, sizeof(buf));
			pack(b, buf);
			if(pwrite(d->fd, buf, BLOCK, b->off*BLOCK) < BLOCK){
				dprint("write: %r\n");
				b->error = Eio;
			}
		}else{
			int n, m;

			for(n = 0; n < BLOCK; n += m){
				m = pread(d->fd, buf+n, BLOCK-n, b->off*BLOCK+n);
				if(m < 0)
					dprint("read: %r\n");
				if(m <= 0)
					break;
			}
			if(n < BLOCK)
				b->error = Eio;
			else
				unpack(b, buf);
		}
	reply:
		r = b->resp;
		b->resp = nil;
		if(r != nil)
			send(r, &b);
	}
}

Dev *
newdev(char *file)
{
	Dev *d, **e;
	Dir *dir;
	Buf *b;
	
	d = emalloc(sizeof(*d));
	d->fd = open(file, ORDWR);
	if(d->fd < 0){
		free(d);
		return nil;
	}
	dir = dirfstat(d->fd);
	if(dir == nil){
	error:
		close(d->fd);
		free(d);
		return nil;
	}
	d->size = dir->length / BLOCK;
	free(dir);
	if(d->size == 0){
		werrstr("device file too short");
		goto error;
	}
	d->name = estrdup(file);
	for(b = d->buf; b < d->buf + BUFHASH + 1; b++)
		b->dnext = b->dprev = b;
	d->workr.l = &d->workl;
	d->work.wnext = d->work.wprev = &d->work;
	proccreate(devwork, d, mainstacksize);
	for(e = &devs; *e != nil; e = &(*e)->next)
		;
	*e = d;
	return d;
}
