add nusb/cam
This commit is contained in:
parent
a2d8dcfd82
commit
2da5e135dc
9 changed files with 2069 additions and 0 deletions
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\
|
||||
ptp\
|
||||
joy\
|
||||
cam\
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
|
|
Loading…
Reference in a new issue