ref: 2da5e135dc5d1d50ac2a760fe1bc693b6008087c
parent: a2d8dcfd8235547ca1e14dcca42de70e06a0ec96
author: aiju <devnull@localhost>
date: Wed Mar 7 05:06:18 EST 2018
add nusb/cam
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/cam.c
@@ -1,0 +1,374 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <bio.h>
+#include "usb.h"
+#include "uvc.h"
+#include "dat.h"
+#include "fns.h"
+
+char user[] = "cam";
+
+void printVCHeader(void *vp);
+void printVCInputTerminal(void *vp);
+void printVCOutputTerminal(void *vp);
+void printVCCameraTerminal(void *vp);
+void printVCSelectorUnit(void *vp);
+void printVCProcessingUnit(void *vp);
+void printVCEncodingUnit(void *vp);
+void printVCExtensionUnit(void *vp);
+void printVSInputHeader(void *vp);
+void printVSOutputHeader(void *vp);
+void printVSStillFrame(void *vp);
+void printVSUncompressedFormat(void *vp);
+void printVSUncompressedFrame(void *vp);
+void printVSColorFormat(void *vp);
+void printProbeControl(void *vp);
+
+Cam *cams;
+int nunit;
+VCUnit **unit;
+Iface **unitif;
+
+int debug;
+
+int
+getframedesc(Cam *c, int i, int j, Format **fp, VSUncompressedFrame **gp)
+{
+ Format *f;
+
+ if(i >= c->nformat) return -1;
+ f = c->format[i];
+ if(f == nil) return -1;
+ if(j >= f->nframe) return -1;
+ if(f->frame[j] == nil) return -1;
+ if(fp != nil) *fp = f;
+ if(gp != nil) *gp = f->frame[j];
+ return 0;
+}
+
+static void
+parsevcdesc(Dev *dev, Desc *d)
+{
+ VCDescriptor *vdp;
+ static Cam *cam;
+ static Format *format;
+ int i;
+ Format *f;
+
+ if(d == nil) return;
+ vdp = (VCDescriptor *) &d->data;
+ if(vdp->bDescriptorType != 0x24) return;
+ if(Class(d->iface->csp) != CC_VIDEO) return;
+ switch(Subclass(d->iface->csp)){
+ case SC_VIDEOSTREAMING:
+ switch(vdp->bDescriptorSubtype){
+ case VS_INPUT_HEADER:
+ format = nil;
+ cam = emallocz(sizeof(Cam), 1);
+ cam->dev = dev;
+ cam->iface = d->iface;
+ cam->hdr = (VSInputHeader *) vdp;
+ cam->next = cams;
+ cams = cam;
+ break;
+ case VS_FORMAT_UNCOMPRESSED:
+ if(cam == nil) return;
+ f = emallocz(sizeof(Format), 1);
+ f->desc = (void*)vdp;
+ i = f->desc->bFormatIndex;
+ if(i >= cam->nformat){
+ cam->format = realloc(cam->format, (i + 1) * sizeof(void *));
+ memset(cam->format + cam->nformat, 0, (i - cam->nformat) * sizeof(void *));
+ cam->nformat = i + 1;
+ }
+ cam->format[i] = f;
+ if(format == nil) cam->pc.bFormatIndex = i;
+ format = f;
+ break;
+ case VS_FRAME_UNCOMPRESSED:
+ if(cam == nil || cam->nformat == 0) return;
+ f = format;
+ i = ((VSUncompressedFrame*)vdp)->bFrameIndex;
+ if(i >= f->nframe){
+ f->frame = realloc(f->frame, (i + 1) * sizeof(void *));
+ memset(f->frame + f->nframe, 0, (i - f->nframe) * sizeof(void *));
+ f->nframe = i + 1;
+ }
+ f->frame[i] = (void*)vdp;
+ break;
+ }
+ break;
+ case SC_VIDEOCONTROL:
+ switch(vdp->bDescriptorSubtype){
+ case VC_INPUT_TERMINAL:
+ case VC_OUTPUT_TERMINAL:
+ case VC_SELECTOR_UNIT:
+ case VC_PROCESSING_UNIT:
+ case VC_ENCODING_UNIT:
+ case VC_EXTENSION_UNIT:
+ i = ((VCUnit*)vdp)->bUnitID;
+ if(i >= nunit){
+ unit = realloc(unit, (i + 1) * sizeof(void *));
+ unitif = realloc(unitif, (i + 1) * sizeof(Iface *));
+ memset(unit + nunit, 0, (i - nunit) * sizeof(void *));
+ memset(unitif + nunit, 0, (i - nunit) * sizeof(void *));
+ nunit = i + 1;
+ }
+ unit[i] = (void*)vdp;
+ unitif[i] = d->iface;
+ break;
+ }
+ break;
+ }
+}
+
+static void
+createfiles(File *root, char *devname, Cam *c)
+{
+ char buf[512];
+ File *d;
+
+ snprint(buf, sizeof(buf), "cam%s.%d", devname, c->iface->id);
+ d = createfile(root, buf, user, DMDIR|0555, c);
+ c->ctlfile = createfile(d, "ctl", user, 0666, c);
+ c->formatsfile = createfile(d, "formats", user, 0444, c);
+ c->videofile = createfile(d, "video", user, 0444, c);
+ c->framefile = createfile(d, "frame", user, 0444, c);
+ c->descfile = createfile(d, "desc", user, 0444, c);
+}
+
+static char *
+formatread(Cam *c)
+{
+ int i, j, k;
+ Fmt fmt;
+ Format *f;
+ VSUncompressedFrame *g;
+ char buf[5];
+
+ fmtstrinit(&fmt);
+ for(i = 0; i < c->nformat; i++){
+ f = c->format[i];
+ if(f == nil) continue;
+ memcpy(buf, f->desc->guidFormat, 4);
+ buf[4] = 0;
+ for(j = 0; j < f->nframe; j++){
+ if((g = f->frame[j]) == nil) continue;
+ fmtprint(&fmt, "%dx%dx%d-%s ", GET2(g->wWidth), GET2(g->wHeight), f->desc->bBitsPerPixel, buf);
+ if(g->bFrameIntervalType == 0)
+ fmtprint(&fmt, "%.2f-%.2f\n", 10e6 / (u32int)GET4(g->dwFrameInterval[0]), 10e6 / (u32int)GET4(g->dwFrameInterval[1]));
+ else
+ for(k = 0; k < g->bFrameIntervalType; k++)
+ fmtprint(&fmt, "%.2f%c", 10e6 / (u32int)GET4(g->dwFrameInterval[k]), k == g->bFrameIntervalType - 1 ? '\n' : ',');
+ }
+ }
+ return fmtstrflush(&fmt);
+}
+
+static char *
+descread(Cam *c)
+{
+ Fmt fmt;
+ int i;
+ Usbdev *ud;
+ Desc *d;
+ VCDescriptor *vdp;
+
+ ud = c->dev->usb;
+ fmtstrinit(&fmt);
+ for(i = 0; i < nelem(ud->ddesc); i++){
+ d = ud->ddesc[i];
+ if(d == nil) continue;
+ vdp = (VCDescriptor *) &d->data;
+ if(vdp->bDescriptorType != 0x24) continue;
+ if(Class(d->iface->csp) != CC_VIDEO) continue;
+ printDescriptor(&fmt, d->iface, vdp);
+ }
+ return fmtstrflush(&fmt);
+}
+
+typedef struct ReadState ReadState;
+struct ReadState {
+ int len;
+ char *buf;
+};
+
+static void
+strread(Req *req, char *str, int len)
+{
+ ReadState *rs;
+
+ if(str == nil)
+ return;
+ rs = emallocz(sizeof(ReadState), 1);
+ rs->len = len < 0 ? strlen(str) : len;
+ rs->buf = str;
+ req->fid->aux = rs;
+}
+
+static void
+fsread(Req *req)
+{
+ File *f;
+ Cam *c;
+ ReadState *rs;
+
+ if(req->fid == nil || req->fid->file == nil || req->fid->file->aux == nil){
+ respond(req, "the front fell off");
+ return;
+ }
+ f = req->fid->file;
+ c = f->aux;
+ if(req->fid->aux == nil)
+ if(f == c->formatsfile)
+ strread(req, formatread(c), -1);
+ else if(f == c->ctlfile)
+ strread(req, ctlread(c), -1);
+ else if(f == c->descfile)
+ strread(req, descread(c), -1);
+ else if(f == c->videofile || f == c->framefile){
+ videoread(req, c, 1);
+ return;
+ }
+ if((rs = req->fid->aux) == nil){
+ respond(req, "the front fell off");
+ return;
+ }
+ readbuf(req, rs->buf, rs->len);
+ respond(req, nil);
+}
+
+static void
+fswrite(Req *req)
+{
+ File *f;
+ Cam *c;
+ char *s;
+ int n;
+
+ if(req->fid == nil || req->fid->file == nil || req->fid->file->aux == nil){
+err: respond(req, "the front fell off");
+ return;
+ }
+ f = req->fid->file;
+ c = f->aux;
+ if(f != c->ctlfile)
+ goto err;
+ for(n = 0; n < req->ifcall.count; n++)
+ if(req->ifcall.data[n] == '\n')
+ break;
+ s = emallocz(n+1, 0);
+ memcpy(s, req->ifcall.data, n);
+ s[n] = 0;
+ werrstr("invalid argument");
+ if(ctlwrite(c, s) < 0)
+ responderror(req);
+ else
+ respond(req, nil);
+ free(s);
+}
+
+static void
+fsdestroyfid(Fid *fid)
+{
+ ReadState *rs;
+
+ rs = fid->aux;
+ if(rs != nil){
+ free(rs->buf);
+ free(rs);
+ }
+ if(fid->file != nil && fid->file->aux != nil && (fid->file == ((Cam*)fid->file->aux)->videofile || fid->file == ((Cam*)fid->file->aux)->framefile))
+ videoclose(fid->file->aux);
+}
+
+static void
+fsopen(Req *req)
+{
+ File *f;
+ Cam *c;
+
+ if((req->ofcall.qid.type & QTDIR) != 0){
+ respond(req, nil);
+ return;
+ }
+ if(req->fid == nil || req->fid->file == nil || req->fid->file->aux == nil){
+ respond(req, "the front fell off");
+ return;
+ }
+ f = req->fid->file;
+ c = f->aux;
+ if(f == c->videofile || f == c->framefile)
+ if(videoopen(c, f == c->framefile) < 0){
+ responderror(req);
+ return;
+ }
+ respond(req, nil);
+}
+
+static void
+fsflush(Req *req)
+{
+ if(req->oldreq->fid->file != nil && req->oldreq->fid->file->aux != nil && (((Cam*)req->oldreq->fid->file->aux)->videofile == req->oldreq->fid->file || ((Cam*)req->oldreq->fid->file->aux)->framefile == req->oldreq->fid->file))
+ videoflush(req->oldreq, req->oldreq->fid->file->aux);
+ respond(req, nil);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-d] devid\n", argv0);
+ threadexits("usage");
+}
+
+static Srv fs = {
+ .open = fsopen,
+ .read = fsread,
+ .write = fswrite,
+ .flush = fsflush,
+ .destroyfid = fsdestroyfid,
+};
+
+void
+threadmain(int argc, char* argv[])
+{
+ int i;
+ Dev *d;
+ Usbdev *ud;
+ char buf[512];
+ Cam *c;
+
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 1)
+ usage();
+ d = getdev(*argv);
+ if(d == nil)
+ sysfatal("getdev: %r");
+ ud = d->usb;
+ for(i = 0; i < nelem(ud->ddesc); i++)
+ parsevcdesc(d, ud->ddesc[i]);
+ if(cams == nil)
+ sysfatal("no input streams");
+ for(c = cams; c != nil; c = c->next)
+ if(c->nformat > 0){
+ c->pc.bFrameIndex = c->format[c->pc.bFormatIndex]->desc->bDefaultFrameIndex;
+ if(c->pc.bFrameIndex < c->format[c->pc.bFormatIndex]->nframe && c->format[c->pc.bFormatIndex]->frame[c->pc.bFrameIndex] != nil)
+ PUT4(c->pc.dwFrameInterval, GET4(c->format[c->pc.bFormatIndex]->frame[c->pc.bFrameIndex]->dwDefaultFrameInterval));
+ }
+ fs.tree = alloctree(user, "usb", DMDIR|0555, nil);
+ for(c = cams; c != nil; c = c->next)
+ createfiles(fs.tree->root, d->hname, c);
+ snprint(buf, sizeof buf, "%d.cam", d->id);
+ threadpostsharesrv(&fs, nil, "usb", buf);
+ threadexits(nil);
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/ctl.c
@@ -1,0 +1,532 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "usb.h"
+#include "uvc.h"
+#include "dat.h"
+#include "fns.h"
+
+typedef struct Param Param;
+
+enum {
+ PARAMSPEC,
+ PARAMCT,
+ PARAMPU,
+};
+
+struct Param {
+ char *name;
+ int type;
+ int cs;
+ int len;
+ int flag;
+ char *(*read)(Cam *, int, Param *);
+ int (*write)(Cam *, int, Param *, char **, int);
+ void *auxp;
+ int auxi;
+};
+
+void
+errorcode(Dev *d, int term)
+{
+ uchar val;
+ char *str[] = {
+ "No error",
+ "Not ready",
+ "Wrong state",
+ "Power",
+ "Out of range",
+ "Invalid unit",
+ "Invalid control",
+ "Invalid Request",
+ "Invalid value within range"
+ };
+ if(usbcmd(d, 0xA1, GET_CUR, VC_REQUEST_ERROR_CODE_CONTROL << 8, (uchar)term, &val, 1) <= 0)
+ return;
+ if(val < nelem(str))
+ werrstr("%s", str[val]);
+}
+
+int
+infocheck(Cam *c, int term, Param *p)
+{
+ uchar val;
+
+ if(usbcmd(c->dev, 0xA1, GET_INFO, p->cs << 8, term, &val, 1) <= 0){ errorcode(c->dev, term); return -1; }
+ if((val & 1) == 0){
+ werrstr("GET not supported");
+ return -1;
+ }
+ return 0;
+}
+
+char *
+pboolread(Cam *c, int term, Param *p)
+{
+ uchar val;
+ Dev *d;
+
+ d = c->dev;
+ if(infocheck(c, term, p) < 0) return nil;
+ if(usbcmd(d, 0xA1, GET_CUR, p->cs << 8, term, &val, 1) <= 0){ errorcode(d, term); return nil; }
+ if(val)
+ return strdup("true");
+ return strdup("false");
+}
+
+int
+pboolwrite(Cam *c, int term, Param *p, char **f, int nf)
+{
+ uchar v0, v1;
+
+ if(nf != 1)
+ return -1;
+ v0 = cistrcmp(f[0], "false") == 0 || cistrcmp(f[0], "0") == 0 || cistrcmp(f[0], "no") == 0;
+ v1 = cistrcmp(f[0], "true") == 0 || cistrcmp(f[0], "1") == 0 || cistrcmp(f[0], "yes") == 0;
+ if(!(v0 ^ v1))
+ return -1;
+ if(usbcmd(c->dev, 0x21, SET_CUR, p->cs << 8, term, &v1, 1) <= 0){ errorcode(c->dev, term); return -1; }
+ return 0;
+}
+
+char *
+pintread(Cam *c, int term, Param *p)
+{
+ uchar cur[4], min[4], max[4], res[4];
+ Dev *d;
+
+ d = c->dev;
+ if(infocheck(c, term, p) < 0) return nil;
+ if(usbcmd(d, 0xA1, GET_CUR, p->cs << 8, term, cur, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_MIN, p->cs << 8, term, min, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_RES, p->cs << 8, term, res, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_MAX, p->cs << 8, term, max, p->len) < p->len){ errorcode(d, term); return nil; }
+ switch(p->len){
+ case 1: return smprint("%d %d/%d/%d", (char)cur[0], (char)min[0], (char)res[0], (char)max[0]);
+ case 2: return smprint("%d %d/%d/%d", (short)GET2(cur), (short)GET2(min), (short)GET2(res), (short)GET2(max));
+ case 4: return smprint("%d %d/%d/%d", (int)GET4(cur), (int)GET4(min), (int)GET4(res), (int)GET4(max));
+ }
+ werrstr("pintread: unimplemented length %d", p->len);
+ return nil;
+}
+
+int
+pintwrite(Cam *c, int term, Param *p, char **f, int nf)
+{
+ int v;
+ char *sp;
+ uchar buf[4];
+
+ if(nf != 1) return -1;
+ v = strtol(f[0], &sp, 0);
+ if(*f[0] == 0 || *sp != 0) return -1;
+ buf[0] = v;
+ buf[1] = v >> 8;
+ buf[2] = v >> 16;
+ buf[3] = v >> 24;
+ if(usbcmd(c->dev, 0x21, SET_CUR, p->cs << 8, term, buf, p->len) < p->len){ errorcode(c->dev, term); return -1; }
+ return 0;
+}
+
+char *
+puintread(Cam *c, int term, Param *p)
+{
+ uchar cur[4], min[4], max[4], res[4];
+ Dev *d;
+
+ d = c->dev;
+ if(infocheck(c, term, p) < 0) return nil;
+ if(usbcmd(d, 0xA1, GET_CUR, p->cs << 8, term, cur, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_MIN, p->cs << 8, term, min, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_RES, p->cs << 8, term, res, p->len) < p->len){ errorcode(d, term); return nil; }
+ if(usbcmd(d, 0xA1, GET_MAX, p->cs << 8, term, max, p->len) < p->len){ errorcode(d, term); return nil; }
+ switch(p->len){
+ case 1: return smprint("%ud %ud/%ud/%ud", (uchar)cur[0], (uchar)min[0], (uchar)res[0], (uchar)max[0]);
+ case 2: return smprint("%ud %ud/%ud/%ud", (ushort)GET2(cur), (ushort)GET2(min), (ushort)GET2(res), (ushort)GET2(max));
+ case 4: return smprint("%ud %ud/%ud/%ud", (uint)GET4(cur), (uint)GET4(min), (uint)GET4(res), (uint)GET4(max));
+ }
+ werrstr("pintread: unimplemented length %d", p->len);
+ return nil;
+}
+
+int
+puintwrite(Cam *c, int term, Param *p, char **f, int nf)
+{
+ uint v;
+ char *sp;
+ uchar buf[4];
+
+ if(nf != 1) return -1;
+ v = strtoul(f[0], &sp, 0);
+ if(*f[0] == 0 || *sp != 0) return -1;
+ buf[0] = v;
+ buf[1] = v >> 8;
+ buf[2] = v >> 16;
+ buf[3] = v >> 24;
+ if(usbcmd(c->dev, 0x21, SET_CUR, p->cs << 8, term, buf, p->len) < p->len){ errorcode(c->dev, term); return -1; }
+ return 0;
+}
+
+char *
+penumread(Cam *c, int term, Param *p)
+{
+ uchar cur[4];
+ uint val;
+
+ if(infocheck(c, term, p) < 0) return nil;
+ if(usbcmd(c->dev, 0xA1, GET_CUR, p->cs << 8, term, cur, p->len) < p->len){ errorcode(c->dev, term); return nil; }
+ switch(p->len){
+ case 1: val = cur[0]; break;
+ case 2: val = GET2(cur); break;
+ case 4: val = GET4(cur); break;
+ default:
+ werrstr("pintread: unimplemented length %d", p->len);
+ return nil;
+ }
+ if(val >= p->auxi || ((char**)p->auxp)[val] == nil)
+ return smprint("%d", val);
+ return smprint("%s", ((char**)p->auxp)[val]);
+}
+
+int
+penumwrite(Cam *c, int term, Param *p, char **f, int nf)
+{
+ uint i;
+ uchar buf[4];
+
+ if(nf != 1) return -1;
+ for(i = 0; i < p->auxi; i++)
+ if(cistrcmp(((char**)p->auxp)[i], f[0]) == 0)
+ break;
+ if(i == p->auxi)
+ return -1;
+ buf[0] = i;
+ buf[1] = i >> 8;
+ buf[2] = i >> 16;
+ buf[3] = i >> 24;
+ if(usbcmd(c->dev, 0x21, SET_CUR, p->cs << 8, term, buf, p->len) < p->len){ errorcode(c->dev, term); return -1; }
+ return 0;
+}
+
+char *
+pformatread(Cam *c, int, Param *)
+{
+ Format *f;
+ VSUncompressedFrame *g;
+ char buf[5];
+
+ if(c->pc.bFormatIndex >= c->nformat) goto nope;
+ f = c->format[c->pc.bFormatIndex];
+ if(f == nil) goto nope;
+ if(c->pc.bFrameIndex >= f->nframe) goto nope;
+ g = f->frame[c->pc.bFrameIndex];
+ if(g == nil) goto nope;
+ memcpy(buf, f->desc->guidFormat, 4);
+ buf[4] = 0;
+ return smprint("%dx%dx%d-%s", GET2(g->wWidth), GET2(g->wHeight), f->desc->bBitsPerPixel, buf);
+nope:
+ return smprint("#%d,%d", c->pc.bFormatIndex, c->pc.bFrameIndex);
+}
+
+void
+frameinterval(Cam *c, VSUncompressedFrame *f, double t)
+{
+ double δ, minδ;
+ int i, mini;
+ uint min, max, step, val;
+
+ if(f->bFrameIntervalType == 0){
+ min = GET4(f->dwFrameInterval[0]);
+ max = GET4(f->dwFrameInterval[1]);
+ step = GET4(f->dwFrameInterval[2]);
+ if(t <= min)
+ val = min;
+ else if(t >= max)
+ val = max;
+ else{
+ val = floor((t - min) / step) * step + min;
+ if(t >= val + step / 2.0)
+ t += step;
+ }
+ }else{
+ mini = -1;
+ for(i = 0; i < f->bFrameIntervalType; i++){
+ δ = fabs(((u32int)GET4(f->dwFrameInterval[i])) - t);
+ if(mini < 0 || δ < minδ){
+ mini = i;
+ minδ = δ;
+ }
+ }
+ assert(mini >= 0);
+ val = GET4(f->dwFrameInterval[mini]);
+ }
+ PUT4(c->pc.dwFrameInterval, val);
+}
+
+int
+findres(Cam *c, int w, int h, int fr)
+{
+ Format *f;
+ VSUncompressedFrame *g;
+ int i;
+
+ if(fr >= c->nformat || (f = c->format[fr]) == nil) return -1;
+ for(i = 0; i < f->nframe; i++){
+ g = f->frame[i];
+ if(g == nil) continue;
+ if(GET2(g->wWidth) == w && GET2(g->wHeight) == h)
+ return i;
+ }
+ return -1;
+}
+
+int
+pformatwrite(Cam *c, int, Param *, char **args, int nargs)
+{
+ int w, h, bpp;
+ char *p;
+ int i;
+ int j;
+ char *q;
+ Format *f;
+
+ if(nargs != 1) return -1;
+ p = args[0];
+ if(*p == 0) return -1;
+ w = strtol(p, &p, 0);
+ if(*p != 'x') return -1;
+ h = strtol(p + 1, &q, 0);
+ if(q == p + 1) return -1;
+ p = q;
+ if(*p == 0){
+ j = c->pc.bFormatIndex;
+ i = findres(c, w, h, j);
+ if(i < 0)
+ for(j = 0; j < c->nformat; j++){
+ i = findres(c, w, h, j);
+ if(i >= 0) break;
+ }
+ }else{
+ if(*p != 'x' || *++p == '-') return -1;
+ bpp = strtol(p, &p, 0);
+ if(*p != '-') return -1;
+ if(strlen(p) != 4) return -1;
+ i = -1;
+ for(j = 0; j < c->nformat; j++){
+ if((f = c->format[j]) == nil) continue;
+ if(f->desc->bBitsPerPixel != bpp) continue;
+ if(memcmp(f->desc->guidFormat, p, 4) != 0) continue;
+ i = findres(c, w, h, j);
+ if(i >= 0) break;
+ }
+ }
+ if(i < 0)
+ return -1;
+ if(c->active != 0){
+ werrstr("camera active");
+ return -1;
+ }
+ c->pc.bFormatIndex = j;
+ c->pc.bFrameIndex = i;
+ frameinterval(c, c->format[j]->frame[i], GET4(c->pc.dwFrameInterval));
+ return 0;
+}
+
+char *
+pfpsread(Cam *c, int, Param *)
+{
+ if(GET4(c->pc.dwFrameInterval) == 0)
+ return smprint("?");
+ return smprint("%.2f", 10e6 / GET4(c->pc.dwFrameInterval));
+}
+
+int
+pfpswrite(Cam *c, int, Param *, char **args, int nargs)
+{
+ double d, t;
+ char *sp;
+ VSUncompressedFrame *f;
+
+ if(nargs != 1) return -1;
+ d = strtod(args[0], &sp);
+ if(*args[0] == 0 || *sp != 0) return -1;
+ if(getframedesc(c, c->pc.bFormatIndex, c->pc.bFrameIndex, nil, &f) < 0){
+ werrstr("invalid format active");
+ return -1;
+ }
+ if(isNaN(d) || isInf(d, 1) || d <= 0) return -1;
+ if(c->active != 0){
+ werrstr("camera active");
+ return -1;
+ }
+ t = 10e6 / d;
+ frameinterval(c, f, t);
+ return 0;
+}
+
+//static char *autoexposure[] = {"manual", "auto", "shutter", "aperture"};
+static char *powerlinefrequency[] = {"disabled", "50", "60", "auto"};
+
+static Param params[] = {
+ {"format", PARAMSPEC, -1, -1, -1, pformatread, pformatwrite},
+ {"fps", PARAMSPEC, -1, -1, -1, pfpsread, pfpswrite},
+ {"progressive", PARAMCT, CT_SCANNING_MODE_CONTROL, 1, 0, pboolread, pboolwrite},
+// {"auto-exposure-mode", PARAMCT, CT_AE_MODE_CONTROL, 1, 1, pbitread, pbitwrite, autoexposure, nelem(autoexposure)},
+ {"auto-exposure-priority", PARAMCT, CT_AE_PRIORITY_CONTROL, 1, 2, pboolread, pboolwrite},
+ {"exposure-time", PARAMCT, CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, 4, 3, puintread, puintwrite},
+ {"focus", PARAMCT, CT_FOCUS_ABSOLUTE_CONTROL, 2, 5, puintread, puintwrite},
+ {"focus-simple", PARAMCT, CT_FOCUS_SIMPLE_CONTROL, 1, 19, puintread, puintwrite},
+ {"focus-auto", PARAMCT, CT_FOCUS_AUTO_CONTROL, 1, 17, pboolread, pboolwrite},
+ {"iris", PARAMCT, CT_IRIS_ABSOLUTE_CONTROL, 2, 7, puintread, puintwrite},
+ {"zoom", PARAMCT, CT_ZOOM_ABSOLUTE_CONTROL, 2, 9, puintread, puintwrite},
+ {"backlight-compensation", PARAMPU, PU_BACKLIGHT_COMPENSATION_CONTROL, 2, 8, puintread, puintwrite},
+ {"brightness", PARAMPU, PU_BRIGHTNESS_CONTROL, 2, 0, pintread, pintwrite},
+ {"contrast", PARAMPU, PU_CONTRAST_CONTROL, 2, 1, puintread, puintwrite},
+ {"contrast-auto", PARAMPU, PU_CONTRAST_AUTO_CONTROL, 1, 18, pboolread, pboolwrite},
+ {"gain", PARAMPU, PU_GAIN_CONTROL, 2, 9, puintread, puintwrite},
+ {"powerline-frequency", PARAMPU, PU_POWER_LINE_FREQUENCY_CONTROL, 1, 10, penumread, penumwrite, powerlinefrequency, nelem(powerlinefrequency)},
+ {"hue", PARAMPU, PU_HUE_CONTROL, 2, 2, pintread, pintwrite},
+ {"hue-auto", PARAMPU, PU_HUE_AUTO_CONTROL, 1, 11, pboolread, pboolwrite},
+ {"saturation", PARAMPU, PU_SATURATION_CONTROL, 2, 3, puintread, puintwrite},
+ {"sharpness", PARAMPU, PU_SHARPNESS_CONTROL, 2, 4, puintread, puintwrite},
+ {"gamma", PARAMPU, PU_GAMMA_CONTROL, 2, 5, puintread, puintwrite},
+ {"white-balance-temperature", PARAMPU, PU_WHITE_BALANCE_TEMPERATURE_CONTROL, 2, 6, puintread, puintwrite},
+ {"white-balance-temperature-auto", PARAMPU, PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, 1, 12, pboolread, pboolwrite},
+};
+
+int
+unittype(int i, uchar **ctlp)
+{
+ if(unit[i] == nil)
+ return -1;
+ switch(unit[i]->bDescriptorSubtype){
+ case VC_INPUT_TERMINAL:
+ if(GET2(((VCInputTerminal*)unit[i])->wTerminalType) == ITT_CAMERA){
+ if(ctlp != nil) *ctlp = ((VCCameraTerminal*)unit[i])->bmControls;
+ return PARAMCT;
+ }
+ break;
+ case VC_PROCESSING_UNIT:
+ if(ctlp != nil) *ctlp = ((VCProcessingUnit*)unit[i])->bmControls;
+ return PARAMPU;
+ }
+ return -1;
+}
+
+char *
+ctlread(Cam *c)
+{
+ Fmt f;
+ int i;
+ int ut;
+ Param *p;
+ uchar *bmControls;
+ int ifid;
+ char *str;
+
+ fmtstrinit(&f);
+ for(p = params; p < params + nelem(params); p++){
+ if(p->type != PARAMSPEC) continue;
+ str = p->read(c, c->iface->id, p);
+ if(str == nil)
+ continue;
+ fmtprint(&f, "0 %s %s\n", p->name, str);
+ free(str);
+ }
+ for(i = 0; i < nunit; i++){
+ ut = unittype(i, &bmControls);
+ if(ut < 0) continue;
+ ifid = unitif[i]->id;
+ for(p = params; p < params + nelem(params); p++){
+ if(p->type != ut) continue;
+ if(bmControls != nil && p->flag >= 0 && (bmControls[p->flag >> 3] & 1<<(p->flag & 7)) == 0)
+ continue;
+ str = p->read(c, i << 8 | ifid, p);
+ if(str == nil)
+ continue;
+ fmtprint(&f, "%d %s %s\n", i, p->name, str);
+ free(str);
+ }
+ }
+ return fmtstrflush(&f);
+}
+
+static Param *
+findparam(char *s)
+{
+ Param *p;
+
+ for(p = params; p < params + nelem(params); p++)
+ if(strcmp(s, p->name) == 0)
+ return p;
+ werrstr("no such parameter");
+ return nil;
+}
+
+static int
+unitbytype(int type)
+{
+ int i;
+
+ for(i = 0; i < nunit; i++)
+ if(unittype(i, nil) == type)
+ return i;
+ werrstr("no matching unit");
+ return -1;
+}
+
+int
+ctlwrite(Cam *c, char *msg)
+{
+ char *f[10], *sp;
+ uchar *bmControls;
+ Param *p;
+ int aut;
+ int nf;
+ int uid, ifid;
+
+ nf = tokenize(msg, f, nelem(f));
+ if(nf == nelem(f))
+ return -1;
+ uid = strtoul(f[0], &sp, 0);
+ aut = *f[0] == 0 || *sp != 0;
+ if(aut){
+ p = findparam(f[0]);
+ if(p == nil)
+ return -1;
+ if(p->type == PARAMSPEC)
+ uid = 0;
+ else
+ uid = unitbytype(p->type);
+ if(uid < 0)
+ return -1;
+ }else{
+ p = findparam(f[1]);
+ if(p == nil)
+ return -1;
+ if((uint)uid >= nunit || unit[uid] == nil){
+ werrstr("no such unit");
+ return -1;
+ }
+ }
+ if(p->type != PARAMSPEC){
+ if(unittype(uid, &bmControls) != p->type){
+ werrstr("unit does not have this parameter");
+ return -1;
+ }
+ if(bmControls != nil && p->flag >= 0 && (bmControls[p->flag >> 3] & 1<<(p->flag & 7)) == 0){
+ werrstr("parameter not available");
+ return -1;
+ }
+ ifid = unitif[uid]->id;
+ }else
+ ifid = c->iface->id;
+ if(p->write == nil){
+ werrstr("read-only parameter");
+ return -1;
+ }
+ return p->write(c, uid << 8 | ifid, p, f + (2 - aut), nf - (2 - aut));
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/dat.h
@@ -1,0 +1,38 @@
+typedef struct VFrame VFrame;
+typedef struct Cam Cam;
+typedef struct Format Format;
+
+struct Format {
+ VSUncompressedFormat *desc;
+ int nframe;
+ VSUncompressedFrame **frame;
+};
+
+struct VFrame {
+ int n, sz, p;
+ uchar *d;
+ VFrame *next;
+};
+
+struct Cam {
+ Dev *dev, *ep;
+ Iface *iface;
+ VSInputHeader *hdr;
+ int nformat;
+ Format **format;
+ ProbeControl pc;
+ Cam *next;
+ File *ctlfile, *formatsfile, *videofile, *descfile, *framefile;
+
+ int active, abort;
+ VFrame *actl;
+ VFrame *freel;
+ Req *delreq;
+ QLock qulock;
+ int cvtid;
+ int framemode;
+};
+
+extern int nunit;
+extern VCUnit **unit;
+extern Iface **unitif;
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/descprint.c
@@ -1,0 +1,360 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "usb.h"
+#include "uvc.h"
+
+void
+printVCHeader(Fmt *fmt, void *vp)
+{
+ VCHeader *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "VCHeader:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbNumFormats = %d\n", p->bNumFormats);
+ fmtprint(fmt, "\twTotalLength = %d\n", GET2(p->wTotalLength));
+ fmtprint(fmt, "\tdwClockFrequency = %d\n", GET2(p->dwClockFrequency));
+ fmtprint(fmt, "\tbInCollection = %d\n", p->bInCollection);
+ for(i = 0; i < p->bInCollection; i++)
+ fmtprint(fmt, "\tbaInterfaceNr(%d) = %d\n", i+1, p->baInterfaceNr[i]);
+}
+
+void
+printVCInputTerminal(Fmt *fmt, void *vp)
+{
+ VCInputTerminal *p;
+
+ p = vp;
+ fmtprint(fmt, "VCInputTerminal:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbTerminalID = %d\n", p->bTerminalID);
+ fmtprint(fmt, "\twTerminalType = %#.4x\n", GET2(p->wTerminalType));
+ fmtprint(fmt, "\tbAssocTerminal = %d\n", p->bAssocTerminal);
+ fmtprint(fmt, "\tiTerminal = %d\n", p->iTerminal);
+}
+
+void
+printVCOutputTerminal(Fmt *fmt, void *vp)
+{
+ VCOutputTerminal *p;
+
+ p = vp;
+ fmtprint(fmt, "VCOutputTerminal:\n");
+ fmtprint(fmt, "\tbLength = %#.2x\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbTerminalID = %#.2x\n", p->bTerminalID);
+ fmtprint(fmt, "\twTerminalType = %#.4x\n", GET2(p->wTerminalType));
+ fmtprint(fmt, "\tbAssocTerminal = %#.2x\n", p->bAssocTerminal);
+ fmtprint(fmt, "\tbSourceID = %#.2x\n", p->bSourceID);
+ fmtprint(fmt, "\tiTerminal = %#.2x\n", p->iTerminal);
+}
+
+void
+printVCCameraTerminal(Fmt *fmt, void *vp)
+{
+ VCCameraTerminal *p;
+
+ p = vp;
+ fmtprint(fmt, "VCCameraTerminal:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbTerminalID = %d\n", p->bTerminalID);
+ fmtprint(fmt, "\twTerminalType = %#.4x\n", GET2(p->wTerminalType));
+ fmtprint(fmt, "\tbAssocTerminal = %d\n", p->bAssocTerminal);
+ fmtprint(fmt, "\tiTerminal = %d\n", p->iTerminal);
+ fmtprint(fmt, "\twObjectiveFocalLengthMin = %#.4x\n", GET2(p->wObjectiveFocalLengthMin));
+ fmtprint(fmt, "\twObjectiveFocalLengthMax = %#.4x\n", GET2(p->wObjectiveFocalLengthMax));
+ fmtprint(fmt, "\twOcularFocalLength = %#.4x\n", GET2(p->wOcularFocalLength));
+ fmtprint(fmt, "\tbControlSize = %d\n", p->bControlSize);
+ fmtprint(fmt, "\tbmControls = %#.6x\n", GET3(p->bmControls));
+}
+
+void
+printVCSelectorUnit(Fmt *fmt, void *vp)
+{
+ VCSelectorUnit *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "VCSelectorUnit:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbUnitID = %d\n", p->bUnitID);
+ fmtprint(fmt, "\tbNrInPins = %d\n", p->bNrInPins);
+ for(i = 0; i < p->bNrInPins; i++)
+ fmtprint(fmt, "\tbaSourceID(%d) = %d\n", i+1, p->baSourceID[i]);
+ fmtprint(fmt, "\tiSelector = %d\n", p->baSourceID[i]);
+}
+
+void
+printVCProcessingUnit(Fmt *fmt, void *vp)
+{
+ VCProcessingUnit *p;
+
+ p = vp;
+ fmtprint(fmt, "VCProcessingUnit:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbUnitID = %d\n", p->bUnitID);
+ fmtprint(fmt, "\tbSourceID = %d\n", p->bSourceID);
+ fmtprint(fmt, "\twMaxMultiplier = %d\n", GET2(p->wMaxMultiplier));
+ fmtprint(fmt, "\tbControlSize = %d\n", p->bControlSize);
+ fmtprint(fmt, "\tbmControls = %#.6x\n", GET3(p->bmControls));
+ fmtprint(fmt, "\tiProcessing = %d\n", p->iProcessing);
+ fmtprint(fmt, "\tbmVideoStandards = %#.2x\n", p->bmVideoStandards);
+}
+
+void
+printVCEncodingUnit(Fmt *fmt, void *vp)
+{
+ VCEncodingUnit *p;
+
+ p = vp;
+ fmtprint(fmt, "VCEncodingUnit:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbUnitID = %d\n", p->bUnitID);
+ fmtprint(fmt, "\tbSourceID = %d\n", p->bSourceID);
+ fmtprint(fmt, "\tiEncoding = %d\n", p->iEncoding);
+ fmtprint(fmt, "\tbControlSize = %d\n", p->bControlSize);
+ fmtprint(fmt, "\tbmControls = %#.6x\n", GET3(p->bmControls));
+ fmtprint(fmt, "\tbmControlsRuntime = %#.6x\n", GET3(p->bmControlsRuntime));
+}
+
+void
+printVCExtensionUnit(Fmt *fmt, void *vp)
+{
+ VCExtensionUnit *p;
+ int i, i0, e;
+
+ p = vp;
+ fmtprint(fmt, "VCExtensionUnit:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbUnitID = %d\n", p->bUnitID);
+ fmtprint(fmt, "\tbNumControls = %d\n", p->bNumControls);
+ fmtprint(fmt, "\tbNrInPins = %d\n", p->bNrInPins);
+ for(i = 0; i < p->bNrInPins; i++)
+ fmtprint(fmt, "\tbaSourceID(%d) = %d\n", i+1, p->baSourceID[i]);
+ fmtprint(fmt, "\tbControlSize = %d\n", p->baSourceID[i]);
+ i0 = i;
+ e = i + p->baSourceID[i] + 1;
+ for(; i < e; i++)
+ fmtprint(fmt, "\tbmControls(%d) = %d\n", i - i0 + 1, p->baSourceID[i]);
+ fmtprint(fmt, "\tiExtension = %d\n", p->baSourceID[i]);
+}
+
+void
+printVSInputHeader(Fmt *fmt, void *vp)
+{
+ VSInputHeader *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "VSInputHeader:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbNumFormats = %d\n", p->bNumFormats);
+ fmtprint(fmt, "\twTotalLength = %d\n", GET2(p->wTotalLength));
+ fmtprint(fmt, "\tbEndpointAddress = %#x\n", p->bEndpointAddress);
+ fmtprint(fmt, "\tbmInfo = %#.2x\n", p->bmInfo);
+ fmtprint(fmt, "\tbTerminalLink = %d\n", p->bTerminalLink);
+ fmtprint(fmt, "\tbStillCaptureMethod = %d\n", p->bStillCaptureMethod);
+ fmtprint(fmt, "\tbTriggerSupport = %#.2x\n", p->bTriggerSupport);
+ fmtprint(fmt, "\tbTriggerUsage = %#.2x\n", p->bTriggerUsage);
+ fmtprint(fmt, "\tbControlSize = %d\n", p->bControlSize);
+ for(i = 0; i < p->bControlSize; i++)
+ fmtprint(fmt, "\tbmaControls(%d) = %d\n", i+1, p->bmaControls[i]);
+}
+
+void
+printVSOutputHeader(Fmt *fmt, void *vp)
+{
+ VSOutputHeader *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "VSOutputHeader:\n");
+ fmtprint(fmt, "\tbLength = %#.2x\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbNumFormats = %#.2x\n", p->bNumFormats);
+ fmtprint(fmt, "\twTotalLength = %#.4x\n", GET2(p->wTotalLength));
+ fmtprint(fmt, "\tbEndpointAddress = %#.2x\n", p->bEndpointAddress);
+ fmtprint(fmt, "\tbTerminalLink = %#.2x\n", p->bTerminalLink);
+ fmtprint(fmt, "\tbControlSize = %#.2x\n", p->bControlSize);
+ for(i = 0; i < p->bControlSize; i++)
+ fmtprint(fmt, "\tbmaControls(%d) = %d\n", i+1, p->bmaControls[i]);
+}
+
+void
+printVSStillFrame(Fmt *fmt, void *vp)
+{
+ VSStillFrame *p;
+ int i, j;
+
+ p = vp;
+ fmtprint(fmt, "VSStillFrame:\n");
+ fmtprint(fmt, "\tbLength = %#.2x\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbEndpointAddress = %#.2x\n", p->bEndpointAddress);
+ fmtprint(fmt, "\tbNumImageSizePatterns = %#.2x\n", p->bNumImageSizePatterns);
+ for(i = 0; i < p->bNumImageSizePatterns; i++){
+ fmtprint(fmt, "\twWidth(%d) = %d\n", i+1, GET2(&p->data[4 * i]));
+ fmtprint(fmt, "\twHeight(%d) = %d\n", i+1, GET2(&p->data[4 * i + 2]));
+ }
+ fmtprint(fmt, "\tbNumCompressionPatterns = %#.2x\n", p->data[4 * i]);
+ for(j = 0; j < p->data[4 * i]; i++)
+ fmtprint(fmt, "\tbCompression(%d) = %#.2x", j + 1,GET2(&p->data[4 * i + 1 + j]));
+}
+
+void
+printVSUncompressedFormat(Fmt *fmt, void *vp)
+{
+ VSUncompressedFormat *p;
+
+ p = vp;
+ fmtprint(fmt, "VSUncompressedFormat:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbFormatIndex = %d\n", p->bFormatIndex);
+ fmtprint(fmt, "\tbNumFrameDescriptors = %d\n", p->bNumFrameDescriptors);
+ fmtprint(fmt, "\tbBitsPerPixel = %d\n", p->bBitsPerPixel);
+ fmtprint(fmt, "\tbDefaultFrameIndex = %d\n", p->bDefaultFrameIndex);
+ fmtprint(fmt, "\tbAspectRatioX = %d\n", p->bAspectRatioX);
+ fmtprint(fmt, "\tbAspectRatioY = %d\n", p->bAspectRatioY);
+ fmtprint(fmt, "\tbmInterlaceFlags = %#.2x\n", p->bmInterlaceFlags);
+ fmtprint(fmt, "\tbCopyProtect = %#.2x\n", p->bCopyProtect);
+}
+
+void
+printVSUncompressedFrame(Fmt *fmt, void *vp)
+{
+ VSUncompressedFrame *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "VSUncompressedFrame:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbFrameIndex = %d\n", p->bFrameIndex);
+ fmtprint(fmt, "\tbmCapabilities = %#.2x\n", p->bmCapabilities);
+ fmtprint(fmt, "\twWidth = %d\n", GET2(p->wWidth));
+ fmtprint(fmt, "\twHeight = %d\n", GET2(p->wHeight));
+ fmtprint(fmt, "\tdwMinBitRate = %d\n", GET4(p->dwMinBitRate));
+ fmtprint(fmt, "\tdwMaxBitRate = %d\n", GET4(p->dwMaxBitRate));
+ fmtprint(fmt, "\tdwMaxVideoFrameBufferSize = %d\n", GET4(p->dwMaxVideoFrameBufferSize));
+ fmtprint(fmt, "\tdwDefaultFrameInterval = %d\n", GET4(p->dwDefaultFrameInterval));
+ fmtprint(fmt, "\tbFrameIntervalType = %d\n", p->bFrameIntervalType);
+ if(p->bFrameIntervalType == 0){
+ fmtprint(fmt, "\tdwMinFrameInterval = %d\n", GET4(p->dwFrameInterval[0]));
+ fmtprint(fmt, "\tdwMaxFrameInterval = %d\n", GET4(p->dwFrameInterval[1]));
+ fmtprint(fmt, "\tdwFrameIntervalStep = %d\n", GET4(p->dwFrameInterval[2]));
+ }
+ for(i = 0; i < p->bFrameIntervalType; i++)
+ fmtprint(fmt, "\tdwFrameInterval = %d\n", GET4(p->dwFrameInterval[i]));
+}
+
+void
+printVSColorFormat(Fmt *fmt, void *vp)
+{
+ VSColorFormat *p;
+
+ p = vp;
+ fmtprint(fmt, "VSColorFormat:\n");
+ fmtprint(fmt, "\tbLength = %d\n", p->bLength);
+ fmtprint(fmt, "\tbDescriptorType = %#.2x\n", p->bDescriptorType);
+ fmtprint(fmt, "\tbDescriptorSubtype = %#.2x\n", p->bDescriptorSubtype);
+ fmtprint(fmt, "\tbColorPrimaries = %d\n", p->bColorPrimaries);
+ fmtprint(fmt, "\tbTransferCharacteristics = %d\n", p->bTransferCharacteristics);
+ fmtprint(fmt, "\tbMatrixCoefficients = %d\n", p->bMatrixCoefficients);
+}
+
+void
+printProbeControl(Fmt *fmt, void *vp)
+{
+ ProbeControl *p;
+ int i;
+
+ p = vp;
+ fmtprint(fmt, "ProbeControl:\n");
+ fmtprint(fmt, "\tbmHint = %#.4ux\n", GET2(p->bmHint));
+ fmtprint(fmt, "\tbFormatIndex = %#.2ux\n", p->bFormatIndex);
+ fmtprint(fmt, "\tbFrameIndex = %#.2ux\n", p->bFrameIndex);
+ fmtprint(fmt, "\tdwFrameInterval = %#.8ux\n", GET4(p->dwFrameInterval));
+ fmtprint(fmt, "\twKeyFrameRate = %#.4ux\n", GET2(p->wKeyFrameRate));
+ fmtprint(fmt, "\twPFrameRate = %#.4ux\n", GET2(p->wPFrameRate));
+ fmtprint(fmt, "\twCompQuality = %#.4ux\n", GET2(p->wCompQuality));
+ fmtprint(fmt, "\twCompWindowSize = %#.4ux\n", GET2(p->wCompWindowSize));
+ fmtprint(fmt, "\twDelay = %#.4ux\n", GET2(p->wDelay));
+ fmtprint(fmt, "\tdwMaxVideoFrameSize = %#.8ux\n", GET4(p->dwMaxVideoFrameSize));
+ fmtprint(fmt, "\tdwMaxPayloadTransferSize = %#.8ux\n", GET4(p->dwMaxPayloadTransferSize));
+ fmtprint(fmt, "\tdwClockFrequency = %#.8ux\n", GET4(p->dwClockFrequency));
+ fmtprint(fmt, "\tbmFramingInfo = %#.2ux\n", p->bmFramingInfo);
+ fmtprint(fmt, "\tbPreferedVersion = %#.2ux\n", p->bPreferedVersion);
+ fmtprint(fmt, "\tbMinVersion = %#.2ux\n", p->bMinVersion);
+ fmtprint(fmt, "\tbMaxVersion = %#.2ux\n", p->bMaxVersion);
+ fmtprint(fmt, "\tbBitDepthLuma = %#.2ux\n", p->bBitDepthLuma);
+ fmtprint(fmt, "\tbmSettings = %#.2ux\n", p->bmSettings);
+ fmtprint(fmt, "\tbMaxNumberOfRefFramesPlus1 = %#.2ux\n", p->bMaxNumberOfRefFramesPlus1);
+ fmtprint(fmt, "\tbmRateControlModes = %#.4ux\n", GET2(p->bmRateControlModes));
+ for(i = 0; i < 4; i++)
+ fmtprint(fmt, "\tbmLayoutPerStream(%d) = %#.4ux\n", i + 1, GET2(&p->bmLayoutPerStream[2 * i]));
+}
+
+void
+printDescriptor(Fmt *fmt, Iface *iface, void *vp)
+{
+ VCDescriptor *vdp;
+
+ vdp = vp;
+ switch(Subclass(iface->csp)){
+ case SC_VIDEOCONTROL:
+ switch(vdp->bDescriptorSubtype){
+ case VC_HEADER: printVCHeader(fmt, vdp); break;
+ case VC_INPUT_TERMINAL:
+ if(GET2(((VCInputTerminal*)vdp)->wTerminalType) == ITT_CAMERA)
+ printVCCameraTerminal(fmt, vdp);
+ else
+ printVCInputTerminal(fmt, vdp);
+ break;
+ case VC_OUTPUT_TERMINAL: printVCOutputTerminal(fmt, vdp); break;
+ case VC_SELECTOR_UNIT: printVCSelectorUnit(fmt, vdp); break;
+ case VC_PROCESSING_UNIT: printVCProcessingUnit(fmt, vdp); break;
+ case VC_ENCODING_UNIT: printVCEncodingUnit(fmt, vdp); break;
+ case VC_EXTENSION_UNIT: printVCExtensionUnit(fmt, vdp); break;
+ default: fmtprint(fmt, "unknown video control descriptor type %#.2x\n", vdp->bDescriptorSubtype);
+ }
+ break;
+ case SC_VIDEOSTREAMING:
+ switch(vdp->bDescriptorSubtype){
+ case VS_INPUT_HEADER: printVSInputHeader(fmt, vdp); break;
+ case VS_OUTPUT_HEADER: printVSOutputHeader(fmt, vdp); break;
+ case VS_STILL_IMAGE_FRAME: printVSStillFrame(fmt, vdp); break;
+ case VS_FORMAT_UNCOMPRESSED: printVSUncompressedFormat(fmt, vdp); break;
+ case VS_FRAME_UNCOMPRESSED: printVSUncompressedFrame(fmt, vdp); break;
+ case VS_COLORFORMAT: printVSColorFormat(fmt, vdp); break;
+ default: fmtprint(fmt, "unknown video streaming descriptor type %#.2x\n", vdp->bDescriptorSubtype);
+ }
+ break;
+ }
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/fns.h
@@ -1,0 +1,8 @@
+char *ctlread(Cam *);
+void printDescriptor(Fmt *, Iface *, void *);
+int videoopen(Cam *, int);
+void videoclose(Cam *);
+void videoread(Req *, Cam *, int);
+void videoflush(Req *, Cam *);
+int getframedesc(Cam *, int, int, Format **, VSUncompressedFrame **);
+int ctlwrite(Cam *, char *);
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/mkfile
@@ -1,0 +1,25 @@
+</$objtype/mkfile
+
+TARG=cam
+OFILES=\
+ cam.$O \
+ descprint.$O \
+ ctl.$O \
+ video.$O \
+
+HFILES=\
+ ../lib/usb.h \
+ dat.h \
+ fns.h \
+
+LIB=../lib/usb.a$O
+
+BIN=/$objtype/bin/nusb
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mkone
+CFLAGS=-I../lib $CFLAGS
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/uvc.h
@@ -1,0 +1,354 @@
+typedef struct VCDescriptor {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+} VCDescriptor;
+
+typedef struct VCHeader {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bNumFormats;
+ uchar wTotalLength[2];
+ uchar dwClockFrequency[4];
+ uchar bInCollection;
+ uchar baInterfaceNr[1];
+} VCHeader;
+
+typedef struct VCInputTerminal {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bTerminalID;
+ uchar wTerminalType[2];
+ uchar bAssocTerminal;
+ uchar iTerminal;
+} VCInputTerminal;
+
+typedef struct VCOutputTerminal {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bTerminalID;
+ uchar wTerminalType[2];
+ uchar bAssocTerminal;
+ uchar bSourceID;
+ uchar iTerminal;
+} VCOutputTerminal;
+
+typedef struct VCCameraTerminal {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bTerminalID;
+ uchar wTerminalType[2];
+ uchar bAssocTerminal;
+ uchar iTerminal;
+ uchar wObjectiveFocalLengthMin[2];
+ uchar wObjectiveFocalLengthMax[2];
+ uchar wOcularFocalLength[2];
+ uchar bControlSize;
+ uchar bmControls[3];
+} VCCameraTerminal;
+
+typedef struct VCUnit {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bUnitID;
+} VCUnit;
+
+typedef struct VCSelectorUnit {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bUnitID;
+ uchar bNrInPins;
+ uchar baSourceID[1];
+ /* after baSourceID: uchar iSelector; */
+} VCSelectorUnit;
+
+typedef struct VCProcessingUnit {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bUnitID;
+ uchar bSourceID;
+ uchar wMaxMultiplier[2];
+ uchar bControlSize;
+ uchar bmControls[3];
+ uchar iProcessing;
+ uchar bmVideoStandards;
+} VCProcessingUnit;
+
+typedef struct VCEncodingUnit {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bUnitID;
+ uchar bSourceID;
+ uchar iEncoding;
+ uchar bControlSize;
+ uchar bmControls[3];
+ uchar bmControlsRuntime[3];
+} VCEncodingUnit;
+
+typedef struct VCExtensionUnit {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bUnitID;
+ uchar guidExtensionCode[16];
+ uchar bNumControls;
+ uchar bNrInPins;
+ uchar baSourceID[1];
+ /*
+ uchar bControlSize;
+ uchar bmControls[1];
+ uchar iExtension;
+ */
+} VCExtensionUnit;
+
+typedef struct VSInputHeader {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bNumFormats;
+ uchar wTotalLength[2];
+ uchar bEndpointAddress;
+ uchar bmInfo;
+ uchar bTerminalLink;
+ uchar bStillCaptureMethod;
+ uchar bTriggerSupport;
+ uchar bTriggerUsage;
+ uchar bControlSize;
+ uchar bmaControls[1];
+} VSInputHeader;
+
+typedef struct VSOutputHeader {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bNumFormats;
+ uchar wTotalLength[2];
+ uchar bEndpointAddress;
+ uchar bTerminalLink;
+ uchar bControlSize;
+ uchar bmaControls[1];
+} VSOutputHeader;
+
+typedef struct VSStillFrame {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bEndpointAddress;
+ uchar bNumImageSizePatterns;
+ uchar data[1];
+} VSStillFrame;
+
+typedef struct VSUncompressedFormat {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bFormatIndex;
+ uchar bNumFrameDescriptors;
+ uchar guidFormat[16];
+ uchar bBitsPerPixel;
+ uchar bDefaultFrameIndex;
+ uchar bAspectRatioX;
+ uchar bAspectRatioY;
+ uchar bmInterlaceFlags;
+ uchar bCopyProtect;
+} VSUncompressedFormat;
+
+typedef struct VSUncompressedFrame {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bFrameIndex;
+ uchar bmCapabilities;
+ uchar wWidth[2];
+ uchar wHeight[2];
+ uchar dwMinBitRate[4];
+ uchar dwMaxBitRate[4];
+ uchar dwMaxVideoFrameBufferSize[4];
+ uchar dwDefaultFrameInterval[4];
+ uchar bFrameIntervalType;
+ uchar dwFrameInterval[1][4];
+} VSUncompressedFrame;
+
+typedef struct VSColorFormat {
+ uchar bLength;
+ uchar bDescriptorType;
+ uchar bDescriptorSubtype;
+ uchar bColorPrimaries;
+ uchar bTransferCharacteristics;
+ uchar bMatrixCoefficients;
+} VSColorFormat;
+
+typedef struct ProbeControl {
+ uchar bmHint[2];
+ uchar bFormatIndex;
+ uchar bFrameIndex;
+ uchar dwFrameInterval[4];
+ uchar wKeyFrameRate[2];
+ uchar wPFrameRate[2];
+ uchar wCompQuality[2];
+ uchar wCompWindowSize[2];
+ uchar wDelay[2];
+ uchar dwMaxVideoFrameSize[4];
+ uchar dwMaxPayloadTransferSize[4];
+ uchar dwClockFrequency[4];
+ uchar bmFramingInfo;
+ uchar bPreferedVersion;
+ uchar bMinVersion;
+ uchar bMaxVersion;
+ uchar bBitDepthLuma;
+ uchar bmSettings;
+ uchar bMaxNumberOfRefFramesPlus1;
+ uchar bmRateControlModes[2];
+ uchar bmLayoutPerStream[8];
+} ProbeControl;
+
+enum {
+ CC_VIDEO = 0x0E,
+ SC_UNDEFINED = 0x00,
+ SC_VIDEOCONTROL = 0x01,
+ SC_VIDEOSTREAMING = 0x02,
+ SC_VIDEO_INTERFACE_COLLECTION = 0x03,
+ PC_PROTOCOL_UNDEFINED = 0x00,
+ PC_PROTOCOL_15 = 0x01,
+ CS_UNDEFINED = 0x20,
+ CS_DEVICE = 0x21,
+ CS_CONFIGURATION = 0x22,
+ CS_STRING = 0x23,
+ CS_INTERFACE = 0x24,
+ CS_ENDPOINT = 0x25,
+ VC_DESCRIPTOR_UNDEFINED = 0x00,
+ VC_HEADER = 0x01,
+ VC_INPUT_TERMINAL = 0x02,
+ VC_OUTPUT_TERMINAL = 0x03,
+ VC_SELECTOR_UNIT = 0x04,
+ VC_PROCESSING_UNIT = 0x05,
+ VC_EXTENSION_UNIT = 0x06,
+ VC_ENCODING_UNIT = 0x07,
+ VS_UNDEFINED = 0x00,
+ VS_INPUT_HEADER = 0x01,
+ VS_OUTPUT_HEADER = 0x02,
+ VS_STILL_IMAGE_FRAME = 0x03,
+ VS_FORMAT_UNCOMPRESSED = 0x04,
+ VS_FRAME_UNCOMPRESSED = 0x05,
+ VS_FORMAT_MJPEG = 0x06,
+ VS_FRAME_MJPEG = 0x07,
+ VS_FORMAT_MPEG2TS = 0x0A,
+ VS_FORMAT_DV = 0x0C,
+ VS_COLORFORMAT = 0x0D,
+ VS_FORMAT_FRAME_BASED = 0x10,
+ VS_FRAME_FRAME_BASED = 0x11,
+ VS_FORMAT_STREAM_BASED = 0x12,
+ VS_FORMAT_H264 = 0x13,
+ VS_FRAME_H264 = 0x14,
+ VS_FORMAT_H264_SIMULCAST = 0x15,
+ VS_FORMAT_VP8 = 0x16,
+ VS_FRAME_VP8 = 0x17,
+ VS_FORMAT_VP8_SIMULCAST = 0x18,
+ RC_UNDEFINED = 0x00,
+ SET_CUR = 0x01,
+ SET_CUR_ALL = 0x11,
+ GET_CUR = 0x81,
+ GET_MIN = 0x82,
+ GET_MAX = 0x83,
+ GET_RES = 0x84,
+ GET_LEN = 0x85,
+ GET_INFO = 0x86,
+ GET_DEF = 0x87,
+ GET_CUR_ALL = 0x91,
+ GET_MIN_ALL = 0x92,
+ GET_MAX_ALL = 0x93,
+ GET_RES_ALL = 0x94,
+ GET_DEF_ALL = 0x97,
+ VC_CONTROL_UNDEFINED = 0x00,
+ VC_VIDEO_POWER_MODE_CONTROL = 0x01,
+ VC_REQUEST_ERROR_CODE_CONTROL = 0x02,
+ TE_CONTROL_UNDEFINED = 0x00,
+ SU_CONTROL_UNDEFINED = 0x00,
+ SU_INPUT_SELECT_CONTROL = 0x01,
+ CT_CONTROL_UNDEFINED = 0x00,
+ CT_SCANNING_MODE_CONTROL = 0x01,
+ CT_AE_MODE_CONTROL = 0x02,
+ CT_AE_PRIORITY_CONTROL = 0x03,
+ CT_EXPOSURE_TIME_ABSOLUTE_CONTROL = 0x04,
+ CT_EXPOSURE_TIME_RELATIVE_CONTROL = 0x05,
+ CT_FOCUS_ABSOLUTE_CONTROL = 0x06,
+ CT_FOCUS_RELATIVE_CONTROL = 0x07,
+ CT_FOCUS_AUTO_CONTROL = 0x08,
+ CT_IRIS_ABSOLUTE_CONTROL = 0x09,
+ CT_IRIS_RELATIVE_CONTROL = 0x0a,
+ CT_ZOOM_ABSOLUTE_CONTROL = 0x0b,
+ CT_ZOOM_RELATIVE_CONTROL = 0x0c,
+ CT_PANTILT_ABSOLUTE_CONTROL = 0x0d,
+ CT_PANTILT_RELATIVE_CONTROL = 0x0e,
+ CT_ROLL_ABSOLUTE_CONTROL = 0x0f,
+ CT_ROLL_RELATIVE_CONTROL = 0x10,
+ CT_PRIVACY_CONTROL = 0x11,
+ CT_FOCUS_SIMPLE_CONTROL = 0x12,
+ CT_WINDOW_CONTROL = 0x13,
+ CT_REGION_OF_INTEREST_CONTROL = 0x14,
+ PU_CONTROL_UNDEFINED = 0x00,
+ PU_BACKLIGHT_COMPENSATION_CONTROL = 0x01,
+ PU_BRIGHTNESS_CONTROL = 0x02,
+ PU_CONTRAST_CONTROL = 0x03,
+ PU_GAIN_CONTROL = 0x04,
+ PU_POWER_LINE_FREQUENCY_CONTROL = 0x05,
+ PU_HUE_CONTROL = 0x06,
+ PU_SATURATION_CONTROL = 0x07,
+ PU_SHARPNESS_CONTROL = 0x08,
+ PU_GAMMA_CONTROL = 0x09,
+ PU_WHITE_BALANCE_TEMPERATURE_CONTROL = 0x0a,
+ PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL = 0x0b,
+ PU_WHITE_BALANCE_COMPONENT_CONTROL = 0x0c,
+ PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL = 0x0d,
+ PU_DIGITAL_MULTIPLIER_CONTROL = 0x0e,
+ PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL = 0x0f,
+ PU_HUE_AUTO_CONTROL = 0x10,
+ PU_ANALOG_VIDEO_STANDARD_CONTROL = 0x11,
+ PU_ANALOG_LOCK_STATUS_CONTROL = 0x12,
+ PU_CONTRAST_AUTO_CONTROL = 0x13,
+ EU_CONTROL_UNDEFINED = 0x00,
+ EU_SELECT_LAYER_CONTROL = 0x01,
+ EU_PROFILE_TOOLSET_CONTROL = 0x02,
+ EU_VIDEO_RESOLUTION_CONTROL = 0x03,
+ EU_MIN_FRAME_INTERVAL_CONTROL = 0x04,
+ EU_SLICE_MODE_CONTROL = 0x05,
+ EU_RATE_CONTROL_MODE_CONTROL = 0x06,
+ EU_AVERAGE_BITRATE_CONTROL = 0x07,
+ EU_CPB_SIZE_CONTROL = 0x08,
+ EU_PEAK_BIT_RATE_CONTROL = 0x09,
+ EU_QUANTIZATION_PARAMS_CONTROL = 0x0a,
+ EU_SYNC_REF_FRAME_CONTROL = 0x0b,
+ EU_LTR_BUFFER_ = 0x0c,
+ EU_LTR_PICTURE_CONTROL = 0x0d,
+ EU_LTR_VALIDATION_CONTROL = 0x0e,
+ EU_LEVEL_IDC_LIMIT_CONTROL = 0x0f,
+ EU_SEI_PAYLOADTYPE_CONTROL = 0x10,
+ EU_QP_RANGE_CONTROL = 0x11,
+ EU_PRIORITY_CONTROL = 0x12,
+ EU_START_OR_STOP_LAYER_CONTROL = 0x13,
+ EU_ERROR_RESILIENCY_CONTROL = 0x14,
+ XU_CONTROL_UNDEFINED = 0x00,
+ VS_CONTROL_UNDEFINED = 0x00,
+ VS_PROBE_CONTROL = 0x01,
+ VS_COMMIT_CONTROL = 0x02,
+ VS_STILL_PROBE_CONTROL = 0x03,
+ VS_STILL_COMMIT_CONTROL = 0x04,
+ VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
+ VS_STREAM_ERROR_CODE_CONTROL = 0x06,
+ VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
+ VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
+ VS_SYNCH_DELAY_CONTROL = 0x09,
+ ITT_VENDOR_SPECIFIC = 0x0200,
+ ITT_CAMERA = 0x0201,
+ ITT_MEDIA_TRANSPORT_INPUT,
+};
+
+#define GET3(p) (((p)[2] & 0xFF)<<16 | ((p)[1] & 0xFF)<<8 | ((p)[0] & 0xFF))
--- /dev/null
+++ b/sys/src/cmd/nusb/cam/video.c
@@ -1,0 +1,377 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <bio.h>
+#include "usb.h"
+#include "uvc.h"
+#include "dat.h"
+#include "fns.h"
+
+enum {
+ NFrames = 20, /* frames to buffer */
+};
+
+VFrame *
+grabframe(Cam *c)
+{
+ VFrame *v;
+ VFrame **l;
+
+ qlock(&c->qulock);
+ if(c->freel != nil)
+ l = &c->freel;
+ else{
+ assert(c->actl != nil);
+ if(c->actl->p == 0)
+ l = &c->actl;
+ else
+ l = &c->actl->next;
+ }
+ assert(*l != nil);
+ v = *l;
+ *l = v->next;
+ qunlock(&c->qulock);
+ v->next = nil;
+ v->n = 60;
+ v->p = 0;
+ return v;
+}
+
+void
+pushframe(Cam *c, VFrame *v)
+{
+ VFrame **l;
+ Req *r;
+
+ v->next = nil;
+ qlock(&c->qulock);
+ for(l = &c->actl; *l != nil; l = &(*l)->next)
+ ;
+ *l = v;
+ while(c->delreq != nil && c->actl != nil){
+ r = c->delreq;
+ c->delreq = (Req*)r->qu.next;
+ videoread(r, c, 0);
+ }
+ qunlock(&c->qulock);
+}
+
+void
+yuy2convert(Format *, VSUncompressedFrame *g, uchar *in, VFrame *out)
+{
+ int y, x, w, h;
+ double Y0, Y1, U, V, R, G, B;
+ uchar *ip, *op;
+
+ w = GET2(g->wWidth);
+ h = GET2(g->wHeight);
+ op = out->d + out->n;
+ ip = in;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w; x += 2){
+ Y0 = ((int)ip[0] - 16) / 219.0;
+ Y1 = ((int)ip[2] - 16) / 219.0;
+ U = ((int)ip[1] - 128) / 224.0;
+ V = ((int)ip[3] - 128) / 224.0;
+ ip += 4;
+ R = Y0 + V;
+ B = Y0 + U;
+ G = Y0 - 0.509 * V - 0.194 * U;
+ if(R < 0) R = 0; if(R > 1) R = 1;
+ if(G < 0) G = 0; if(G > 1) G = 1;
+ if(B < 0) B = 0; if(B > 1) B = 1;
+ *op++ = R * 255;
+ *op++ = G * 255;
+ *op++ = B * 255;
+ R = Y1 + V;
+ B = Y1 + U;
+ G = Y1 - 0.509 * V - 0.194 * U;
+ if(R < 0) R = 0; if(R > 1) R = 1;
+ if(G < 0) G = 0; if(G > 1) G = 1;
+ if(B < 0) B = 0; if(B > 1) B = 1;
+ *op++ = R * 255;
+ *op++ = G * 255;
+ *op++ = B * 255;
+ }
+ out->n = op - out->d;
+}
+
+struct Converter {
+ uchar guid[16];
+ void (*fn)(Format *, VSUncompressedFrame *, uchar *, VFrame *);
+} converters[] = {
+ {{0x59,0x55,0x59,0x32, 0x00,0x00,0x10,0x00, 0x80,0x00,0x00,0xAA, 0x00,0x38,0x9B,0x71}, yuy2convert},
+};
+
+struct Converter *
+getconverter(Format *f)
+{
+ struct Converter *c;
+ uchar *guid;
+
+ guid = f->desc->guidFormat;
+ for(c = converters; c < converters + nelem(converters); c++)
+ if(memcmp(guid, c->guid, 16) == 0)
+ return c;
+ werrstr("unknown format %.2X%.2X%.2X%.2X %.2X%.2X%.2X%.2X %.2X%.2X%.2X%.2X %.2X%.2X%.2X%.2X",
+ guid[0], guid[1], guid[2], guid[3],
+ guid[4], guid[5], guid[6], guid[7],
+ guid[8], guid[9], guid[10], guid[11],
+ guid[12], guid[13], guid[14], guid[15]);
+ return nil;
+}
+
+static void
+freeframes(VFrame **fp)
+{
+ VFrame *f, *g;
+
+ for(f = *fp; f != nil; f = g){
+ g = f->next;
+ free(f);
+ }
+ *fp = nil;
+}
+
+void
+cvtproc(void *v)
+{
+ int frsz;
+ Cam *c;
+ Format *f;
+ VSUncompressedFrame *g;
+ uchar *fbuf;
+ int n;
+ int rc;
+ uchar buf[3*1024];
+ struct Converter *cvt;
+ int bufn;
+ int ob;
+ VFrame *of;
+
+ c = v;
+ assert(getframedesc(c, c->pc.bFormatIndex, c->pc.bFrameIndex, &f, &g) >= 0);
+ cvt = getconverter(f);
+ assert(cvt != nil);
+ frsz = GET2(g->wWidth) * GET2(g->wHeight) * f->desc->bBitsPerPixel / 8;
+ fbuf = emallocz(frsz, 1);
+ bufn = 0;
+ ob = 0;
+ for(;;){
+ if(c->abort) break;
+ rc = read(c->ep->dfd, buf, sizeof(buf));
+ if(c->abort || rc < 0) break;
+ if(rc == 0) continue;
+ if(((ob ^ buf[1]) & 1) != 0 && bufn != 0){
+ if(!c->framemode || bufn == frsz){
+ if(bufn < frsz)
+ memset(fbuf + bufn, 0, frsz - bufn);
+ of = grabframe(c);
+ cvt->fn(f, g, fbuf, of);
+ pushframe(c, of);
+ }
+ bufn = 0;
+ }
+ ob = buf[1];
+ n = rc - buf[0];
+ if(n > frsz - bufn) n = frsz - bufn;
+ if(n > 0){
+ memcpy(fbuf + bufn, buf + buf[0], n);
+ bufn += n;
+ }
+
+ }
+ qlock(&c->qulock);
+ freeframes(&c->actl);
+ freeframes(&c->freel);
+ c->abort = 1;
+ free(fbuf);
+ closedev(c->ep);
+ usbcmd(c->dev, 0x01, Rsetiface, 0, c->iface->id, nil, 0);
+ c->ep = nil;
+ c->active = 0;
+ c->abort = 0;
+ qunlock(&c->qulock);
+}
+
+static Altc *
+selaltc(Cam *c, ProbeControl *pc)
+{
+ int k;
+ uvlong bw, bw1, minbw;
+ int mink;
+ Format *fo;
+ VSUncompressedFrame *f;
+ Iface *iface;
+ Altc *altc;
+
+ if(getframedesc(c, pc->bFormatIndex, pc->bFrameIndex, &fo, &f) < 0){
+ werrstr("selaltc: PROBE_CONTROL returned invalid bFormatIndex,bFrameIndex=%d,%d", pc->bFormatIndex, pc->bFrameIndex);
+ return nil;
+ }
+ bw = (uvlong)GET2(f->wWidth) * GET2(f->wHeight) * fo->desc->bBitsPerPixel * 10e6 / GET4(c->pc.dwFrameInterval);
+ iface = c->iface;
+ mink = -1;
+ for(k = 0; k < nelem(iface->altc); k++){
+ altc = iface->altc[k];
+ if(altc == nil) continue;
+ bw1 = altc->maxpkt * altc->ntds * 8 * 1000 * 8;
+ if(bw1 < bw) continue;
+ if(mink < 0 || bw1 < minbw){
+ minbw = bw1;
+ mink = k;
+ }
+ }
+ if(mink < 0){
+ werrstr("device does not have enough bandwidth (need %lld bit/s)", bw);
+ return nil;
+ }
+ if(usbcmd(c->dev, 0x01, Rsetiface, mink, iface->id, nil, 0) < 0){
+ werrstr("selaltc: SET_INTERFACE(%d, %d): %r", iface->id, mink);
+ return nil;
+ }
+ return iface->altc[mink];
+}
+
+static void
+mkframes(Cam *c)
+{
+ int i;
+ VSUncompressedFrame *f;
+ int frsz;
+ VFrame *v;
+
+ assert(getframedesc(c, c->pc.bFormatIndex, c->pc.bFrameIndex, nil, &f) >= 0);
+ frsz = GET2(f->wWidth) * GET2(f->wHeight) * 3;
+ for(i = 0; i < NFrames; i++){
+ v = emallocz(sizeof(VFrame) + 60 + frsz, 1);
+ sprint((char*)&v[1], "%11s %11d %11d %11d %11d ", "b8g8r8", 0, 0, GET2(f->wWidth), GET2(f->wHeight));
+ v->d = (uchar*)&v[1];
+ v->sz = frsz;
+ v->n = 60;
+ v->next = c->freel;
+ c->freel = v;
+ }
+}
+
+int
+videoopen(Cam *c, int fr)
+{
+ Dev *d;
+ Altc *altc;
+ Dev *ep;
+ Format *f;
+
+ qlock(&c->qulock);
+ if(c->active){
+ qunlock(&c->qulock);
+ werrstr("already in use");
+ return -1;
+ }
+ if(getframedesc(c, c->pc.bFormatIndex, c->pc.bFrameIndex, &f, nil) < 0){
+err:
+ qunlock(&c->qulock);
+ return -1;
+ }
+ if(getconverter(f) == nil) goto err;
+ d = c->dev;
+ if(usbcmd(d, 0x01, Rsetiface, 0, c->iface->id, nil, 0) < 0) goto err;
+ if(usbcmd(d, 0x21, SET_CUR, VS_PROBE_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < sizeof(ProbeControl)) goto err;
+ if(usbcmd(d, 0xA1, GET_CUR, VS_PROBE_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < 0) goto err;
+ if(usbcmd(d, 0x21, SET_CUR, VS_COMMIT_CONTROL << 8, c->iface->id, (uchar *) &c->pc, sizeof(ProbeControl)) < sizeof(ProbeControl)) goto err;
+ altc = selaltc(c, &c->pc);
+ if(altc == nil)
+ goto err;
+ ep = openep(d, c->hdr->bEndpointAddress & 0x7f);
+ if(ep == nil){
+ usbcmd(d, 0x01, Rsetiface, 0, c->iface->id, nil, 0);
+ goto err;
+ }
+ devctl(ep, "pollival %d", altc->interval);
+ devctl(ep, "uframes 1");
+ devctl(ep, "ntds %d", altc->ntds);
+ devctl(ep, "maxpkt %d", altc->maxpkt);
+ if(opendevdata(ep, OREAD) < 0){
+ usbcmd(d, 0x01, Rsetiface, 0, c->iface->id, nil, 0);
+ closedev(ep);
+ goto err;
+ }
+ c->ep = ep;
+ mkframes(c);
+ c->active = 1;
+ c->framemode = fr;
+ qunlock(&c->qulock);
+ c->cvtid = proccreate(cvtproc, c, 16384);
+ return 0;
+}
+
+void
+videoclose(Cam *c)
+{
+ if(c->active == 0 || c->abort)
+ return;
+ c->abort = -1;
+}
+
+void
+videoread(Req *r, Cam *c, int lock)
+{
+ VFrame *v;
+ int n;
+ Req **rp;
+
+ if(lock) qlock(&c->qulock);
+ if(c->active == 0 || c->abort){
+ if(lock) qunlock(&c->qulock);
+ respond(r, "the front fell off");
+ return;
+ }
+ if(c->framemode == 2){
+ c->framemode = 1;
+ if(lock) qunlock(&c->qulock);
+ r->ofcall.count = 0;
+ respond(r, nil);
+ return;
+ }
+ if(c->actl == nil){
+ for(rp = &c->delreq; *rp != nil; rp = (Req**)&(*rp)->qu.next)
+ ;
+ r->qu.next = nil;
+ *rp = r;
+ if(lock) qunlock(&c->qulock);
+ return;
+ }
+ v = c->actl;
+ n = v->n - v->p;
+ if(n > r->ifcall.count) n = r->ifcall.count;
+ memcpy(r->ofcall.data, v->d + v->p, n);
+ v->p += n;
+ if(v->p == v->n){
+ if(c->framemode)
+ c->framemode = 2;
+ c->actl = v->next;
+ v->next = c->freel;
+ c->freel = v;
+ }
+ if(lock) qunlock(&c->qulock);
+ r->ofcall.count = n;
+ respond(r, nil);
+}
+
+void
+videoflush(Req *r, Cam *c)
+{
+ Req **rp;
+
+ qlock(&c->qulock);
+ for(rp = &c->delreq; *rp != nil; rp = (Req**)&(*rp)->qu.next)
+ if(*rp == r){
+ *rp = (Req *) r->qu.next;
+ respond(r, "interrupted");
+ break;
+ }
+ qunlock(&c->qulock);
+}
--- a/sys/src/cmd/nusb/mkfile
+++ b/sys/src/cmd/nusb/mkfile
@@ -10,6 +10,7 @@
serial\
ptp\
joy\
+ cam\
UPDATE=\
mkfile\