add nusb/cam
This commit is contained in:
parent
a2d8dcfd82
commit
2da5e135dc
374
sys/src/cmd/nusb/cam/cam.c
Normal file
374
sys/src/cmd/nusb/cam/cam.c
Normal 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
532
sys/src/cmd/nusb/cam/ctl.c
Normal 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));
|
||||||
|
}
|
38
sys/src/cmd/nusb/cam/dat.h
Normal file
38
sys/src/cmd/nusb/cam/dat.h
Normal 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;
|
360
sys/src/cmd/nusb/cam/descprint.c
Normal file
360
sys/src/cmd/nusb/cam/descprint.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
8
sys/src/cmd/nusb/cam/fns.h
Normal file
8
sys/src/cmd/nusb/cam/fns.h
Normal 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 *);
|
25
sys/src/cmd/nusb/cam/mkfile
Normal file
25
sys/src/cmd/nusb/cam/mkfile
Normal 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
354
sys/src/cmd/nusb/cam/uvc.h
Normal 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))
|
377
sys/src/cmd/nusb/cam/video.c
Normal file
377
sys/src/cmd/nusb/cam/video.c
Normal 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);
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ DIRS=\
|
||||||
serial\
|
serial\
|
||||||
ptp\
|
ptp\
|
||||||
joy\
|
joy\
|
||||||
|
cam\
|
||||||
|
|
||||||
UPDATE=\
|
UPDATE=\
|
||||||
mkfile\
|
mkfile\
|
||||||
|
|
Loading…
Reference in a new issue