diff --git a/sys/src/cmd/nusb/cam/cam.c b/sys/src/cmd/nusb/cam/cam.c new file mode 100644 index 000000000..a1da6e213 --- /dev/null +++ b/sys/src/cmd/nusb/cam/cam.c @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#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); +} diff --git a/sys/src/cmd/nusb/cam/ctl.c b/sys/src/cmd/nusb/cam/ctl.c new file mode 100644 index 000000000..4cdafc6ae --- /dev/null +++ b/sys/src/cmd/nusb/cam/ctl.c @@ -0,0 +1,532 @@ +#include +#include +#include +#include +#include +#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)); +} diff --git a/sys/src/cmd/nusb/cam/dat.h b/sys/src/cmd/nusb/cam/dat.h new file mode 100644 index 000000000..6f6863d37 --- /dev/null +++ b/sys/src/cmd/nusb/cam/dat.h @@ -0,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; diff --git a/sys/src/cmd/nusb/cam/descprint.c b/sys/src/cmd/nusb/cam/descprint.c new file mode 100644 index 000000000..94894db0e --- /dev/null +++ b/sys/src/cmd/nusb/cam/descprint.c @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#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; + } +} diff --git a/sys/src/cmd/nusb/cam/fns.h b/sys/src/cmd/nusb/cam/fns.h new file mode 100644 index 000000000..72536df94 --- /dev/null +++ b/sys/src/cmd/nusb/cam/fns.h @@ -0,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 *); diff --git a/sys/src/cmd/nusb/cam/mkfile b/sys/src/cmd/nusb/cam/mkfile new file mode 100644 index 000000000..ed8f2416c --- /dev/null +++ b/sys/src/cmd/nusb/cam/mkfile @@ -0,0 +1,25 @@ + +#include +#include +#include +#include +#include <9p.h> +#include +#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); +} diff --git a/sys/src/cmd/nusb/mkfile b/sys/src/cmd/nusb/mkfile index 4dd113a50..017a19afc 100644 --- a/sys/src/cmd/nusb/mkfile +++ b/sys/src/cmd/nusb/mkfile @@ -10,6 +10,7 @@ DIRS=\ serial\ ptp\ joy\ + cam\ UPDATE=\ mkfile\