add nusb/cam

This commit is contained in:
aiju 2018-03-07 10:06:18 +00:00
parent a2d8dcfd82
commit 2da5e135dc
9 changed files with 2069 additions and 0 deletions

374
sys/src/cmd/nusb/cam/cam.c Normal file
View file

@ -0,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);
}

532
sys/src/cmd/nusb/cam/ctl.c Normal file
View file

@ -0,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));
}

View file

@ -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;

View file

@ -0,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;
}
}

View file

@ -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 *);

View file

@ -0,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

354
sys/src/cmd/nusb/cam/uvc.h Normal file
View file

@ -0,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))

View file

@ -0,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);
}

View file

@ -10,6 +10,7 @@ DIRS=\
serial\
ptp\
joy\
cam\
UPDATE=\
mkfile\