From a56d713c5f3cb60d31eb87a19ce1128ffb2567a5 Mon Sep 17 00:00:00 2001 From: Linux User Date: Mon, 4 Nov 2019 21:10:46 -0800 Subject: [PATCH] initial --- Makefile | 9 + NinePea.c | 485 +++++++++++++++++++++++++++++++++++++ NinePea.h | 238 ++++++++++++++++++ fs.c | 324 +++++++++++++++++++++++++ jpeg.c | 48 ++++ jpeg.h | 2 + v4l.c | 703 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ v4l.h | 13 + 8 files changed, 1822 insertions(+) create mode 100644 Makefile create mode 100644 NinePea.c create mode 100644 NinePea.h create mode 100644 fs.c create mode 100644 jpeg.c create mode 100644 jpeg.h create mode 100644 v4l.c create mode 100644 v4l.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b84afcd --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +ALL=v4lfs + +all: $(ALL) + +v4lfs: fs.o NinePea.o v4l.o jpeg.o + $(CC) -o v4lfs fs.o NinePea.o v4l.o jpeg.o -ljpeg + +clean: + rm -f v4lfs *.o *~ diff --git a/NinePea.c b/NinePea.c new file mode 100644 index 0000000..ea93690 --- /dev/null +++ b/NinePea.c @@ -0,0 +1,485 @@ +#include "NinePea.h" + +struct htable *fs_fids; + +int +puthdr(unsigned char *buf, unsigned long index, unsigned char type, unsigned int tag, unsigned long size) { + put4(buf, index, size); + buf[index++] = type; + put2(buf, index, tag); + + return index; +} + +int +mkerr(unsigned char *buffer, unsigned char tag, char *errstr) { + int index; + int slen = strlen(errstr); + int size = slen + 9; + + index = puthdr(buffer, 0, RError, tag, size); + + put2(buffer, index, slen); + memcpy(&buffer[index], errstr, slen); + + return size; +} + +int +putstat(unsigned char *buffer, unsigned long index, Stat *stat) { + unsigned int namelen = strlen(stat->name); + unsigned int uidlen = strlen(stat->uid); + unsigned int gidlen = strlen(stat->gid); + unsigned int muidlen = strlen(stat->muid); + + unsigned int size = 2 + 4 + (1 + 4 + 8) + 4 + 4 + 4 + 8 + (2 * 4) + namelen + uidlen + gidlen + muidlen; + put2(buffer, index, size); + + put2(buffer, index, stat->type); + put4(buffer, index, stat->dev); + buffer[index++] = stat->qid.type; + put4(buffer, index, stat->qid.version); + put8(buffer, index, stat->qid.path, 0); + put4(buffer, index, stat->mode); + put4(buffer, index, stat->atime); + put4(buffer, index, stat->mtime); + put8(buffer, index, stat->length, 0); + + put2(buffer, index, namelen); + memcpy(&buffer[index], stat->name, namelen); + index += namelen; + + put2(buffer, index, uidlen); + memcpy(&buffer[index], stat->uid, uidlen); + index += uidlen; + + put2(buffer, index, gidlen); + memcpy(&buffer[index], stat->gid, gidlen); + index += gidlen; + + put2(buffer, index, muidlen); + memcpy(&buffer[index], stat->muid, muidlen); + index += muidlen; + + return (size + 2); +} + +int +getstat(unsigned char *buffer, unsigned long index, Stat *stat) { + unsigned int size; + unsigned long tmp; + unsigned int namelen; + unsigned int uidlen; + unsigned int gidlen; + unsigned int muidlen; + + get2(buffer, index, size); + + get2(buffer, index, stat->type); + get4(buffer, index, stat->dev); + buffer[index++] = stat->qid.type; + get4(buffer, index, stat->qid.version); + get8(buffer, index, stat->qid.path, tmp); + get4(buffer, index, stat->mode); + get4(buffer, index, stat->atime); + get4(buffer, index, stat->mtime); + get8(buffer, index, stat->length, tmp); + + get2(buffer, index, namelen); + stat->name = (char*)malloc (sizeof (char) * (namelen + 1)); + stat->name[namelen] = '\0'; + memcpy(stat->name, &buffer[index], namelen); + index += namelen; + + get2(buffer, index, uidlen); + stat->uid = (char*)malloc (sizeof (char) * (uidlen + 1)); + stat->uid[uidlen] = '\0'; + memcpy(stat->uid, &buffer[index], uidlen); + index += uidlen; + + get2(buffer, index, gidlen); + stat->gid = (char*)malloc (sizeof (char) * (gidlen + 1)); + stat->gid[gidlen] = '\0'; + memcpy(stat->gid, &buffer[index], gidlen); + index += gidlen; + + get2(buffer, index, muidlen); + stat->muid = (char*)malloc (sizeof (char) * (muidlen + 1)); + stat->muid[muidlen] = '\0'; + memcpy(stat->muid, &buffer[index], muidlen); + index += muidlen; + + return index; +} + +char Etoobig[] = "TMI"; +char Ebadtype[] = "9P?"; +char Enofile[] = "file not found"; +char Eperm[] = "permission denied"; + +unsigned long +proc9p(unsigned char *msg, unsigned long size, Callbacks *cb) { + Fcall ifcall; + Fcall *ofcall = NULL; + unsigned long slen, tmp; + unsigned long index; + unsigned char i; + + msg[size] = '\0'; + index = 4; + ifcall.type = msg[index++]; + get2(msg, index, ifcall.tag); + + if (size > MAX_MSG) { + return mkerr(msg, ifcall.tag, Etoobig); + } + + // if it isn't here, it isn't implemented + switch(ifcall.type) { + case TVersion: + i = index; + index = 7; + + get4(msg, i, ifcall.msize); + get2(msg, i, slen); + + if (ifcall.msize > MAX_MSG) + ifcall.msize = MAX_MSG; + + put4(msg, index, ifcall.msize); + put2(msg, index, slen); + + index += slen; + puthdr(msg, 0, RVersion, ifcall.tag, index); + + break; + case TAuth: + index = mkerr(msg, ifcall.tag, "no auth"); + break; + case TAttach: + get4(msg, index, ifcall.fid); + get4(msg, index, ifcall.afid); + + get2(msg, index, slen); + index += slen; + + get2(msg, index, slen); + msg[index-2] = '\0'; + ifcall.uname = strdup((char*)&msg[index-2-slen]); + + index += slen; + msg[index] = '\0'; + ifcall.aname = strdup((char*)&msg[index-slen]); + + ofcall = cb->attach(&ifcall); + + free(ifcall.uname); + free(ifcall.aname); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = 7; + msg[index++] = ofcall->qid.type; + put4(msg, index, ofcall->qid.version); + put8(msg, index, ofcall->qid.path, 0); + puthdr(msg, 0, RAttach, ifcall.tag, index); + + break; + case TWalk: + get4(msg, index, ifcall.fid); + get4(msg, index, ifcall.newfid); + get2(msg, index, ifcall.nwname); + + if (ifcall.nwname > MAX_WELEM) + ifcall.nwname = MAX_WELEM; + + get2(msg, index, slen); + for (i = 0; i < ifcall.nwname; i++) { + index += slen; + get2(msg, index, tmp); + msg[index-2] = '\0'; + ifcall.wname[i] = strdup((char*)&msg[index-2-slen]); + slen = tmp; + } + + ofcall = cb->walk(&ifcall); + + for (i = 0; i < ifcall.nwname; i++) + free(ifcall.wname[i]); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RWalk, ifcall.tag, 9 + ofcall->nwqid * 13); + put2(msg, index, ofcall->nwqid); + + for (i = 0; i < ofcall->nwqid; i++) { + msg[index++] = ofcall->wqid[i].type; + put4(msg, index, ofcall->wqid[i].version); + put8(msg, index, ofcall->wqid[i].path, 0); + } + + break; + case TStat: + get4(msg, index, ifcall.fid); + + ofcall = cb->stat(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + slen = putstat(msg, 9, &(ofcall->stat)); + index = puthdr(msg, 0, RStat, ifcall.tag, slen + 9); + put2(msg, index, slen); // bleh? + index += slen; + + break; + case TClunk: + get4(msg, index, ifcall.fid); + + ofcall = cb->clunk(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RClunk, ifcall.tag, 7); + + break; + case TOpen: + get4(msg, index, ifcall.fid); + ifcall.mode = msg[index++]; + + ofcall = cb->open(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, ROpen, ifcall.tag, 24); + msg[index++] = ofcall->qid.type; + put4(msg, index, ofcall->qid.version); + put8(msg, index, ofcall->qid.path, 0); + put4(msg, index, MAX_IO); + + break; + case TRead: + get4(msg, index, ifcall.fid); + get4(msg, index, ifcall.offset); + index += 4; // :( + get4(msg, index, ifcall.count); + + ofcall = cb->read(&ifcall, &msg[11]); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + // No response + if (ofcall == NULL) { + index = 0; + + break; + } + + index = puthdr(msg, 0, RRead, ifcall.tag, 11 + ofcall->count); + put4(msg, index, ofcall->count); + index += ofcall->count; + + break; + case TCreate: + get4(msg, index, ifcall.fid); + get2(msg, index, slen); + ifcall.name = (char*)&msg[index]; + index += slen; + get4(msg, index, ifcall.perm); + msg[index-4] = '\0'; + ifcall.mode = msg[index++]; + + ofcall = cb->create(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RCreate, ifcall.tag, 24); + msg[index++] = ofcall->qid.type; + put4(msg, index, ofcall->qid.version); + put8(msg, index, ofcall->qid.path, 0); + put4(msg, index, MAX_IO); + + break; + case TWrite: + get4(msg, index, ifcall.fid); + get4(msg, index, ifcall.offset); + index += 4; // bleh... again + get4(msg, index, ifcall.count); + + ofcall = cb->write(&ifcall, &msg[index]); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RWrite, ifcall.tag, 11); + put4(msg, index, ofcall->count); + + break; + case TRemove: + get4(msg, index, ifcall.fid); + + ofcall = cb->remove(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RRemove, ifcall.tag, 7); + + break; + case TFlush: + get2(msg, index, ifcall.oldtag); + + ofcall = cb->flush(&ifcall); + + if (ofcall->type == RError) { + index = mkerr(msg, ifcall.tag, ofcall->ename); + + break; + } + + index = puthdr(msg, 0, RFlush, ifcall.tag, 7); + + break; + case TWStat: +#if 0 + get4(msg, index, ifcall.fid); + index = getstat(msg, index, &ifcall.stat); + + ofcall = cb->wstat(&ifcall); + + free (ifcall.stat.name); + free (ifcall.stat.uid); + free (ifcall.stat.gid); + free (ifcall.stat.muid); + + if (ofcall->type == RError) + index = mkerr(msg, ifcall.tag, ofcall->ename); + else + index = puthdr(msg, 0, RWStat, ifcall.tag, 7); + break; +#endif + default: + index = mkerr(msg, ifcall.tag, Ebadtype); + break; + } + + if (index > MAX_MSG) { + index = mkerr(msg, ifcall.tag, Etoobig); + } + + return index; +} + +/* fid mapping functions */ + +unsigned long +hashf(struct htable *tbl, unsigned long id) { + return id % tbl->length; +} + +struct hentry* +fs_fid_find(unsigned long id) { + struct hentry **cur; + + for (cur = &(fs_fids->data[hashf(fs_fids, id)]); + *cur != NULL; cur = &(*cur)->next) { + if ((*cur)->id == id) + break; + } + + return *cur; +} + +struct hentry* +fs_fid_add(unsigned long id, unsigned long data) { + struct hentry *cur = fs_fid_find(id); + unsigned char h; + + if (cur == NULL) { + cur = (struct hentry*)calloc(1, sizeof(*cur)); + cur->id = id; + h = hashf(fs_fids, id); + + if (fs_fids->data[h]) { + cur->next = fs_fids->data[h]; + cur->next->prev = cur; + } + fs_fids->data[h] = cur; + } + + cur->data = data; + + return cur; +} + +void +fs_fid_del(unsigned long id) { + unsigned char h = hashf(fs_fids, id); + struct hentry *cur = fs_fids->data[h]; + + if (cur->id == id) + fs_fids->data[h] = cur->next; + else { + cur = cur->next; + while (cur) { + if (cur->id == id) + break; + + cur = cur->next; + } + } + + if (cur == NULL) { + return; + } + + if (cur->prev) + cur->prev->next = cur->next; + if (cur->next) + cur->next->prev = cur->prev; + + if (cur->aux != NULL) + free(cur->aux); + + free(cur); +} + +void +fs_fid_init(int l) { + fs_fids = (htable*)malloc(sizeof(htable)); + fs_fids->length = l; + fs_fids->data = (hentry**)calloc(fs_fids->length, sizeof(hentry*)); +} diff --git a/NinePea.h b/NinePea.h new file mode 100644 index 0000000..296144e --- /dev/null +++ b/NinePea.h @@ -0,0 +1,238 @@ +#ifndef NINEPEA_H +#define NINEPEA_H + +#include +#include + +#define put2(buffer, index, value) \ + buffer[index++] = value & 0xFF; \ + buffer[index++] = (value >> 8) & 0xFF; \ + +#define put4(buffer, index, value) \ + buffer[index++] = value & 0xFF; \ + buffer[index++] = (value >> 8) & 0xFF; \ + buffer[index++] = (value >> 16) & 0xFF; \ + buffer[index++] = (value >> 24) & 0xFF; \ + +#define put8(buffer, index, lvalue, hvalue) \ + buffer[index++] = lvalue & 0xFF; \ + buffer[index++] = (lvalue >> 8) & 0xFF; \ + buffer[index++] = (lvalue >> 16) & 0xFF; \ + buffer[index++] = (lvalue >> 24) & 0xFF; \ + buffer[index++] = hvalue & 0xFF; \ + buffer[index++] = (hvalue >> 8) & 0xFF; \ + buffer[index++] = (hvalue >> 16) & 0xFF; \ + buffer[index++] = (hvalue >> 24) & 0xFF; \ + +#define get4(buffer, index, value) \ + value = buffer[index++]; \ + value |= buffer[index++] << 8; \ + value |= buffer[index++] << 16; \ + value |= buffer[index++] << 24; \ + +#define get8(buffer, index, lvalue, hvalue) \ + get4(buffer, index, lvalue); \ + get4(buffer, index, hvalue); \ + +#define get2(buffer, index, value) \ + value = buffer[index++]; \ + value |= buffer[index++] << 8; \ + +// might have to change these depending on memory allocated +#define MAX_IO 8192 +#define MAX_MSG 8192+128 +#define MAX_WELEM 16 +#define MAX_PGMBUF 80 +#define NOTAG ~0 + +/* 9P message types */ +enum { + TVersion = 'd', + RVersion, + TAuth = 'f', + RAuth, + TAttach = 'h', + RAttach, + TError = 'j', /* illegal */ + RError, + TFlush = 'l', + RFlush, + TWalk = 'n', + RWalk, + TOpen = 'p', + ROpen, + TCreate = 'r', + RCreate, + TRead = 't', + RRead, + TWrite = 'v', + RWrite, + TClunk = 'x', + RClunk, + TRemove = 'z', + RRemove, + TStat = '|', + RStat, + TWStat = '~', + RWStat, +}; + +/* bits in Qid.type */ +enum { + QTDIR = 0x80, /* type bit for directories */ + QTAPPEND = 0x40, /* type bit for append only files */ + QTEXCL = 0x20, /* type bit for exclusive use files */ + QTMOUNT = 0x10, /* type bit for mounted channel */ + QTAUTH = 0x08, /* type bit for authentication file */ + QTTMP = 0x04, /* type bit for non-backed-up file */ + QTSYMLINK = 0x02, /* type bit for symbolic link */ + QTFILE = 0x00 /* type bits for plain file */ +}; + +/* from libc.h in p9p */ +enum { + OREAD = 0, /* open for read */ + OWRITE = 1, /* write */ + ORDWR = 2, /* read and write */ + OEXEC = 3, /* execute, == read but check execute permission */ + OTRUNC = 16, /* or'ed in (except for exec), truncate file first */ + OCEXEC = 32, /* or'ed in, close on exec */ + ORCLOSE = 64, /* or'ed in, remove on close */ + ODIRECT = 128, /* or'ed in, direct access */ + ONONBLOCK = 256, /* or'ed in, non-blocking call */ + OEXCL = 0x1000, /* or'ed in, exclusive use (create only) */ + OLOCK = 0x2000, /* or'ed in, lock after opening */ + OAPPEND = 0x4000 /* or'ed in, append only */ +}; + +/* Larger than int, can't be enum */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMTMP 0x04000000 /* mode bit for non-backed-up file */ + +typedef struct { + unsigned char type; + unsigned long version; + unsigned long path; +} Qid; + +typedef struct { + unsigned int type; + unsigned long dev; + Qid qid; + unsigned long mode; + unsigned long atime; + unsigned long mtime; + unsigned long length; + char *name; + char *uid; + char *gid; + char *muid; +} Stat; + +typedef struct { + unsigned char type; + unsigned long tag; + unsigned long fid; + + union { + struct {/* Tversion, Rversion */ + unsigned long msize; +// char *version; + }; + struct { /* Tflush */ + unsigned int oldtag; + }; + struct { /* Rerror */ + char *ename; + }; + struct { /* Ropen, Rcreate */ + Qid qid; /* +Rattach */ +// unsigned long iounit; + }; + struct { /* Rauth */ + Qid aqid; + }; + struct { /* Tauth, Tattach */ + unsigned long afid; + char *uname; + char *aname; + }; + struct { /* Tcreate */ + unsigned long perm; + char *name; + unsigned char mode; /* +Topen */ + }; + struct { /* Twalk */ + unsigned long newfid; + unsigned int nwname; + char *wname[MAX_WELEM]; + }; + struct { /* Rwalk */ + unsigned int nwqid; + Qid wqid[MAX_WELEM]; + }; + struct { + unsigned long offset; /* Tread, Twrite */ + unsigned long count; /* Tread, Twrite, Rread */ +// char *data; /* Twrite, Rread */ + }; + struct { /* Rstat */ + unsigned long nstat; + Stat stat; + }; + struct { /* Twstat */ + Stat st; + }; + }; +} Fcall; + +typedef struct { +// Fcall* (*version)(Fcall*); +// Fcall* (*auth)(Fcall*); + Fcall* (*attach)(Fcall*); + Fcall* (*flush)(Fcall*); + Fcall* (*walk)(Fcall*); + Fcall* (*open)(Fcall*); + Fcall* (*create)(Fcall*); + Fcall* (*read)(Fcall*, unsigned char*); + Fcall* (*write)(Fcall*, unsigned char*); + Fcall* (*clunk)(Fcall*); + Fcall* (*remove)(Fcall*); + Fcall* (*stat)(Fcall*); + Fcall* (*wstat)(Fcall*); +} Callbacks; + +int putstat(unsigned char *buffer, unsigned long index, Stat *stat); +unsigned long proc9p(unsigned char *msg, unsigned long size, Callbacks *cb); + +/* fid mapping functions */ + +typedef struct hentry { + unsigned long id; + unsigned long data; + void *aux; + int len; + struct hentry *next; + struct hentry *prev; +} hentry; + +typedef struct htable { + unsigned char length; + struct hentry **data; +} htable; + +struct hentry* fs_fid_find(unsigned long id); +struct hentry* fs_fid_add(unsigned long id, unsigned long data); +void fs_fid_del(unsigned long id); +void fs_fid_init(int l); + +extern char Etoobig[]; +extern char Ebadtype[]; +extern char Enofile[]; +extern char Eperm[]; + +#endif diff --git a/fs.c b/fs.c new file mode 100644 index 0000000..9f5eb37 --- /dev/null +++ b/fs.c @@ -0,0 +1,324 @@ +#include +#include +#include "NinePea.h" +#include "v4l.h" +#include "jpeg.h" + +Fcall ofcall; +char errstr[64]; +char Snone[] = "none"; +char Sroot[] = "/"; +char Sjpeg[] = "jpeg"; + +enum { + Qroot = 0, + Qjpeg, + QNUM, +}; + +/* 9p handlers */ + +Fcall* +fs_attach(Fcall *ifcall) { + ofcall.qid.type = QTDIR | QTTMP; + ofcall.qid.version = 0; + ofcall.qid.path = Qroot; + + fs_fid_add(ifcall->fid, Qroot); + + return &ofcall; +} + +Fcall* +fs_walk(Fcall *ifcall) { + unsigned long path; + struct hentry *ent = fs_fid_find(ifcall->fid); + int i; + + if (!ent) { + ofcall.type = RError; + ofcall.ename = Enofile; + + return &ofcall; + } + + path = ent->data; + + for (i = 0; i < ifcall->nwname; i++) { + switch(ent->data) { + case Qroot: + if (!strcmp(ifcall->wname[i], "jpeg")) { + ofcall.wqid[i].type = QTFILE; + ofcall.wqid[i].version = 0; + ofcall.wqid[i].path = path = Qjpeg; + } + else if (!strcmp(ifcall->wname[i], ".")) { + ofcall.wqid[i].type = QTDIR; + ofcall.wqid[i].version = 0; + ofcall.wqid[i].path = path = Qroot; + } + else { + ofcall.type = RError; + ofcall.ename = Enofile; + return &ofcall; + } + break; + default: + ofcall.type = RError; + ofcall.ename = Enofile; + + return &ofcall; + break; + } + } + + ofcall.nwqid = i; + + if (fs_fid_find(ifcall->newfid) != NULL) { + ofcall.type = RError; + strcpy(errstr, "new fid exists"); + ofcall.ename = errstr; + return &ofcall; + } + + fs_fid_add(ifcall->newfid, path); + + return &ofcall; +} + +Fcall* +fs_stat(Fcall *ifcall) { + struct hentry *ent; + + if ((ent = fs_fid_find(ifcall->fid)) == NULL) { + ofcall.type = RError; + ofcall.ename = Enofile; + + return &ofcall; + } + + ofcall.stat.qid.type = QTTMP; + ofcall.stat.mode = 0666 | DMTMP; + ofcall.stat.atime = ofcall.stat.mtime = ofcall.stat.length = 0; + ofcall.stat.uid = Snone; + ofcall.stat.gid = Snone; + ofcall.stat.muid = Snone; + + switch (ent->data) { + case Qroot: + ofcall.stat.qid.type |= QTDIR; + ofcall.stat.qid.path = Qroot; + ofcall.stat.mode |= 0111 | DMDIR; + ofcall.stat.name = Sroot; + break; + case Qjpeg: + ofcall.stat.qid.path = Qjpeg; + ofcall.stat.name = Sjpeg; + break; + } + + return &ofcall; +} + +Fcall* +fs_clunk(Fcall *ifcall) { + fs_fid_del(ifcall->fid); + + return ifcall; +} + +Fcall* +fs_open(Fcall *ifcall) { + int len; + struct hentry *cur = fs_fid_find(ifcall->fid); + + if (cur == NULL) { + ofcall.type = RError; + ofcall.ename = Enofile; + + return &ofcall; + } + + ofcall.qid.type = QTFILE; + ofcall.qid.path = cur->data; + + if (cur->data == Qroot) + ofcall.qid.type = QTDIR; + if (cur->data == Qjpeg) { + read_frame(); + len = compressjpg(RGB, 640, 480); + cur->aux = malloc(len); + memcpy(cur->aux, RGB, len); + cur->len = len; + } + + return &ofcall; +} + +Fcall* +fs_read(Fcall *ifcall, unsigned char *out) { + struct hentry *cur = fs_fid_find(ifcall->fid); + Stat stat; + char tmpstr[32]; + unsigned int i; + unsigned long value; + + if (cur == NULL) { + ofcall.type = RError; + ofcall.ename = Enofile; + } + else if (((unsigned long)cur->data) == Qroot) { + if (ifcall->offset == 0) { + stat.type = 0; + stat.dev = 0; + stat.qid.type = QTFILE; + stat.mode = 0666; + stat.atime = 0; + stat.mtime = 0; + stat.length = 0; + + stat.qid.path = Qjpeg; + stat.name = Sjpeg; + stat.uid = Snone; + stat.gid = Snone; + stat.muid = Snone; + ofcall.count = putstat(out, 0, &stat); + } + } + else if (((unsigned long)cur->data) == Qjpeg) { + i = cur->len - ifcall->offset; + if (i > MAX_IO) + i = MAX_IO; + if (i > 0) + memcpy(out, &((unsigned char*)cur->aux)[ifcall->offset], i); + ofcall.count = i; + } + else { + ofcall.type = RError; + ofcall.ename = Enofile; + } + + return &ofcall; +} + +Fcall* +fs_create(Fcall *ifcall) { + ofcall.type = RError; + ofcall.ename = Eperm; + + return &ofcall; +} + +Fcall* +fs_write(Fcall *ifcall, unsigned char *in) { + struct hentry *cur = fs_fid_find(ifcall->fid); + char *ep = &in[ifcall->count]; + + ofcall.count = ifcall->count; + + if (cur == NULL) { + ofcall.type = RError; + ofcall.ename = Enofile; + } + else if (((unsigned long)cur->data) == Qroot) { + ofcall.type = RError; + ofcall.ename = Eperm; + } + else { + ofcall.type = RError; + ofcall.ename = Eperm; + } + + return &ofcall; +} + +Fcall* +fs_remove(Fcall *ifcall) { + ofcall.type = RError; + ofcall.ename = Eperm; + + return &ofcall; +} + +Fcall* +fs_flush(Fcall *ifcall) { + return ifcall; +} + +Fcall* +fs_wstat(Fcall *ifcall) { + return ifcall; +} + +Callbacks callbacks; + +void +sysfatal(int code) +{ + fprintf(stderr, "sysfatal: %d\n", code); + exit(code); +} + +int main() { + fs_fid_init(64); + + // this is REQUIRED by proc9p (see below) + callbacks.attach = fs_attach; + callbacks.flush = fs_flush; + callbacks.walk = fs_walk; + callbacks.open = fs_open; + callbacks.create = fs_create; + callbacks.read = fs_read; + callbacks.write = fs_write; + callbacks.clunk = fs_clunk; + callbacks.remove = fs_remove; + callbacks.stat = fs_stat; + callbacks.wstat = fs_wstat; + + unsigned char msg[MAX_MSG+1]; + unsigned int msglen = 0; + unsigned int r = 0; + + dev_name = "/dev/video0"; + + open_device(); + init_device(); + start_capturing(); + + for(;;){ + unsigned long i; + + while (r < 5) { + msg[r++] = getchar(); + } + + i = 0; + get4(msg, i, msglen); + + // sanity check + if (msg[i] & 1 || msglen > MAX_MSG || msg[i] < TVersion || msg[i] > TWStat) { + sysfatal(3); + } + + while (r < msglen) { + msg[r++] = getchar(); + } + + memset(&ofcall, 0, sizeof(ofcall)); + + // proc9p accepts valid 9P msgs of length msglen, + // processes them using callbacks->various(functions); + // returns variable out's msglen + msglen = proc9p(msg, msglen, &callbacks); + + write(1, msg, msglen); + fflush(stdout); + + r = msglen = 0; + } + + stop_capturing(); + uninit_device(); + close_device(); +} + diff --git a/jpeg.c b/jpeg.c new file mode 100644 index 0000000..5f0ce87 --- /dev/null +++ b/jpeg.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +int +compressjpg(unsigned char *image, int width, int height) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + unsigned char *buf; + size_t buflen = 0; + int length; + + cinfo.err = jpeg_std_error( &jerr ); + jpeg_create_compress(&cinfo); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = 3; + + length = width * height * 3; + + jpeg_mem_dest( &cinfo, &buf, &buflen ); + jpeg_set_defaults( &cinfo ); + + jpeg_start_compress( &cinfo, TRUE ); + while( cinfo.next_scanline < cinfo.image_height ) { + row_pointer[0] = &image[ cinfo.next_scanline * cinfo.image_width * cinfo.input_components]; + + jpeg_write_scanlines( &cinfo, row_pointer, 1 ); + } + + jpeg_finish_compress( &cinfo ); + + if (buflen < length) + length = buflen; + memcpy(image, buf, length); + + jpeg_destroy_compress( &cinfo ); + free(buf); + + return length; +} + diff --git a/jpeg.h b/jpeg.h new file mode 100644 index 0000000..3c36992 --- /dev/null +++ b/jpeg.h @@ -0,0 +1,2 @@ +int compressjpg(unsigned char *, int, int); + diff --git a/v4l.c b/v4l.c new file mode 100644 index 0000000..f59af0b --- /dev/null +++ b/v4l.c @@ -0,0 +1,703 @@ +/* + * V4L2 video capture example + * + * This program can be used and distributed without restrictions. + * + * This program is provided with the V4L2 API + * see http://linuxtv.org/docs.php for more information + */ + +#include +#include +#include +#include + +#include /* getopt_long() */ + +#include /* low-level i/o */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +enum io_method { + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, +}; + +struct buffer { + void *start; + size_t length; +}; + +char *dev_name; +char *rgbbuffer; +static enum io_method io = IO_METHOD_MMAP; +static int fd = -1; +struct buffer *buffers; +static unsigned int n_buffers; +static int out_buf; +static int force_format = 1; +static int frame_count = 70; +unsigned char *RGB; + +static void errno_exit(const char *s) +{ + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); + exit(EXIT_FAILURE); +} + +static int xioctl(int fh, unsigned long int request, void *arg) +{ + int r; + + do { + r = ioctl(fh, request, arg); + } while (-1 == r && EINTR == errno); + + return r; +} + +int CLIPVALUE(int x) { + if (x < 0) + return 0; + else if (x > 255) + return 255; + return x; +} + +void YUV2RGB(const unsigned char y, const unsigned char u,const unsigned char v, unsigned char* r, + unsigned char* g, unsigned char* b) +{ + const int y2 = (int)y; + const int u2 = (int)u - 128; + const int v2 = (int)v - 128; + //std::cerr << "YUV=("<> 16); + // int g2 = y2 - ( ((u2*22544) + (v2*46793)) >> 16 ); + // int b2 = y2 + ( (u2*115999) >> 16); + // This is an adjusted version (UV spread out a bit) + + int r2 = y2 + ((v2 * 37221) >> 15); + int g2 = y2 - (((u2 * 12975) + (v2 * 18949)) >> 15); + int b2 = y2 + ((u2 * 66883) >> 15); + //std::cerr << " RGB=("< 0) { + for (;;) { + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select(fd + 1, &fds, NULL, NULL, &tv); + + if (-1 == r) { + if (EINTR == errno) + continue; + errno_exit("select"); + } + + if (0 == r) { + fprintf(stderr, "select timeout\n"); + exit(EXIT_FAILURE); + } + + if (read_frame()) + break; + /* EAGAIN - continue select loop. */ + } + } +} + +void stop_capturing(void) +{ + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) + errno_exit("VIDIOC_STREAMOFF"); + break; + } +} + +void start_capturing(void) +{ + unsigned int i; + enum v4l2_buf_type type; + + switch (io) { + case IO_METHOD_READ: + /* Nothing to do. */ + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)buffers[i].start; + buf.length = buffers[i].length; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); + } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); + break; + } +} + +void uninit_device(void) +{ + unsigned int i; + + switch (io) { + case IO_METHOD_READ: + free(buffers[0].start); + break; + + case IO_METHOD_MMAP: + for (i = 0; i < n_buffers; ++i) + if (-1 == munmap(buffers[i].start, buffers[i].length)) + errno_exit("munmap"); + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) + free(buffers[i].start); + break; + } + + free(buffers); +} + +static void init_read(unsigned int buffer_size) +{ + buffers = calloc(1, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + buffers[0].length = buffer_size; + buffers[0].start = malloc(buffer_size); + + if (!buffers[0].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } +} + +static void init_mmap(void) +{ + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, "%s does not support " + "memory mapping\n", dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + + if (req.count < 2) { + fprintf(stderr, "Insufficient buffer memory on %s\n", + dev_name); + exit(EXIT_FAILURE); + } + + buffers = calloc(req.count, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { + struct v4l2_buffer buf; + + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) + errno_exit("VIDIOC_QUERYBUF"); + + buffers[n_buffers].length = buf.length; + buffers[n_buffers].start = + mmap(NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + fd, buf.m.offset); + + if (MAP_FAILED == buffers[n_buffers].start) + errno_exit("mmap"); + } +} + +static void init_userp(unsigned int buffer_size) +{ + struct v4l2_requestbuffers req; + + CLEAR(req); + + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + fprintf(stderr, "%s does not support " + "user pointer i/o\n", dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_REQBUFS"); + } + } + + buffers = calloc(4, sizeof(*buffers)); + + if (!buffers) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + + for (n_buffers = 0; n_buffers < 4; ++n_buffers) { + buffers[n_buffers].length = buffer_size; + buffers[n_buffers].start = malloc(buffer_size); + + if (!buffers[n_buffers].start) { + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); + } + } +} + +void init_device(void) +{ + struct v4l2_capability cap; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { + if (EINVAL == errno) { + fprintf(stderr, "%s is no V4L2 device\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_QUERYCAP"); + } + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + fprintf(stderr, "%s is no video capture device\n", + dev_name); + exit(EXIT_FAILURE); + } + + switch (io) { + case IO_METHOD_READ: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf(stderr, "%s does not support read i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + fprintf(stderr, "%s does not support streaming i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + } + + + /* Select video input, video standard and tune here. */ + + + CLEAR(cropcap); + + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; /* reset to default */ + + if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { + switch (errno) { + case EINVAL: + /* Cropping not supported. */ + break; + default: + /* Errors ignored. */ + break; + } + } + } else { + /* Errors ignored. */ + } + + + CLEAR(fmt); + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (force_format) { + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) + errno_exit("VIDIOC_S_FMT"); + + /* Note VIDIOC_S_FMT may change width and height. */ + } else { + /* Preserve original settings as set by v4l2-ctl for example */ + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + } + + switch (io) { + case IO_METHOD_READ: + init_read(fmt.fmt.pix.sizeimage); + break; + + case IO_METHOD_MMAP: + init_mmap(); + break; + + case IO_METHOD_USERPTR: + init_userp(fmt.fmt.pix.sizeimage); + break; + } +} + +void close_device(void) +{ + if (-1 == close(fd)) + errno_exit("close"); + + fd = -1; + + free(RGB); +} + +void open_device(void) +{ + struct stat st; + + if (-1 == stat(dev_name, &st)) { + fprintf(stderr, "Cannot identify '%s': %d, %s\n", + dev_name, errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (!S_ISCHR(st.st_mode)) { + fprintf(stderr, "%s is no device\n", dev_name); + exit(EXIT_FAILURE); + } + + fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); + + if (-1 == fd) { + fprintf(stderr, "Cannot open '%s': %d, %s\n", + dev_name, errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + RGB = (unsigned char*)malloc(1920 * 1080 * 3); +} + +static void usage(FILE *fp, int argc, char **argv) +{ + fprintf(fp, + "Usage: %s [options]\n\n" + "Version 1.3\n" + "Options:\n" + "-d | --device name Video device name [%s]\n" + "-h | --help Print this message\n" + "-m | --mmap Use memory mapped buffers [default]\n" + "-r | --read Use read() calls\n" + "-u | --userp Use application allocated buffers\n" + "-o | --output Outputs stream to stdout\n" + "-f | --format Force format to 640x480 YUYV\n" + "-c | --count Number of frames to grab [%i]\n" + "", + argv[0], dev_name, frame_count); +} + +static const char short_options[] = "d:hmruofc:"; + +static const struct option +long_options[] = { + { "device", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "mmap", no_argument, NULL, 'm' }, + { "read", no_argument, NULL, 'r' }, + { "userp", no_argument, NULL, 'u' }, + { "output", no_argument, NULL, 'o' }, + { "format", no_argument, NULL, 'f' }, + { "count", required_argument, NULL, 'c' }, + { 0, 0, 0, 0 } +}; + +/* +int main(int argc, char **argv) +{ + dev_name = "/dev/video0"; + + for (;;) { + int idx; + int c; + + c = getopt_long(argc, argv, + short_options, long_options, &idx); + + if (-1 == c) + break; + + switch (c) { + case 0: // getopt_long() flag + break; + + case 'd': + dev_name = optarg; + break; + + case 'h': + usage(stdout, argc, argv); + exit(EXIT_SUCCESS); + + case 'm': + io = IO_METHOD_MMAP; + break; + + case 'r': + io = IO_METHOD_READ; + break; + + case 'u': + io = IO_METHOD_USERPTR; + break; + + case 'o': + out_buf++; + break; + + case 'f': + force_format++; + break; + + case 'c': + errno = 0; + frame_count = strtol(optarg, NULL, 0); + if (errno) + errno_exit(optarg); + break; + + default: + usage(stderr, argc, argv); + exit(EXIT_FAILURE); + } + } + + open_device(); + init_device(); + start_capturing(); + mainloop(); + stop_capturing(); + uninit_device(); + close_device(); + fprintf(stderr, "\n"); + return 0; +} +*/ diff --git a/v4l.h b/v4l.h new file mode 100644 index 0000000..14b468e --- /dev/null +++ b/v4l.h @@ -0,0 +1,13 @@ +extern char *dev_name; +extern unsigned char *RGB; + +void open_device(); +void init_device(); +void start_capturing(); +void stop_capturing(); +void uninit_device(); +void close_device(); + +int read_frame(); + +