#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)); }