Make CH347 driver faster
Speed up toggleClk Defer write-only USB transactions to better utilize bus
This commit is contained in:
parent
67159e8297
commit
c82a8e6207
|
|
@ -28,7 +28,7 @@ using namespace std;
|
||||||
#define CH347JTAG_WRITE_EP 0x06
|
#define CH347JTAG_WRITE_EP 0x06
|
||||||
#define CH347JTAG_READ_EP 0x86
|
#define CH347JTAG_READ_EP 0x86
|
||||||
|
|
||||||
#define CH347JTAG_TIMEOUT 2000
|
#define CH347JTAG_TIMEOUT 200
|
||||||
|
|
||||||
enum CH347JtagCmd {
|
enum CH347JtagCmd {
|
||||||
CMD_BYTES_WO = 0xd3,
|
CMD_BYTES_WO = 0xd3,
|
||||||
|
|
@ -43,13 +43,39 @@ enum CH347JtagSig {
|
||||||
SIG_TMS = 0b10,
|
SIG_TMS = 0b10,
|
||||||
SIG_TDI = 0b10000,
|
SIG_TDI = 0b10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void LIBUSB_CALL sync_cb(struct libusb_transfer *transfer) {
|
static void LIBUSB_CALL sync_cb(struct libusb_transfer *transfer) {
|
||||||
int *complete = (int *)transfer->user_data;
|
int *complete = (int *)transfer->user_data;
|
||||||
*complete = true;
|
*complete = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) {
|
// defer should only be used with rlen == 0
|
||||||
wcomplete = 0;
|
|
||||||
|
int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract, bool defer) {
|
||||||
|
if (_verbose) {
|
||||||
|
fprintf(stderr, "usb_xfer: deferred: %ld\n", obuf - _obuf);
|
||||||
|
}
|
||||||
|
if (defer && !rlen && obuf - _obuf + wlen < MAX_BUFFER - 12) {
|
||||||
|
obuf += wlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out whole buffer
|
||||||
|
|
||||||
|
if (obuf - _obuf > MAX_BUFFER) {
|
||||||
|
throw runtime_error("buffer overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
wlen += obuf - _obuf;
|
||||||
|
if (wlen > MAX_BUFFER) {
|
||||||
|
throw runtime_error("buffer overflow");
|
||||||
|
}
|
||||||
|
obuf = _obuf;
|
||||||
|
|
||||||
|
if (wlen == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (_verbose) {
|
if (_verbose) {
|
||||||
fprintf(stderr, "obuf[%d] = {", wlen);
|
fprintf(stderr, "obuf[%d] = {", wlen);
|
||||||
for (unsigned i = 0; i < wlen; ++i) {
|
for (unsigned i = 0; i < wlen; ++i) {
|
||||||
|
|
@ -57,6 +83,8 @@ int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) {
|
||||||
}
|
}
|
||||||
fprintf(stderr, "}\n\n");
|
fprintf(stderr, "}\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int wcomplete = 0, rcomplete = 0;
|
||||||
libusb_fill_bulk_transfer(wtrans, dev_handle, CH347JTAG_WRITE_EP, obuf,
|
libusb_fill_bulk_transfer(wtrans, dev_handle, CH347JTAG_WRITE_EP, obuf,
|
||||||
wlen, sync_cb, &wcomplete, CH347JTAG_TIMEOUT);
|
wlen, sync_cb, &wcomplete, CH347JTAG_TIMEOUT);
|
||||||
int r = libusb_submit_transfer(wtrans);
|
int r = libusb_submit_transfer(wtrans);
|
||||||
|
|
@ -86,8 +114,6 @@ int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) {
|
||||||
wcomplete = 1;
|
wcomplete = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsigned rdone = 0;
|
|
||||||
wait_rcompletion:
|
|
||||||
while (!rcomplete) {
|
while (!rcomplete) {
|
||||||
r = libusb_handle_events_completed(usb_ctx, &rcomplete);
|
r = libusb_handle_events_completed(usb_ctx, &rcomplete);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
|
@ -104,24 +130,16 @@ wait_rcompletion:
|
||||||
return LIBUSB_ERROR_IO;
|
return LIBUSB_ERROR_IO;
|
||||||
}
|
}
|
||||||
if (rlen) {
|
if (rlen) {
|
||||||
rdone += rtrans->actual_length;
|
|
||||||
if (rtrans->status != LIBUSB_TRANSFER_COMPLETED) {
|
if (rtrans->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||||
return LIBUSB_ERROR_IO;
|
return LIBUSB_ERROR_IO;
|
||||||
}
|
}
|
||||||
if (rlen > rdone) {
|
if ((int)rlen > rtrans->actual_length) {
|
||||||
rcomplete = 0;
|
throw runtime_error("input buffer underflow");
|
||||||
libusb_fill_bulk_transfer(rtrans, dev_handle, CH347JTAG_READ_EP,
|
|
||||||
&ibuf[rdone], rlen - rdone, sync_cb, &rcomplete,
|
|
||||||
CH347JTAG_TIMEOUT);
|
|
||||||
r = libusb_submit_transfer(rtrans);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
goto wait_rcompletion;
|
|
||||||
}
|
}
|
||||||
if (ract) *ract = rdone;
|
if (ract) *ract = rtrans->actual_length;
|
||||||
if (_verbose) {
|
if (_verbose) {
|
||||||
fprintf(stderr, "ibuf[%d] = {", rdone);
|
fprintf(stderr, "ibuf[%d] = {", rtrans->actual_length);
|
||||||
for (unsigned i = 0; i < rdone; ++i) {
|
for (int i = 0; i < rtrans->actual_length; ++i) {
|
||||||
fprintf(stderr, "%02x ", ibuf[i]);
|
fprintf(stderr, "%02x ", ibuf[i]);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "}\n\n");
|
fprintf(stderr, "}\n\n");
|
||||||
|
|
@ -131,12 +149,14 @@ wait_rcompletion:
|
||||||
}
|
}
|
||||||
|
|
||||||
int CH347Jtag::setClk(const uint8_t &factor) {
|
int CH347Jtag::setClk(const uint8_t &factor) {
|
||||||
|
// flush the obuf
|
||||||
|
usb_xfer(0, 0, 0, false); // is called from constructor, don't replace with virtual flush()
|
||||||
memset(obuf, 0, 16);
|
memset(obuf, 0, 16);
|
||||||
obuf[0] = CMD_CLK;
|
obuf[0] = CMD_CLK;
|
||||||
obuf[1] = 6;
|
obuf[1] = 6;
|
||||||
obuf[4] = factor;
|
obuf[4] = factor;
|
||||||
unsigned actual = 0;
|
unsigned actual = 0;
|
||||||
int rv = usb_xfer(9, 4, &actual);
|
int rv = usb_xfer(9, 4, &actual, false);
|
||||||
if (rv || actual != 4)
|
if (rv || actual != 4)
|
||||||
return -1;
|
return -1;
|
||||||
if (ibuf[0] != 0xd0 || ibuf[3] != 0)
|
if (ibuf[0] != 0xd0 || ibuf[3] != 0)
|
||||||
|
|
@ -145,7 +165,7 @@ int CH347Jtag::setClk(const uint8_t &factor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CH347Jtag::CH347Jtag(uint32_t clkHZ, int8_t verbose):
|
CH347Jtag::CH347Jtag(uint32_t clkHZ, int8_t verbose):
|
||||||
_verbose(verbose>1), dev_handle(NULL), usb_ctx(NULL)
|
_verbose(verbose>1), dev_handle(NULL), usb_ctx(NULL), obuf(_obuf)
|
||||||
{
|
{
|
||||||
int actual_length = 0;
|
int actual_length = 0;
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
|
|
@ -223,47 +243,52 @@ CH347Jtag::~CH347Jtag()
|
||||||
|
|
||||||
int CH347Jtag::_setClkFreq(uint32_t clkHZ)
|
int CH347Jtag::_setClkFreq(uint32_t clkHZ)
|
||||||
{
|
{
|
||||||
unsigned i = 0, sl = 2000000;
|
#if 1
|
||||||
if (clkHZ <= 2000000) {
|
unsigned i = 0, sl = 60000000 >> 5;
|
||||||
_clkHZ = 2000000;
|
for (; i < 6; ++i, sl <<= 1) {
|
||||||
i = 0;
|
if (clkHZ < sl)
|
||||||
} else {
|
break;
|
||||||
for (; i < 5; ++i, sl *= 2) {
|
|
||||||
if (clkHZ < sl) break;
|
|
||||||
_clkHZ = sl;
|
|
||||||
}
|
|
||||||
i--;
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
int i = 6;
|
||||||
|
unsigned sl = 60000000;
|
||||||
|
for (; i > 1; --i, sl >>= 1) {
|
||||||
|
if (clkHZ > sl)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_clkHZ = sl;
|
||||||
if (setClk(i)) {
|
if (setClk(i)) {
|
||||||
printError("failed to set clock rate");
|
printError("failed to set clock rate");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
char mess[256];
|
char mess[256];
|
||||||
snprintf(mess, 256, "JTAG TCK frequency set to %d MHz\n\n", _clkHZ/1000000);
|
snprintf(mess, 256, "JTAG TCK frequency set to %.3f MHz\n\n", _clkHZ/1e6);
|
||||||
printInfo(mess);
|
printInfo(mess);
|
||||||
return _clkHZ;
|
return _clkHZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CH347Jtag::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer)
|
int CH347Jtag::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer)
|
||||||
{
|
{
|
||||||
(void) flush_buffer;
|
if (get_obuf_length() < (int)(len * 2 + 4)) { // check if there is enough room left
|
||||||
|
flush();
|
||||||
|
}
|
||||||
uint8_t *ptr = obuf;
|
uint8_t *ptr = obuf;
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
if (ptr == obuf) {
|
if (ptr == obuf) {
|
||||||
*ptr++ = CMD_BITS_WO;
|
*ptr++ = CMD_BITS_WO;
|
||||||
ptr += 2; // leave place for length;
|
ptr += 2; // leave place for length;
|
||||||
}
|
}
|
||||||
uint8_t x = /*SIG_TDI |*/ ((tms[i >> 3] & (1 << (i & 7))) ? SIG_TMS : 0);
|
uint8_t x = ((tms[i >> 3] & (1 << (i & 7))) ? SIG_TMS : 0);
|
||||||
*ptr++ = x;
|
*ptr++ = x;
|
||||||
*ptr++ = x | SIG_TCK;
|
*ptr++ = x | SIG_TCK;
|
||||||
unsigned wlen = ptr - obuf;
|
int wlen = ptr - obuf;
|
||||||
if (wlen > sizeof(obuf) - 3 || i == len - 1) {
|
if (wlen + 1 >= get_obuf_length() || i == len - 1) {
|
||||||
*ptr++ = x; // clear TCK
|
*ptr++ = x; // clear TCK
|
||||||
++wlen;
|
wlen = ptr - obuf;
|
||||||
obuf[1] = wlen - 3;
|
obuf[1] = wlen - 3;
|
||||||
obuf[2] = (wlen - 3) >> 8;
|
obuf[2] = (wlen - 3) >> 8;
|
||||||
int ret = usb_xfer(wlen, 0, 0);
|
int ret = usb_xfer(wlen, 0, 0, !flush_buffer);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
cerr << "writeTMS: usb bulk write failed: " <<
|
cerr << "writeTMS: usb bulk write failed: " <<
|
||||||
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
||||||
|
|
@ -280,6 +305,13 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len)
|
||||||
uint8_t bits = 0;
|
uint8_t bits = 0;
|
||||||
if (tms) bits |= SIG_TMS;
|
if (tms) bits |= SIG_TMS;
|
||||||
if (tdi) bits |= SIG_TDI;
|
if (tdi) bits |= SIG_TDI;
|
||||||
|
if (!bits && len > 7) {
|
||||||
|
return writeTDI(0, 0, len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_obuf_length() < (int)(len * 2 + 4)) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t *ptr = obuf;
|
uint8_t *ptr = obuf;
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
for (uint32_t i = 0; i < len; ++i) {
|
||||||
|
|
@ -289,13 +321,13 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len)
|
||||||
}
|
}
|
||||||
*ptr++ = bits;
|
*ptr++ = bits;
|
||||||
*ptr++ = bits | SIG_TCK;
|
*ptr++ = bits | SIG_TCK;
|
||||||
unsigned wlen = ptr - obuf;
|
int wlen = ptr - obuf;
|
||||||
if (wlen > sizeof(obuf) - 3 || i == len - 1) {
|
if (wlen + 1 >= get_obuf_length() || i == len - 1) {
|
||||||
*ptr++ = bits; // clear TCK
|
*ptr++ = bits; // clear TCK
|
||||||
++wlen;
|
wlen = ptr - obuf;
|
||||||
obuf[1] = wlen - 3;
|
obuf[1] = wlen - 3;
|
||||||
obuf[2] = (wlen - 3) >> 8;
|
obuf[2] = (wlen - 3) >> 8;
|
||||||
int ret = usb_xfer(wlen, 0, 0);
|
int ret = usb_xfer(wlen, 0, 0, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
cerr << "writeCLK: usb bulk write failed: " <<
|
cerr << "writeCLK: usb bulk write failed: " <<
|
||||||
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
||||||
|
|
@ -309,25 +341,32 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len)
|
||||||
|
|
||||||
int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
{
|
{
|
||||||
if (!tx || !len)
|
if (len == 0)
|
||||||
return 0;
|
return 0;
|
||||||
unsigned bytes = (len - ((end)?1:0)) / 8;
|
unsigned bytes = (len - (end ? 1 : 0)) / 8;
|
||||||
unsigned bits = len - bytes * 8;
|
unsigned bits = len - bytes * 8;
|
||||||
uint8_t *rptr = rx;
|
uint8_t *rptr = rx;
|
||||||
const uint8_t *tptr = tx;
|
const uint8_t *tptr = tx;
|
||||||
const uint8_t *txend = tx + bytes;
|
const uint8_t *txend = tx + bytes;
|
||||||
uint8_t cmd = (rx) ? CMD_BYTES_WR : CMD_BYTES_WO;
|
uint8_t cmd = (rx != nullptr) ? CMD_BYTES_WR : CMD_BYTES_WO;
|
||||||
while (tptr < txend) {
|
while (tptr < txend) {
|
||||||
unsigned avail = sizeof(obuf) - 3;
|
if (get_obuf_length() < 4) {
|
||||||
unsigned chunk = (txend - tptr < avail)? txend - tptr: avail;
|
flush();
|
||||||
memcpy(&obuf[3], tptr, chunk);
|
}
|
||||||
|
int avail = get_obuf_length() - 3;
|
||||||
|
int chunk = (txend - tptr < avail)? txend - tptr: avail;
|
||||||
|
if (tx) {
|
||||||
|
memcpy(&obuf[3], tptr, chunk);
|
||||||
|
} else {
|
||||||
|
memset(&obuf[3], 0, chunk);
|
||||||
|
}
|
||||||
tptr += chunk;
|
tptr += chunk;
|
||||||
// write header
|
// write header
|
||||||
obuf[0] = cmd;
|
obuf[0] = cmd;
|
||||||
obuf[1] = chunk;
|
obuf[1] = chunk;
|
||||||
obuf[2] = chunk >> 8;
|
obuf[2] = chunk >> 8;
|
||||||
unsigned actual_length = 0;
|
unsigned actual_length = 0;
|
||||||
int ret = usb_xfer(chunk + 3, (rx) ? chunk + 3 : 0, &actual_length);
|
int ret = usb_xfer(chunk + 3, (rx) ? chunk + 3 : 0, &actual_length, rx == 0 && get_obuf_length());
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
cerr << "writeTDI: usb bulk read failed: " <<
|
cerr << "writeTDI: usb bulk read failed: " <<
|
||||||
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
libusb_strerror(static_cast<libusb_error>(ret)) << endl;
|
||||||
|
|
@ -347,11 +386,14 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
if (bits == 0)
|
if (bits == 0)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
cmd = (rx) ? CMD_BITS_WR : CMD_BITS_WO;
|
cmd = (rx) ? CMD_BITS_WR : CMD_BITS_WO;
|
||||||
|
if (get_obuf_length() < (int)(4 + bits * 2)) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
uint8_t *ptr = &obuf[3];
|
uint8_t *ptr = &obuf[3];
|
||||||
uint8_t x = 0;
|
uint8_t x = 0;
|
||||||
const uint8_t *bptr = &tx[bytes];
|
const uint8_t *bptr = tx + bytes;
|
||||||
for (unsigned i = 0; i < bits; ++i) {
|
for (unsigned i = 0; i < bits; ++i) {
|
||||||
uint8_t txb = bptr[i >> 3];
|
uint8_t txb = (tx) ? bptr[i >> 3] : 0;
|
||||||
uint8_t _tdi = (txb & (1 << (i & 7))) ? SIG_TDI : 0;
|
uint8_t _tdi = (txb & (1 << (i & 7))) ? SIG_TDI : 0;
|
||||||
x = _tdi;
|
x = _tdi;
|
||||||
if (end && i == bits - 1) {
|
if (end && i == bits - 1) {
|
||||||
|
|
@ -365,7 +407,7 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
obuf[0] = cmd;
|
obuf[0] = cmd;
|
||||||
obuf[1] = wlen - 3;
|
obuf[1] = wlen - 3;
|
||||||
obuf[2] = (wlen - 3) >> 8;
|
obuf[2] = (wlen - 3) >> 8;
|
||||||
int ret = usb_xfer(wlen, (rx) ? (bits + 3) : 0, &actual_length);
|
int ret = usb_xfer(wlen, (rx) ? (bits + 3) : 0, &actual_length, rx == nullptr);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
cerr << "writeTDI: usb bulk read failed: " <<
|
cerr << "writeTDI: usb bulk read failed: " <<
|
||||||
|
|
@ -381,9 +423,9 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
cerr << "writeTDI: invalid read data: " << endl;
|
cerr << "writeTDI: invalid read data: " << endl;
|
||||||
return -EXIT_FAILURE;
|
return -EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < size / 16; ++i) {
|
for (unsigned i = 0; i < size / 8; ++i) {
|
||||||
uint8_t b = 0;
|
uint8_t b = 0;
|
||||||
uint8_t *xb = &ibuf[3 + i * 16 + 1];
|
uint8_t *xb = &ibuf[3 + i * 8];
|
||||||
for (unsigned j = 0; j < 8; ++j)
|
for (unsigned j = 0; j < 8; ++j)
|
||||||
b |= (xb[j] & 1) << j;
|
b |= (xb[j] & 1) << j;
|
||||||
*rptr++ = b;
|
*rptr++ = b;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include "jtagInterface.hpp"
|
#include "jtagInterface.hpp"
|
||||||
|
|
||||||
|
constexpr unsigned MAX_BUFFER = 512;
|
||||||
|
|
||||||
class CH347Jtag : public JtagInterface {
|
class CH347Jtag : public JtagInterface {
|
||||||
public:
|
public:
|
||||||
CH347Jtag(uint32_t clkHZ, int8_t verbose);
|
CH347Jtag(uint32_t clkHZ, int8_t verbose);
|
||||||
|
|
@ -22,11 +24,11 @@ class CH347Jtag : public JtagInterface {
|
||||||
/* clk */
|
/* clk */
|
||||||
int toggleClk(uint8_t tms, uint8_t tdo, uint32_t clk_len) override;
|
int toggleClk(uint8_t tms, uint8_t tdo, uint32_t clk_len) override;
|
||||||
|
|
||||||
int get_buffer_size() override { return 0;}
|
int get_buffer_size() override {return get_obuf_length();}
|
||||||
|
|
||||||
bool isFull() override { return false;}
|
bool isFull() override {return get_obuf_length() == 0;}
|
||||||
|
|
||||||
int flush() override {return 0;}
|
int flush() override {return usb_xfer(0, 0, 0, false);}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
|
|
@ -35,8 +37,9 @@ class CH347Jtag : public JtagInterface {
|
||||||
libusb_device_handle *dev_handle;
|
libusb_device_handle *dev_handle;
|
||||||
libusb_context *usb_ctx;
|
libusb_context *usb_ctx;
|
||||||
struct libusb_transfer *wtrans, *rtrans;
|
struct libusb_transfer *wtrans, *rtrans;
|
||||||
int rcomplete, wcomplete;
|
uint8_t ibuf[MAX_BUFFER];
|
||||||
uint8_t ibuf[512];
|
uint8_t _obuf[MAX_BUFFER];
|
||||||
uint8_t obuf[512];
|
uint8_t *obuf;
|
||||||
int usb_xfer(unsigned wlen, unsigned rlen, unsigned *actual);
|
int get_obuf_length() const {return MAX_BUFFER - (obuf - _obuf);}
|
||||||
|
int usb_xfer(unsigned wlen, unsigned rlen, unsigned *actual, bool defer);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue