📄 usbhost.c
字号:
if (length > USBTEST_MAX_CONTROL_DATA) {
fprintf(stderr, "usbhost: internal error, control message involves too much data.\n");
exit(EXIT_FAILURE);
}
#if 1
// Workaround - send additional data in the index and length fields.
if ((length > 0) && (USB_DIR_OUT == (USB_ENDPOINT_DIR_MASK & request_type))) {
int i;
unsigned char* buf = (unsigned char*) data;
for (i = 0; i < length; i+= 4) {
int this_len = length - 1;
int ioctl_result;
transfer.bRequestType = USB_TYPE_CLASS | USB_RECIP_DEVICE;
if (this_len > 4) {
this_len = 4;
}
switch (this_len) {
case 1: transfer.bRequest = USBTEST_CONTROL_DATA1; break;
case 2: transfer.bRequest = USBTEST_CONTROL_DATA2; break;
case 3: transfer.bRequest = USBTEST_CONTROL_DATA3; break;
case 4: transfer.bRequest = USBTEST_CONTROL_DATA4; break;
default:
fprintf(stderr, "usbhost: internal error, confusion about transfer length.\n");
exit(EXIT_FAILURE);
}
transfer.wValue = (buf[i] << 8) | buf[i+1]; // Possible read beyond end of buffer,
transfer.wIndex = (buf[i+2] << 8) | buf[i+3]; // but not worth worrying about.
transfer.wLength = 0;
transfer.timeout = 10 * 1000; // ten seconds, the target should always accept data faster than this.
transfer.data = NULL;
// This is too strict, deciding what to do about errors should be
// handled by higher-level code. However it will do for now.
ioctl_result = ioctl(fd, USBDEVFS_CONTROL, &transfer);
if (0 != ioctl_result) {
fprintf(stderr, "usbhost: error, failed to send control message (data) to target.\n");
exit(EXIT_FAILURE);
}
}
// There is no more data to be transferred.
length = 0;
}
#endif
transfer.bRequestType = request_type;
transfer.bRequest = request;
transfer.wValue = value;
transfer.wIndex = index;
transfer.wLength = length;
transfer.timeout = 10000;
transfer.data = data;
result = ioctl(fd, USBDEVFS_CONTROL, &transfer);
return result;
}
// A variant of the above which can be called when the target should always respond
// correctly. This can be used for class control messages.
static int
usb_reliable_control_message(int fd, int request_type, int request, int value, int index, int length, void* data)
{
int result = usb_control_message(fd, request_type, request, value, index, length, data);
if (-1 == result) {
fprintf(stderr, "usbhost: error, failed to send control message %02x to target.\n", request);
fprintf(stderr, " : errno %d (%s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
return result;
}
// Either send or receive a single bulk message. The top bit of the endpoint
// number indicates the direction.
static int
usb_bulk_message(int fd, int endpoint, unsigned char* buffer, int length)
{
struct usbdevfs_bulktransfer transfer;
int result;
transfer.ep = endpoint;
transfer.len = length;
transfer.timeout = 60 * 60 * 1000;
// An hour. These operations should not time out because that
// leaves the system in a confused state. Instead there is
// higher-level recovery code that should ensure the operation
// really does complete, and the return value here is used
// by the calling code to determine whether the operation
// was successful or whether there was an error and the recovery
// code was invoked.
transfer.data = buffer;
errno = 0;
result = ioctl(fd, USBDEVFS_BULK, &transfer);
return result;
}
// Synchronise with the target. This can be used after the host has sent a request that
// may take a bit of time, e.g. it may involve waking up a thread. The host will send
// synch requests at regular intervals, until the target is ready.
//
// The limit argument can be used to avoid locking up. -1 means loop forever, otherwise
// it means that many iterations of 100ms apiece.
static int
usb_sync(int fd, int limit)
{
unsigned char buf[1];
struct timespec delay;
int loops = 0;
int result = 0;
VERBOSE(2, "Synchronizing with target\n");
while (1) {
buf[0] = 0;
usb_reliable_control_message(fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_SYNCH, 0, 0, 1, buf);
if (buf[0]) {
result = 1;
break;
} else {
if ((-1 != limit) && (++loops > limit)) {
break;
} else {
VERBOSE(3, "Not yet synchronized, sleeping\n");
delay.tv_sec = 0;
delay.tv_nsec = 100000000; // 100 ms
nanosleep(&delay, NULL);
}
}
}
VERBOSE(2, "%s\n", result ? "Synchronized" : "Not synchronized");
return result;
}
// Abort the target. Things seem to be completely messed up and there is no easy
// way to restore sanity to both target and host.
static void
usb_abort(int fd)
{
VERBOSE(2, "Target-side abort operation invoked\n");
usb_reliable_control_message(fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_ABORT, 0, 0, 0, (void*)0);
}
/*}}}*/
/*{{{ Initialise endpoints */
// ----------------------------------------------------------------------------
// On power-up some endpoints may not be in a sensible state. For example,
// with the SA11x0 the hardware may start accepting bulk OUT transfers
// before the target-side software has started a receive operation,
// so if the host sends a bulk packet before the target is ready then
// things get messy. This is especially troublesome if the target-side
// attempts any diagnostic output because of verbosity.
//
// This code loops through the various endpoints and makes sure that
// they are all in a reasonable state, before any real tests get run
// That means known hardware flaws do not show up as test failures,
// but of course they are still documented and application software
// will have to do the right thing.
static void
usb_initialise_control_endpoint(int min_size, int max_size)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
static void
usb_initialise_isochronous_in_endpoint(int number, int min_size, int max_size)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
static void
usb_initialise_isochronous_out_endpoint(int number, int min_size, int max_size)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
static void
usb_initialise_bulk_in_endpoint(int number, int min_size, int max_size, int padding)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
static void
usb_initialise_bulk_out_endpoint(int number, int min_size, int max_size)
{
unsigned char buf[1];
// On the SA1110 the hardware comes up with a bogus default value,
// causing the hardware to accept packets before the software has
// set up DMA or in any way prepared for incoming data. This is
// a problem. It is worked around by making the target receive
// a single packet, sending that packet, and then performing a
// sync.
VERBOSE(2, "Performing bulk OUT initialization on endpoint %d\n", number);
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
USBTEST_INIT_BULK_OUT, number, 0, 0, (void*) 0);
usb_bulk_message(usb_master_fd, number, buf, 1);
usb_sync(usb_master_fd, 10);
}
static void
usb_initialise_interrupt_in_endpoint(int number, int min_size, int max_size)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
static void
usb_initialise_interrupt_out_endpoint(int number, int min_size, int max_size)
{
// At this time there are no known problems on any hardware
// that would need to be addressed
}
/*}}}*/
/*{{{ Host/target common code */
#define HOST
#include "../tests/common.c"
/*}}}*/
/*{{{ The test cases themselves */
/*{{{ UsbTest definition */
// ----------------------------------------------------------------------------
// All the data associated with a single test.
typedef struct UsbTest {
// A "unique" identifier to make verbose output easier to understand.
int id;
// Which file descriptor should be used to access USB.
int fd;
// Which test should be run.
usbtest which_test;
// Test-specific details.
union {
UsbTest_Bulk bulk;
UsbTest_ControlIn control_in;
} test_params;
// How to recover from any problems. Specifically, what kind of message
// could the target send or receive that would unlock the thread on this
// side.
UsbTest_Recovery recovery;
int result_pass;
char result_message[USBTEST_MAX_MESSAGE];
unsigned char buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];
} UsbTest;
// Reset the information in a given test. This is used by the pool allocation
// code. The data union is left alone, filling in the appropriate union
// member is left to other code.
static void
reset_usbtest(UsbTest* test)
{
static int next_id = 1;
test->id = next_id++;
test->which_test = usbtest_invalid;
usbtest_recovery_reset(&(test->recovery));
test->result_pass = 0;
test->result_message[0] = '\0';
}
/*}}}*/
/*{{{ bulk OUT */
static void
run_test_bulk_out(UsbTest* test)
{
unsigned char* buf = test->buffer;
int i;
VERBOSE(1, "Starting test %d, bulk OUT on endpoint %d\n", test->id, test->test_params.bulk.endpoint);
for (i = 0; i < test->test_params.bulk.number_packets; i++) {
int transferred;
int packet_size = test->test_params.bulk.tx_size;
test->recovery.endpoint = test->test_params.bulk.endpoint;
test->recovery.protocol = USB_ENDPOINT_XFER_BULK;
test->recovery.size = packet_size;
usbtest_fill_buffer(&(test->test_params.bulk.data), buf, packet_size);
if (verbose < 3) {
VERBOSE(2, "Bulk OUT test %d: iteration %d, packet size %d\n", test->id, i, packet_size);
} else {
// Output the first 32 bytes of data as well.
char msg[256];
int index;
int j;
index = snprintf(msg, 255, "Bulk OUT test %d: iteration %d, packet size %d\n Data %s:",
test->id, i, packet_size,
(usbtestdata_none == test->test_params.bulk.data.format) ? "(uninitialized)" : "");
for (j = 0; ((j + 3) < packet_size) && (j < 32); j+= 4) {
index += snprintf(msg+index, 255-index, " %02x%02x%02x%02x",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -