| // 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> |
| |
| #include "verilator_sim_ctrl.h" |
| |
| 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"}; |
| |
| extern VerilatorSimCtrl *simctrl; |
| |
| static void finish(void) { |
| if (!simctrl) { |
| return; |
| } |
| simctrl->RequestStop(); |
| } |
| |
| void *usbdpi_create(const char *name, int loglevel) { |
| int i; |
| 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->inframe = 4; |
| ctx->state = ST_IDLE; |
| ctx->driving = 0; |
| ctx->hostSt = HS_NEXTFRAME; |
| ctx->loglevel = loglevel; |
| ctx->mon = monitor_usb_init(); |
| |
| char cwd[PATH_MAX]; |
| char *cwd_rv; |
| cwd_rv = getcwd(cwd, sizeof(cwd)); |
| assert(cwd_rv != NULL); |
| |
| int rv; |
| rv = snprintf(ctx->fifo_pathname, PATH_MAX, "%s/%s", cwd, name); |
| assert(rv <= PATH_MAX && rv > 0); |
| |
| rv = mkfifo(ctx->fifo_pathname, 0644); // writes are not supported currently |
| if (rv != 0) { |
| fprintf(stderr, "USB: Unable to create FIFO at %s: %s\n", |
| ctx->fifo_pathname, strerror(errno)); |
| return NULL; |
| } |
| |
| ctx->fifo_fd = open(ctx->fifo_pathname, O_RDWR); |
| if (ctx->fifo_fd < 0) { |
| fprintf(stderr, "USB: Unable to open FIFO at %s: %s\n", ctx->fifo_pathname, |
| strerror(errno)); |
| return NULL; |
| } |
| |
| printf( |
| "\n" |
| "USB: FIFO pipe created at %s. Run\n" |
| "$ cat %s\n" |
| "to observe the output.\n", |
| ctx->fifo_pathname, ctx->fifo_pathname); |
| return (void *)ctx; |
| } |
| |
| const char *decode_usb[] = {"SE0", "0-K", "1-J", "SE1"}; |
| |
| void usbdpi_device_to_host(void *ctx_void, svBitVecVal *d2p_data) { |
| struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; |
| assert(ctx); |
| int d2p = d2p_data[0]; |
| int dp, dn; |
| int n; |
| char obuf[MAX_OBUF]; |
| |
| char raw_str[D2P_BITS + 1]; |
| for (int i = 0; i < 5; i++) { |
| raw_str[5 - i - 1] = !!(d2p & (1 << i)) + '0'; |
| } |
| raw_str[D2P_BITS] = 0; |
| |
| if (d2p & (D2P_DP_EN | D2P_DN_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; |
| } |
| } |
| |
| dp = (((d2p & D2P_DP_EN) && (d2p & D2P_DP)) || |
| (!(d2p & D2P_DP_EN) && (d2p & D2P_PU))) |
| ? 1 |
| : 0; |
| |
| dn = ((d2p & D2P_DN_EN) && (d2p & D2P_DN)) ? 1 : 0; |
| |
| if (ctx->loglevel & LOG_BIT) { |
| n = snprintf(obuf, MAX_OBUF, "%4x %8d %s %s %s\n", ctx->frame, ctx->tick, |
| raw_str, (d2p & D2P_PU) ? "PU" : " ", |
| (ctx->state == ST_GET) ? decode_usb[dp << 1 | dn] : "ZZ "); |
| ssize_t written = write(ctx->fifo_fd, obuf, n); |
| 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; |
| ctx->data[4] = 0; // h2d, std, device |
| ctx->data[5] = 5; // set address |
| ctx->data[6] = 2; // device address |
| ctx->data[7] = 0; |
| 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] = 0xEB; // pre-computed CRC16 |
| // ctx->data[13] = 0x16; |
| 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 + 48; // 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; |
| } |
| 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; |
| } |
| 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; |
| 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; |
| } |
| break; |
| 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; |
| ctx->data[5] = 0x69; |
| 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; |
| } |
| } |
| |
| char usbdpi_host_to_device(void *ctx_void, svBitVecVal *d2p_data) { |
| struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; |
| assert(ctx); |
| int d2p = d2p_data[0]; |
| uint32_t last_driving = ctx->driving; |
| int force_stat = 0; |
| int dat; |
| |
| if (ctx->tick == 0) { |
| for (int i = 7; i > 0; i--) { |
| printf("Sleep %d...\n", i); |
| sleep(1); |
| } |
| } |
| ctx->tick++; |
| ctx->tick_bits = ctx->tick >> 2; |
| if (ctx->tick & 3) { |
| return ctx->driving; |
| } |
| |
| monitor_usb(ctx->mon, ctx->fifo_fd, 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; |
| 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: |
| if ((simctrl && simctrl->TracingEnabled() && (ctx->frame == 20)) || |
| (ctx->frame > 2000)) |
| finish(); |
| |
| 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; |
| |
| default: |
| if (ctx->frame > ctx->inframe) { |
| 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 = (ctx->driving & P2D_SENSE) | 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) { // sent 6 ones |
| // bit stuff and force a transition |
| ctx->driving ^= (P2D_DP | P2D_DN); |
| force_stat = 1; |
| ctx->linebits = (ctx->linebits << 1); |
| } else if (ctx->byte >= ctx->bytes) { |
| ctx->state = ST_EOP; |
| ctx->driving = ctx->driving & P2D_SENSE; // SE0 |
| ctx->bit = 1; |
| force_stat = 1; |
| } else { |
| int nextbit; |
| nextbit = (ctx->data[ctx->byte] & ctx->bit) ? 1 : 0; |
| if (nextbit == 0) { |
| ctx->driving ^= (P2D_DP | P2D_DN); |
| } |
| 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 = ctx->driving & P2D_SENSE; // SE0 |
| ctx->state = ST_EOP; |
| break; |
| |
| case ST_EOP: // SE0 SE0 J |
| if (ctx->bit == 4) { |
| ctx->driving = (ctx->driving & P2D_SENSE) | P2D_DP; // J |
| } |
| if (ctx->bit == 8) { |
| ctx->driving = (d2p & D2P_PU) ? (ctx->driving & P2D_SENSE) | P2D_DP |
| : // Z + pullup |
| ctx->driving & P2D_SENSE; // z without pullup = SE0 |
| 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\n", ctx->frame, ctx->tick, |
| ctx->driving & P2D_SENSE ? "VBUS" : " ", |
| (ctx->state != ST_IDLE) ? decode_usb[(ctx->driving >> 1) & 3] : "ZZ "); |
| ssize_t written = write(ctx->fifo_fd, obuf, n); |
| assert(written == n); |
| } |
| return ctx->driving; |
| } |
| |
| void usbdpi_close(void *ctx_void) { |
| struct usbdpi_ctx *ctx = (struct usbdpi_ctx *)ctx_void; |
| if (!ctx) { |
| return; |
| } |
| int rv; |
| rv = close(ctx->fifo_fd); |
| if (rv != 0) { |
| printf("USB: Failed to close FIFO: %s\n", strerror(errno)); |
| } |
| rv = unlink(ctx->fifo_pathname); |
| if (rv != 0) { |
| printf("USB: Failed to unlink FIFO file at %s: %s\n", ctx->fifo_pathname, |
| strerror(errno)); |
| } |
| free(ctx); |
| } |