enum
{
/*
* Max device conf is also limited by max control request size as
* limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
* Some devices ignore the high byte of control transfer reads so keep
* the low byte all ones. asking for 16K kills Newsham's disk.
*/
Maxdevconf = 4*1024 - 1,
};
int
loaddevconf(Dev *d, int n)
{
uchar *buf;
int nr;
int type;
if(n >= nelem(d->usb->conf)){
werrstr("loaddevconf: bug: out of configurations in device");
fprint(2, "%s: %r\n", argv0);
return -1;
}
buf = emallocz(Maxdevconf, 0);
type = Rd2h|Rstd|Rdev;
nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
if(nr < Dconflen){
free(buf);
return -1;
}
if(d->usb->conf[n] == nil)
d->usb->conf[n] = emallocz(sizeof(Conf), 1);
nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
free(buf);
return nr;
}
if(n > 0 && n > b[0])
n = b[0];
if(n <= 2 || (n & 1) != 0)
return strdup("none");
n = (n - 2)/2;
b += 2;
us = s = emallocz(n*UTFmax+1, 0);
for(; --n >= 0; b += 2){
r = GET2(b);
s += runetochar(s, &r);
}
*s = 0;
return us;
}
char*
loaddevstr(Dev *d, int sid)
{
uchar buf[256-2]; /* keep size < 256 */
int langid;
int type;
int nr;
if(sid == 0)
return estrdup("none");
type = Rd2h|Rstd|Rdev;
/*
* there are devices which do not return a string if used
* with invalid language id, so at least try to use the first
* one and choose english if failed
*/
nr=usbcmd(d, type, Rgetdesc, Dstr<<8, 0, buf, sizeof(buf));
if(nr < 4)
langid = 0x0409; // english
else
langid = buf[3]<<8|buf[2];
if(type&Rd2h)
s = seprint(buf, buf+sizeof(buf), "d2h");
else
s = seprint(buf, buf+sizeof(buf), "h2d");
if(type&Rclass)
s = seprint(s, buf+sizeof(buf), "|cls");
else if(type&Rvendor)
s = seprint(s, buf+sizeof(buf), "|vnd");
else
s = seprint(s, buf+sizeof(buf), "|std");
s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
switch(req){
case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
}
USED(s);
return buf;
}
static int
cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
{
int ndata, n;
uchar *wp;
uchar buf[8];
char *hd, *rs;
int
usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
{
int i, r, nerr;
char err[64];
/*
* Some devices do not respond to commands some times.
* Others even report errors but later work just fine. Retry.
*/
r = -1;
*err = 0;
for(i = nerr = 0; i < Uctries; i++){
if(type & Rd2h)
r = cmdreq(d, type, req, value, index, nil, count);
else
r = cmdreq(d, type, req, value, index, data, count);
if(r >= 0){
if((type & Rd2h) == 0)
break;
r = cmdrep(d, data, count);
if(r > 0)
break;
if(r == 0)
werrstr("no data from device");
}
nerr++;
if(*err == 0)
rerrstr(err, sizeof(err));
sleep(Ucdelay);
}
if(r >= 0 && i >= 2)
/* let the user know the device is not in good shape */
fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
argv0, d->dir, i, err);
return r;
}