|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | #include "usbdpi.h" | 
|  |  | 
|  | #ifdef __linux__ | 
|  | #include <pty.h> | 
|  | #elif __APPLE__ | 
|  | #include <util.h> | 
|  | #endif | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <limits.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | // Historically the simulation started too fast to connect to all | 
|  | // the fifos and terminals without loss of output. So a delay was added. | 
|  | // Today the startup is slow enough this does not seem to be needed. | 
|  | // In case things change again Im going to leave this behind a define | 
|  | // for now, but if this continues not to be needed the code can be deleted. | 
|  | // Uncomment next line if you need the delay | 
|  | // #define NEED_SLEEP | 
|  |  | 
|  | static const char *st_states[] = {"ST_IDLE 0", "ST_SEND 1", "ST_GET 2", | 
|  | "ST_SYNC 3", "ST_EOP 4",  "ST_EOP0 5"}; | 
|  |  | 
|  | static const char *hs_states[] = { | 
|  | "HS_STARTFRAME 0", "HS_WAITACK 1",   "HS_SET_DATASTAGE 2", "HS_DS_RXDATA 3", | 
|  | "HS_DS_SENDACK 4", "HS_DONEDADR 5",  "HS_REQDATA 6",       "HS_WAITDATA 7", | 
|  | "HS_SENDACK 8",    "HS_WAIT_PKT 9",  "HS_ACKIFDATA 10",    "HS_SENDHI 11", | 
|  | "HS_EMPTYDATA 12", "HS_WAITACK2 13", "HS_NEXTFRAME 14"}; | 
|  |  | 
|  | void *usbdpi_create(const char *name, int loglevel) { | 
|  | struct usbdpi_ctx *ctx = | 
|  | (struct usbdpi_ctx *)calloc(1, sizeof(struct usbdpi_ctx)); | 
|  | assert(ctx); | 
|  |  | 
|  | ctx->tick = 0; | 
|  | ctx->frame = 0; | 
|  | ctx->framepend = 0; | 
|  | ctx->lastframe = 0; | 
|  | ctx->last_pu = 0; | 
|  | ctx->inframe = 4; | 
|  | ctx->state = ST_IDLE; | 
|  | ctx->driving = 0; | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | ctx->loglevel = loglevel; | 
|  | ctx->mon = monitor_usb_init(); | 
|  | ctx->baudrate_set_successfully = 0; | 
|  |  | 
|  | char cwd[PATH_MAX]; | 
|  | char *cwd_rv; | 
|  | cwd_rv = getcwd(cwd, sizeof(cwd)); | 
|  | assert(cwd_rv != NULL); | 
|  |  | 
|  | int rv; | 
|  |  | 
|  | // Monitor log file | 
|  | rv = snprintf(ctx->mon_pathname, PATH_MAX, "%s/%s.log", cwd, name); | 
|  | assert(rv <= PATH_MAX && rv > 0); | 
|  | ctx->mon_file = fopen(ctx->mon_pathname, "w"); | 
|  | if (ctx->mon_file == NULL) { | 
|  | fprintf(stderr, "USB: Unable to open monitor file at %s: %s\n", | 
|  | ctx->mon_pathname, strerror(errno)); | 
|  | return NULL; | 
|  | } | 
|  | // more useful for tail -f | 
|  | setlinebuf(ctx->mon_file); | 
|  | printf( | 
|  | "\nUSB: Monitor output file created at %s. Works well with tail:\n" | 
|  | "$ tail -f %s\n", | 
|  | ctx->mon_pathname, ctx->mon_pathname); | 
|  |  | 
|  | return (void *)ctx; | 
|  | } | 
|  |  | 
|  | const char *decode_usb[] = {"SE0", "0-K", "1-J", "SE1"}; | 
|  |  | 
|  | void usbdpi_device_to_host(void *ctx_void, const svBitVecVal *usb_d2p) { | 
|  | struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; | 
|  | assert(ctx); | 
|  | int d2p = usb_d2p[0]; | 
|  | int dp, dn; | 
|  | int n; | 
|  | char obuf[MAX_OBUF]; | 
|  |  | 
|  | char raw_str[D2P_BITS + 1]; | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < D2P_BITS; i++) { | 
|  | raw_str[D2P_BITS - i - 1] = d2p & (1 << i) ? '1' : '0'; | 
|  | } | 
|  | } | 
|  | raw_str[D2P_BITS] = 0; | 
|  |  | 
|  | if (d2p & (D2P_DP_EN | D2P_DN_EN | D2P_D_EN)) { | 
|  | if (ctx->state == ST_SEND) { | 
|  | printf("USB: %4x %8d error state %s hs %s and device drives\n", | 
|  | ctx->frame, ctx->tick, st_states[ctx->state], | 
|  | hs_states[ctx->hostSt]); | 
|  | } | 
|  | ctx->state = ST_GET; | 
|  | } else { | 
|  | if (ctx->state == ST_GET) { | 
|  | ctx->state = ST_IDLE; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((d2p & D2P_DNPU) && (d2p & D2P_DPPU)) { | 
|  | printf("USB: %4x %8d error both pullups are driven\n", ctx->frame, | 
|  | ctx->tick); | 
|  | } | 
|  | if ((d2p & D2P_PU) != ctx->last_pu) { | 
|  | n = snprintf(obuf, MAX_OBUF, "%4x %8d Pullup change to %s%s%s\n", | 
|  | ctx->frame, ctx->tick, (d2p & D2P_DPPU) ? "DP Pulled up " : "", | 
|  | (d2p & D2P_DNPU) ? "DN Pulled up " : "", | 
|  | (d2p & D2P_TXMODE_SE) ? "SingleEnded" : "Differential"); | 
|  | ssize_t written = fwrite(obuf, sizeof(char), (size_t)n, ctx->mon_file); | 
|  | assert(written == n); | 
|  | ctx->last_pu = d2p & D2P_PU; | 
|  | } | 
|  | if (d2p & D2P_TXMODE_SE) { | 
|  | // Normal D+/D- mode | 
|  | if (d2p & D2P_DNPU) { | 
|  | // DN pullup would say DP and DN are swapped | 
|  | dp = ((d2p & D2P_DN_EN) && (d2p & D2P_DN)) || | 
|  | (!(d2p & D2P_DN_EN) && (d2p & D2P_DNPU)); | 
|  | dn = (d2p & D2P_DP_EN) && (d2p & D2P_DP); | 
|  | } else { | 
|  | // No DN pullup so normal orientation | 
|  | dp = ((d2p & D2P_DP_EN) && (d2p & D2P_DP)) || | 
|  | (!(d2p & D2P_DP_EN) && (d2p & D2P_DPPU)); | 
|  | dn = (d2p & D2P_DN_EN) && (d2p & D2P_DN); | 
|  | } | 
|  | } else { | 
|  | // "differential" mode uses D and SE0 | 
|  | if (d2p & D2P_D_EN) { | 
|  | if (d2p & D2P_DNPU) { | 
|  | // Pullup says swap i.e. D is inverted | 
|  | dp = (d2p & D2P_SE0) ? 0 : ((d2p & D2P_D) ? 0 : 1); | 
|  | dn = (d2p & D2P_SE0) ? 0 : ((d2p & D2P_D) ? 1 : 0); | 
|  | } else { | 
|  | dp = (d2p & D2P_SE0) ? 0 : ((d2p & D2P_D) ? 1 : 0); | 
|  | dn = (d2p & D2P_SE0) ? 0 : ((d2p & D2P_D) ? 0 : 1); | 
|  | } | 
|  | } else { | 
|  | dp = (d2p & D2P_PU) ? 1 : 0; | 
|  | dn = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ctx->loglevel & LOG_BIT) { | 
|  | const char *pullup = (d2p & D2P_PU) ? "PU" : "  "; | 
|  | const char *state = | 
|  | (ctx->state == ST_GET) ? decode_usb[dp << 1 | dn] : "ZZ "; | 
|  |  | 
|  | n = snprintf(obuf, MAX_OBUF, "%4x %8d %s %s %s %x\n", ctx->frame, ctx->tick, | 
|  | raw_str, pullup, state, d2p); | 
|  | ssize_t written = fwrite(obuf, sizeof(char), (size_t)n, ctx->mon_file); | 
|  | assert(written == n); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Note: start points to the PID which is not in the CRC | 
|  | void add_crc16(uint8_t *dp, int start, int pos) { | 
|  | uint32_t crc = CRC16(dp + start + 1, pos - start - 1); | 
|  | dp[pos] = crc & 0xff; | 
|  | dp[pos + 1] = crc >> 8; | 
|  | } | 
|  |  | 
|  | // Set device address (with null data stage) | 
|  | void setDeviceAddress(struct usbdpi_ctx *ctx) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | // Setup PID and data to set device 2 | 
|  | ctx->data[0] = USB_PID_SETUP; | 
|  | ctx->data[1] = 0; | 
|  | ctx->data[2] = 0 | CRC5(0, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | if (INSERT_ERR_PID) { | 
|  | ctx->data[3] = 0xc4; | 
|  | } | 
|  | ctx->data[4] = 0;  // h2d, std, device | 
|  | ctx->data[5] = 5;  // set address | 
|  | ctx->data[6] = 2;  // device address | 
|  | ctx->data[7] = 0; | 
|  | // Trigger bitstuffing, technically the device | 
|  | // behaviour is unspecified with wIndex != 0 | 
|  | ctx->data[8] = 0xFF;  // wIndex = 0xFF00 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0;  // wLength = 0 | 
|  | ctx->data[11] = 0; | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | // ctx->data[12] = 0xEB; // pre-computed CRC16 | 
|  | // ctx->data[13] = 0x16; | 
|  | if (INSERT_ERR_CRC) { | 
|  | // Flip the last CRC bit to emulate a CRC error | 
|  | ctx->data[13] = ctx->data[13] ^ 0x01; | 
|  | } | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | case HS_WAITACK: | 
|  | ctx->wait = ctx->tick_bits + 532;  // HACK | 
|  | ctx->hostSt = HS_SET_DATASTAGE; | 
|  | break; | 
|  | case HS_SET_DATASTAGE: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 0; | 
|  | ctx->data[2] = 0 | CRC5(0, 11) << 3; | 
|  | ctx->hostSt = HS_DS_RXDATA; | 
|  | } | 
|  | break; | 
|  | case HS_DS_RXDATA: | 
|  | ctx->wait = ctx->tick_bits + 24;  // HACK -- 2 bytes | 
|  | ctx->hostSt = HS_DS_SENDACK; | 
|  | break; | 
|  | case HS_DS_SENDACK: | 
|  | if (ctx->tick_bits >= ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 1; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_ACK; | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf("[usbdpi] setDeviceAddress done\n"); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Get Descriptor | 
|  | void readDescriptor(struct usbdpi_ctx *ctx) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_SETUP; | 
|  | ctx->data[1] = 2; | 
|  | ctx->data[2] = 0 | CRC5(2, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0x80;  // d2h, std, device | 
|  | ctx->data[5] = 6;     // get descr | 
|  | ctx->data[6] = 0;     // index 0 | 
|  | ctx->data[7] = 1;     // type device | 
|  | ctx->data[8] = 0;     // wIndex = 0 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0x12;  // wLength = 18 | 
|  | ctx->data[11] = 0; | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | // ctx->data[12] = 0xE0; // pre-computed CRC32 | 
|  | // ctx->data[13] = 0xF4; | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | case HS_WAITACK: | 
|  | ctx->wait = ctx->tick_bits + 532;  // HACK | 
|  | ctx->hostSt = HS_REQDATA; | 
|  | break; | 
|  | case HS_REQDATA: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 2; | 
|  | ctx->data[2] = 0 | CRC5(2, 11) << 3; | 
|  | ctx->hostSt = HS_WAITDATA; | 
|  | } | 
|  | break; | 
|  | case HS_WAITDATA: | 
|  | ctx->wait = ctx->tick_bits + 200;  // HACK | 
|  | ctx->hostSt = HS_SENDACK; | 
|  | break; | 
|  | case HS_SENDACK: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 1; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_ACK; | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf("[usbdpi] readDescriptor done\n"); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Get Baud | 
|  | void readBaud(struct usbdpi_ctx *ctx) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_SETUP; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0xc2;  // d2h, vendor, endpoint | 
|  | ctx->data[5] = 2;     // get baud | 
|  | ctx->data[6] = 0;     // index 0 | 
|  | ctx->data[7] = 0;     // type device | 
|  | ctx->data[8] = 0;     // wIndex = 0 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0x2;  // wLength = 2 | 
|  | ctx->data[11] = 0; | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | // ctx->data[12] = 0x10; // pre-computed CRC32 | 
|  | // ctx->data[13] = 0xDD; | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | case HS_WAITACK: | 
|  | ctx->wait = ctx->tick_bits + 32;  // HACK | 
|  | ctx->hostSt = HS_REQDATA; | 
|  | break; | 
|  | case HS_REQDATA: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->hostSt = HS_WAITDATA; | 
|  | } | 
|  | break; | 
|  | case HS_WAITDATA: | 
|  | ctx->wait = ctx->tick_bits + 200;  // HACK | 
|  | ctx->hostSt = HS_SENDACK; | 
|  | break; | 
|  | case HS_SENDACK: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 1; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_ACK; | 
|  | ctx->hostSt = HS_EMPTYDATA; | 
|  | } | 
|  | break; | 
|  | case HS_EMPTYDATA: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 6; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_OUT; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA1; | 
|  | ctx->data[4] = 0x0;  // pre-computed CRC32 | 
|  | ctx->data[5] = 0x0; | 
|  | ctx->hostSt = HS_WAITACK2; | 
|  | break; | 
|  | case HS_WAITACK2: | 
|  | ctx->wait = ctx->tick_bits + 32;  // HACK | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf("[usbdpi] readBaud done\n"); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set Baud | 
|  | void setBaud(struct usbdpi_ctx *ctx) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_SETUP; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0x42;  // h2d, vendor, endpoint | 
|  | ctx->data[5] = 3;     // set baud | 
|  | ctx->data[6] = 96;    // index 0 | 
|  | ctx->data[7] = 0;     // type device | 
|  | ctx->data[8] = 0;     // wIndex = 0 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0;  // wLength = 0 | 
|  | ctx->data[11] = 0; | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | // ctx->data[12] = 0x00; // pre-computed CRC32 | 
|  | // ctx->data[13] = 0xBD; | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | case HS_WAITACK: | 
|  | ctx->wait = ctx->tick_bits + 32;  // HACK | 
|  | ctx->hostSt = HS_REQDATA; | 
|  | break; | 
|  | case HS_REQDATA: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->hostSt = HS_WAITDATA; | 
|  | } | 
|  | break; | 
|  | case HS_WAITDATA: | 
|  | ctx->wait = ctx->tick_bits + 40;  // HACK | 
|  | ctx->hostSt = HS_SENDACK; | 
|  | break; | 
|  | case HS_SENDACK: | 
|  | if (ctx->tick_bits >= ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 1; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_ACK; | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | ctx->baudrate_set_successfully = 1; | 
|  | printf("[usbdpi] setBaud done\n"); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test the ischronous transfers (without ACKs) | 
|  | void testIso(struct usbdpi_ctx *ctx) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_OUT; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 1 | CRC5(0x182, 11) << 3; | 
|  |  | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0x42;  // h2d, vendor, endpoint | 
|  | ctx->data[5] = 3;     // set baud | 
|  | ctx->data[6] = 96;    // index 0 | 
|  | ctx->data[7] = 0;     // type device | 
|  | ctx->data[8] = 0;     // wIndex = 0 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0;  // wLength = 0 | 
|  | ctx->data[11] = 0; | 
|  |  | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | // ctx->data[12] = 0x00; // pre-computed CRC32 | 
|  | // ctx->data[13] = 0xBD; | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | case HS_WAITACK:  // no actual ACK | 
|  | ctx->wait = ctx->tick_bits + 32; | 
|  | ctx->hostSt = HS_REQDATA; | 
|  | break; | 
|  | case HS_REQDATA: | 
|  | if (ctx->tick_bits == ctx->wait) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 1 | CRC5(0x0182, 11) << 3; | 
|  | ctx->hostSt = HS_WAITDATA; | 
|  | } | 
|  | break; | 
|  | case HS_WAITDATA: | 
|  | ctx->wait = ctx->tick_bits + 40;  // HACK | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf("[usbdpi] testIso done\n"); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Request IN. Get back NAK or DATA0/DATA1. | 
|  | // sendHi -> also send OUT packet | 
|  | // nakData -> send NAK instead of ACK if there is data | 
|  | void pollRX(struct usbdpi_ctx *ctx, int sendHi, int nakData) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_IN; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0x00 | CRC5(0x0082, 11) << 3; | 
|  | ctx->hostSt = HS_WAIT_PKT; | 
|  | ctx->lastrxpid = 0; | 
|  | break; | 
|  | case HS_WAIT_PKT: | 
|  | if ((ctx->lastrxpid) && (ctx->lastrxpid != USB_PID_IN)) { | 
|  | ctx->wait = ctx->tick_bits + 32;  // Expect to be busy then | 
|  | ctx->hostSt = HS_ACKIFDATA; | 
|  | } | 
|  | break; | 
|  | case HS_ACKIFDATA: | 
|  | if (ctx->tick_bits >= ctx->wait) { | 
|  | if (ctx->lastrxpid != USB_PID_NAK) { | 
|  | // device sent data so ACK it | 
|  | // TODO check DATA0 vs DATA1 | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 1; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = nakData ? USB_PID_NAK : USB_PID_ACK; | 
|  | } | 
|  | if (sendHi) { | 
|  | ctx->hostSt = HS_SENDHI; | 
|  | } else { | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | ctx->inframe = ctx->frame; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case HS_SENDHI: | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 9; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_OUT; | 
|  | ctx->data[1] = 0x82; | 
|  | ctx->data[2] = 0 | CRC5(0x82, 11) << 3; | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0x48;  // "H" | 
|  | ctx->data[5] = 0x69;  // "i" | 
|  | ctx->data[6] = 0x21;  // "!" | 
|  | add_crc16(ctx->data, ctx->datastart, 7); | 
|  | // ctx->data[7] = 0xE0; // pre-computed CRC16 | 
|  | // ctx->data[8] = 0x61; | 
|  | ctx->inframe = ctx->frame; | 
|  | ctx->hostSt = HS_NEXTFRAME;  // Device will ACK | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test unimplemented endpoints | 
|  | void testUnimplEp(struct usbdpi_ctx *ctx, uint8_t pid) { | 
|  | switch (ctx->hostSt) { | 
|  | case HS_STARTFRAME: | 
|  | if ((pid == USB_PID_SETUP) || (pid == USB_PID_OUT)) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 14; | 
|  | ctx->datastart = 3; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | // The bytes are transmitted LSB to MSB | 
|  | ctx->data[0] = pid; | 
|  | ctx->data[1] = | 
|  | ((UNIMPL_EP_ID & 0x1) << 7) | 0x2;  // endpoint ID LSB, address | 
|  | ctx->data[2] = CRC5((UNIMPL_EP_ID << 7) | 0x2, 11) << 3 | | 
|  | ((UNIMPL_EP_ID >> 1) & 0x7);  // CRC, endpoint ID MSBs | 
|  |  | 
|  | ctx->data[3] = USB_PID_DATA0; | 
|  | ctx->data[4] = 0;  // h2d, std, device | 
|  | ctx->data[5] = 5;  // set address | 
|  | ctx->data[6] = 2;  // device address | 
|  | ctx->data[7] = 0; | 
|  | // Trigger bitstuffing, technically the device | 
|  | // behaviour is unspecified with wIndex != 0 | 
|  | ctx->data[8] = 0xFF;  // wIndex = 0xFF00 | 
|  | ctx->data[9] = 0; | 
|  | ctx->data[10] = 0;  // wLength = 0 | 
|  | ctx->data[11] = 0; | 
|  | add_crc16(ctx->data, ctx->datastart, 12); | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | } else if (pid == USB_PID_IN) { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = 0; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | // The bytes are transmitted LSB to MSB | 
|  | ctx->data[0] = pid; | 
|  | ctx->data[1] = | 
|  | ((UNIMPL_EP_ID & 0x1) << 7) | 0x2;  // endpoint ID LSB, address | 
|  | ctx->data[2] = CRC5((UNIMPL_EP_ID << 7) | 0x2, 11) << 3 | | 
|  | ((UNIMPL_EP_ID >> 1) & 0x7);  // CRC, endpoint ID MSBs | 
|  | // Since the endpoint is not implemented, the device will respond with a | 
|  | // STALL packet (not DATA0/1 or NAK). | 
|  | ctx->hostSt = HS_WAITACK; | 
|  | break; | 
|  | } else { | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf( | 
|  | "[usbdpi] testUnimplEp supports SETUP, OUT and IN transactions " | 
|  | "only\n"); | 
|  | break; | 
|  | } | 
|  | case HS_WAITACK: | 
|  | // Note: We currently can't observe the responses sent by the device, but | 
|  | // monitor_usb() does log all transactions from host and device and does | 
|  | // some basic decoding. | 
|  | // Depending on the transaction type to unimplemented endpoints, we would | 
|  | // expect the following response: | 
|  | // - SETUP: no response (must be ignored by the device) | 
|  | // - OUT/IN: a STALL packet from the device | 
|  | ctx->wait = ctx->tick_bits + 32;  // HACK | 
|  | ctx->hostSt = HS_NEXTFRAME; | 
|  | printf("[usbdpi] testUnimplEp done\n"); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int set_driving(struct usbdpi_ctx *ctx, int d2p, int newval) { | 
|  | if (d2p & D2P_DNPU) { | 
|  | if (d2p & D2P_TXMODE_SE) { | 
|  | return (ctx->driving & P2D_SENSE) | ((newval & P2D_DP) ? P2D_DN : 0) | | 
|  | ((newval & P2D_DN) ? P2D_DP : 0); | 
|  | } | 
|  | if (newval & (P2D_DP | P2D_DN)) { | 
|  | // sets single ended lines to K after swapping | 
|  | return (ctx->driving & P2D_SENSE) | P2D_DP | | 
|  | ((newval & P2D_DN) ? P2D_D : 0); | 
|  | } | 
|  | // SE0 so D could be anything (make it 1 after swapping) | 
|  | return ctx->driving & P2D_SENSE; | 
|  | } | 
|  | if (d2p & D2P_TXMODE_SE) { | 
|  | return (ctx->driving & P2D_SENSE) | newval; | 
|  | } | 
|  | if (newval & (P2D_DP | P2D_DN)) { | 
|  | // sets single ended lines to K | 
|  | return (ctx->driving & P2D_SENSE) | P2D_DN | | 
|  | ((newval & P2D_DP) ? P2D_D : 0); | 
|  | } | 
|  | // SE0 so D could be anything (make it 1) | 
|  | return (ctx->driving & P2D_SENSE) | P2D_D; | 
|  | } | 
|  |  | 
|  | int inv_driving(struct usbdpi_ctx *ctx, int d2p) { | 
|  | // works for either orientation | 
|  | return ctx->driving ^ ((d2p & D2P_TXMODE_SE) ? (P2D_DP | P2D_DN) : P2D_D); | 
|  | } | 
|  |  | 
|  | char usbdpi_host_to_device(void *ctx_void, const svBitVecVal *usb_d2p) { | 
|  | struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; | 
|  | assert(ctx); | 
|  | int d2p = usb_d2p[0]; | 
|  | uint32_t last_driving = ctx->driving; | 
|  | int force_stat = 0; | 
|  | int dat; | 
|  |  | 
|  | if (ctx->tick == 0) { | 
|  | int i; | 
|  | #ifdef NEED_SLEEP | 
|  | for (i = 7; i > 0; i--) { | 
|  | printf("Sleep %d...\n", i); | 
|  | sleep(1); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | ctx->tick++; | 
|  | ctx->tick_bits = ctx->tick >> 2; | 
|  | if (ctx->tick & 3) { | 
|  | return ctx->driving; | 
|  | } | 
|  |  | 
|  | monitor_usb(ctx->mon, ctx->mon_file, ctx->loglevel, ctx->tick, | 
|  | (ctx->state != ST_IDLE) && (ctx->state != ST_GET), ctx->driving, | 
|  | d2p, &(ctx->lastrxpid)); | 
|  |  | 
|  | if (ctx->tick_bits == SENSE_AT) { | 
|  | ctx->driving |= P2D_SENSE; | 
|  | } | 
|  |  | 
|  | if ((d2p & D2P_PU) == 0) { | 
|  | ctx->recovery_time = ctx->tick + 4 * 48; | 
|  | return ctx->driving; | 
|  | } | 
|  |  | 
|  | if (ctx->tick < ctx->recovery_time) { | 
|  | ctx->lastframe = ctx->tick_bits; | 
|  | return ctx->driving; | 
|  | } | 
|  |  | 
|  | if ((ctx->tick_bits - ctx->lastframe) >= FRAME_INTERVAL) { | 
|  | if (ctx->state != ST_IDLE) { | 
|  | if (ctx->framepend == 0) { | 
|  | printf("USB: %4x %8d error state %d at frame %d time\n", ctx->frame, | 
|  | ctx->tick, ctx->state, ctx->frame + 1); | 
|  | } | 
|  | ctx->framepend = 1; | 
|  | } else { | 
|  | if (ctx->framepend == 1) { | 
|  | printf("USB: %4x %8d can send frame %d SOF\n", ctx->frame, ctx->tick, | 
|  | ctx->frame + 1); | 
|  | } | 
|  | ctx->framepend = 0; | 
|  | ctx->frame++; | 
|  | ctx->lastframe = ctx->tick_bits; | 
|  |  | 
|  | if (ctx->frame >= 20 && ctx->frame < 30) { | 
|  | // Test suspend | 
|  | ctx->state = ST_IDLE; | 
|  | printf("Idle frame %d\n", ctx->frame); | 
|  | } else { | 
|  | ctx->state = ST_SYNC; | 
|  | ctx->bytes = 3; | 
|  | ctx->datastart = -1; | 
|  | ctx->byte = 0; | 
|  | ctx->bit = 1; | 
|  | ctx->data[0] = USB_PID_SOF; | 
|  | ctx->data[1] = ctx->frame & 0xff; | 
|  | ctx->data[2] = | 
|  | ((ctx->frame & 0x700) >> 8) | (CRC5(ctx->frame & 0x7ff, 11) << 3); | 
|  | } | 
|  | printf("USB: %8d frame 0x%x CRC5 0x%x\n", ctx->tick, ctx->frame, | 
|  | CRC5(ctx->frame, 11)); | 
|  | if (ctx->hostSt == HS_NEXTFRAME) { | 
|  | ctx->hostSt = HS_STARTFRAME; | 
|  | } | 
|  | } | 
|  | } | 
|  | switch (ctx->state) { | 
|  | case ST_IDLE: | 
|  | switch (ctx->frame) { | 
|  | case 1: | 
|  | setDeviceAddress(ctx); | 
|  | break; | 
|  | case 2: | 
|  | readDescriptor(ctx); | 
|  | break; | 
|  | // These should be at 3 and 4 but the read needs the host | 
|  | // not to be sending (until skip fifo is implemented in in_pe engine) | 
|  | // so for now push later when things are quiet (could also adjust | 
|  | // hello_world to not use the uart until frame 4) | 
|  | case 10: | 
|  | readBaud(ctx); | 
|  | break; | 
|  | case 15: | 
|  | setBaud(ctx); | 
|  | break; | 
|  | case 17: | 
|  | testIso(ctx); | 
|  | break; | 
|  | case 18: | 
|  | testIso(ctx); | 
|  | break; | 
|  | case 20: | 
|  | testUnimplEp(ctx, USB_PID_SETUP); | 
|  | break; | 
|  | case 21: | 
|  | testUnimplEp(ctx, USB_PID_OUT); | 
|  | break; | 
|  | case 22: | 
|  | testUnimplEp(ctx, USB_PID_IN); | 
|  | break; | 
|  | default: | 
|  | if (ctx->frame > ctx->inframe && | 
|  | !(ctx->frame >= 23 && ctx->frame < 33)) { | 
|  | pollRX(ctx, (ctx->frame == 9) || (ctx->frame == 14), | 
|  | ((ctx->frame == 16) || (ctx->frame == 9))); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ST_SYNC: | 
|  | dat = ((USB_SYNC & ctx->bit)) ? P2D_DP : P2D_DN; | 
|  | ctx->driving = set_driving(ctx, d2p, dat); | 
|  | force_stat = 1; | 
|  | ctx->bit <<= 1; | 
|  | if (ctx->bit == 0x100) { | 
|  | ctx->bit = 1; | 
|  | ctx->linebits = 0; | 
|  | ctx->state = ST_SEND; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ST_SEND: | 
|  | if ((ctx->linebits & 0x3f) == 0x3f && | 
|  | !INSERT_ERR_BITSTUFF) {  // sent 6 ones | 
|  | // bit stuff and force a transition | 
|  | ctx->driving = inv_driving(ctx, d2p); | 
|  | force_stat = 1; | 
|  | ctx->linebits = (ctx->linebits << 1); | 
|  | } else if (ctx->byte >= ctx->bytes) { | 
|  | ctx->state = ST_EOP; | 
|  | ctx->driving = set_driving(ctx, d2p, 0);  // SE0 | 
|  | ctx->bit = 1; | 
|  | force_stat = 1; | 
|  | } else { | 
|  | int nextbit; | 
|  | nextbit = (ctx->data[ctx->byte] & ctx->bit) ? 1 : 0; | 
|  | if (nextbit == 0) { | 
|  | ctx->driving = inv_driving(ctx, d2p); | 
|  | } | 
|  | ctx->linebits = (ctx->linebits << 1) | nextbit; | 
|  | force_stat = 1; | 
|  | ctx->bit <<= 1; | 
|  | if (ctx->bit == 0x100) { | 
|  | ctx->bit = 1; | 
|  | ctx->byte++; | 
|  | if (ctx->byte == ctx->datastart) { | 
|  | ctx->state = ST_EOP0; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ST_EOP0: | 
|  | ctx->driving = set_driving(ctx, d2p, 0);  // SE0 | 
|  | ctx->state = ST_EOP; | 
|  | break; | 
|  |  | 
|  | case ST_EOP:  // SE0 SE0 J | 
|  | if (ctx->bit == 4) { | 
|  | ctx->driving = set_driving(ctx, d2p, P2D_DP);  // J | 
|  | } | 
|  | if (ctx->bit == 8) { | 
|  | // Stop driving: host pulldown to SE0 unless there is a pullup on DP | 
|  | ctx->driving = set_driving(ctx, d2p, (d2p & D2P_PU) ? P2D_DP : 0); | 
|  | if (ctx->byte == ctx->datastart) { | 
|  | ctx->bit = 1; | 
|  | ctx->state = ST_SYNC; | 
|  | } else { | 
|  | ctx->state = ST_IDLE; | 
|  | } | 
|  | } | 
|  | ctx->bit <<= 1; | 
|  | break; | 
|  | } | 
|  | if ((ctx->loglevel & LOG_BIT) && | 
|  | (force_stat || (ctx->driving != last_driving))) { | 
|  | int n; | 
|  | char obuf[MAX_OBUF]; | 
|  |  | 
|  | n = snprintf( | 
|  | obuf, MAX_OBUF, "%4x %8d              %s %s %s\n", ctx->frame, | 
|  | ctx->tick, ctx->driving & P2D_SENSE ? "VBUS" : "    ", | 
|  | (ctx->state != ST_IDLE) ? decode_usb[(ctx->driving >> 1) & 3] : "ZZ ", | 
|  | (ctx->driving & P2D_D) ? "1" : "0"); | 
|  | ssize_t written = fwrite(obuf, sizeof(char), (size_t)n, ctx->mon_file); | 
|  | assert(written == n); | 
|  | } | 
|  | return ctx->driving; | 
|  | } | 
|  |  | 
|  | void usbdpi_close(void *ctx_void) { | 
|  | struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; | 
|  | if (!ctx) { | 
|  | return; | 
|  | } | 
|  | fclose(ctx->mon_file); | 
|  | free(ctx); | 
|  | } |