/** @brief USB Protocol analysis for Lauterbach TRACE32 Integrator with USB-Probe MEII. * * For an extensive documentation of how to construct protocol * analyzer DLLs, see the T32 documentation protocol_app.pdf * * indent -kr -l120 protousb2.c */ #include "proto.h" typedef unsigned char uint08_t; /* --------------------------------------------------- USB2PROTO_FILE_TRACE_DEBUG */ /* #define USB2PROTO_FILE_TRACE_DEBUG */ #ifdef USB2PROTO_FILE_TRACE_DEBUG #include #define FILE_TRACE_NAME "protousb2" #define FILE_TRACE(XXXX) log_print XXXX #define FILE_TRACE_DUMP(XXXX) log_dump XXXX void log_print(char *s, ...) { FILE *fd; char buf[256]; va_list varargs; va_start(varargs, s); vsprintf(buf, s, varargs); fd = fopen(FILE_TRACE_NAME ".log", "a"); fprintf(fd, "%s\n", buf); fclose(fd); } #define FILE_TRACE_DUMP_LINE_AMOUNT 16 void log_dump(char *name, uint08_t * buf, int len) { if (len) { FILE *fd; int i, j; char ascbuf[FILE_TRACE_DUMP_LINE_AMOUNT + 1]; j = 0; ascbuf[FILE_TRACE_DUMP_LINE_AMOUNT] = '\0'; fd = fopen(FILE_TRACE_NAME ".log", "a"); fprintf(fd, "%s: dumping %s\n", FILE_TRACE_NAME, name); while (j < len) { fprintf(fd, "%04x :", j); for (i = 0; i < FILE_TRACE_DUMP_LINE_AMOUNT; i++) { if (j < len) { fprintf(fd, " %02x", buf[j]); ascbuf[i] = (buf[j] < 32) ? '.' : (char) buf[j]; } else { fprintf(fd, " "); ascbuf[i] = ' '; } j++; } fprintf(fd, " %s\n", ascbuf); } fclose(fd); } } #else #define FILE_TRACE(XXXX) #define FILE_TRACE_DUMP(XXXX) #endif /* --------------------------------------------------- USB2PROTO_FILE_TRACE_DEBUG */ typedef struct stageOneDataS { protoTime time; uint08_t flags; uint08_t validh; uint16_t data; } stageOneData; #define ValIndexMax 8 typedef struct stageTwoDataS { protoTime time; uint08_t flags; uint08_t token; uint08_t val[ValIndexMax]; /* up to eight data values are displayed per line */ uint08_t valindex; /* maximum display index + 1 = range 0..8 */ uint16_t valcount; /* this can go up to record size == 512 */ uint16_t vallength; uint16_t crc16_calc; uint16_t crc16; } stageTwoData; #define DataIndexMax 16 #define RespIndexMax 512 typedef struct stageThreeDataS { protoTime time; uint08_t dataflags; uint08_t datatype; uint08_t datalen; uint08_t data[DataIndexMax]; uint16_t resplen; uint08_t resp[RespIndexMax]; } stageThreeData; /* Flags */ #define Fnoflag 0 #define Fpidstart 1 #define Fpidend 2 #define Fdata 4 /* PID codes */ typedef enum { USB2_RESVD = 0xF0, USB2_USBOUT = 0xE1, USB2_ACK = 0xD2, USB2_DATA0 = 0xC3, USB2_PING = 0xB4, USB2_SOF = 0xA5, USB2_NYET = 0x96, USB2_DATA2 = 0x87, USB2_SPLIT = 0x78, USB2_USBIN = 0x69, USB2_NAK = 0x5A, USB2_DATA1 = 0x4B, USB2_ERR = 0x3C, USB2_SETUP = 0x2D, USB2_STALL = 0x1E, USB2_MDATA = 0x0F } PID_CODES; /* Stage Three flags */ #define Frequest 8 #define Fresponse 16 /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ uint16_t const crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; /** * Return new CRC value for given old value and new data byte. * * @param crc old CRC value * @param data input data byte * * @returns new CRC value */ static uint16_t crc16_byte(uint16_t crc, const uint08_t data) { return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; } /** * Stage One: Generate bytes from of raw trace data. * * @param context communication structure needed to call other Trace32 functions * @param arrayOut pointer to output array * @param arrayOutSize number of allocated entries in output array * @param arrayIn pointer to input array * @param arrayInSize number of valid entries in input array * @param localdata pointer to allocated local data block * * @returns number of last valid data record in arrayOut * or PROTO_PROCESS_CANCEL if aborted by user * or PROTO_PROCESS_OUTOFMEMORY if outArray too small */ static protoSizeT PROTOAPI process1(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { /* Strobe lines */ # define PID TBits[0] # define RXACT TBits[1] # define RXERR TBits[2] # define RXVAL TBits[3] # define LSTATE0 TBits[4] # define LSTATE1 TBits[5] # define VALIDH TBits[6] # define TXRDY TBits[7] /* Data lines */ # define Doff0 8 // Offset of DATA-Bus entry # define Doff1 Doff0+8 # define DATB0 ((TBits[Doff0+7]<<7)|(TBits[Doff0+6]<<6)|(TBits[Doff0+5]<<5)|(TBits[Doff0+4]<<4)|(TBits[Doff0+3]<<3)|(TBits[Doff0+2]<<2)|(TBits[Doff0+1]<<1)|TBits[Doff0]) # define DATB1 ((TBits[Doff1+7]<<7)|(TBits[Doff1+6]<<6)|(TBits[Doff1+5]<<5)|(TBits[Doff1+4]<<4)|(TBits[Doff1+3]<<3)|(TBits[Doff1+2]<<2)|(TBits[Doff1+1]<<1)|TBits[Doff1]) # define DATA16 (((DATB1<<8)|DATB0)&0xFFFF) stageOneData *dataOut; /* Pointer to array which will contain the output records */ protoSizeT indexin; /* index for input data records */ protoSizeT indexout; /* index for output data records */ protoTime traceTime; /* timestamp of raw trace data record */ int TBits[24]; /* processed USB signal bit values */ int RXACT_Last; uint08_t flgUSBdata; dataOut = (stageOneData *) arrayOut; /* type cast to the used output record type */ indexin = 0; indexout = 0; RXACT_Last = 1; flgUSBdata = 0; for (indexin = 0; indexin < arrayInSize; indexin++) { PROTO_ReadTrace(context, indexin, &traceTime, TBits); if (indexout >= arrayOutSize) /* If there is no free space to store more */ return PROTO_PROCESS_OUTOFMEMORY; /* records, the processing is aborted */ if ((indexin & 0xfff) == 0) /* give GUI a chance to stop us */ if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; if ((RXACT_Last == 1) && (RXACT == 0)) { dataOut[indexout].flags = Fpidend; dataOut[indexout].time = traceTime; dataOut[indexout].data = (uint16_t) DATA16; dataOut[indexout].validh = (uint08_t) VALIDH; indexout++; } if (RXVAL == 1) { if (PID) { dataOut[indexout].flags = Fpidstart; switch (DATA16 & 0xff) { case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: flgUSBdata = 1; break; default: flgUSBdata = 0; break; } } else { if (flgUSBdata == 1) dataOut[indexout].flags = Fdata; else dataOut[indexout].flags = 0; } dataOut[indexout].time = traceTime; dataOut[indexout].data = (uint16_t) DATA16; dataOut[indexout].validh = (uint08_t) VALIDH; indexout++; } RXACT_Last = RXACT; } return indexout; } /** * Stage One: Display word/byte raw data. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to display data * @param localdata pointer to allocated local data block */ static void PROTOAPI display1(protoContext context, protoPtr pdata, protoPtr localdata) { stageOneData *printData; printData = (stageOneData *) pdata; if ((printData->flags) != Fpidend) { if (printData->validh) PROTO_Printf(context, " %04X", (uint32_t) printData->data); else PROTO_Printf(context, " %02X", (uint32_t) printData->data & 0xff); } } /** * Stage Two: Generate aggregated information from Stage One data. * * Generates data displayed at layer 2 in the ListProtocol window. * In this case, it just computes the CRC checksums. * * @param context communication structure needed to call other Trace32 functions * @param arrayOut pointer to output array * @param arrayOutSize number of allocated entries in output array * @param arrayIn pointer to input array * @param arrayInSize number of valid entries in input array * @param localdata pointer to allocated local data block * * @returns number of last valid data record in arrayOut */ static protoSizeT PROTOAPI process2(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { stageOneData *dataInp; stageTwoData *dataOut; protoSizeT indexin, indexout, FirstValRec, ValRec; uint08_t ValIndex, USBdata; uint16_t ValCount; uint16_t crc16_calc, crc16_calc_history[3]; dataInp = (stageOneData *) arrayIn; dataOut = (stageTwoData *) arrayOut; crc16_calc = 0xffffu; ValCount = 0; ValIndex = ValIndexMax; FirstValRec = 0; ValRec = 0; indexout = 0; USBdata = 0; for (indexin = 0; indexin < arrayInSize; indexin++) { if ((indexin & 0xfff) == 0) /* every 4096 times, give GUI a chance to stop us */ if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; if (indexout >= arrayOutSize) /* If there is no free space to store more */ return PROTO_PROCESS_OUTOFMEMORY; /* records, the processing is aborted */ if ((dataInp[indexin].flags) == Fpidstart) { crc16_calc = 0xffffu; /* CRC16 start value for USB */ crc16_calc_history[0] = crc16_calc; crc16_calc_history[1] = crc16_calc; crc16_calc_history[2] = crc16_calc; dataOut[indexout].time = dataInp[indexin].time; dataOut[indexout].flags = dataInp[indexin].flags; dataOut[indexout].token = dataInp[indexin].data & 0xff; ValIndex = 0; ValCount = 0; ValRec = indexout; FirstValRec = indexout; dataOut[ValRec].valcount = ValCount; switch (dataInp[indexin].data & 0xff) { case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: USBdata = 1; break; default: USBdata = 0; break; } if (dataInp[indexin].validh) { dataOut[ValRec].valcount = ValCount; dataOut[ValRec].val[ValIndex] = (uint08_t) ((dataInp[indexin].data >> 8) & 0xff); dataOut[ValRec].valindex = ValIndex; dataOut[FirstValRec].vallength = ValCount + 1; crc16_calc = crc16_byte(crc16_calc, (dataInp[indexin].data >> 8) & 0xff); crc16_calc_history[ValCount % 3] = crc16_calc; dataOut[FirstValRec].crc16_calc = 0xffffu ^ crc16_calc_history[(ValCount + 1) % 3]; dataOut[FirstValRec].crc16 >>= 8; dataOut[FirstValRec].crc16 |= dataInp[indexin].data & 0xff00; ValIndex++; ValCount++; } indexout++; } else { if ((ValIndex < ValIndexMax) && (dataInp[indexin].flags != Fpidend)) { dataOut[ValRec].val[ValIndex] = (uint08_t) (dataInp[indexin].data & 0xff); dataOut[ValRec].valindex = ValIndex; dataOut[FirstValRec].vallength = ValCount + 1; crc16_calc = crc16_byte(crc16_calc, dataInp[indexin].data & 0xff); crc16_calc_history[ValCount % 3] = crc16_calc; dataOut[FirstValRec].crc16_calc = 0xffffu ^ crc16_calc_history[(ValCount + 1) % 3]; dataOut[FirstValRec].crc16 >>= 8; dataOut[FirstValRec].crc16 |= (dataInp[indexin].data << 8) & 0xff00; ValIndex++; ValCount++; if (dataInp[indexin].validh) { if (ValIndex < ValIndexMax) { dataOut[ValRec].val[ValIndex] = (uint08_t) ((dataInp[indexin].data >> 8) & 0xff); dataOut[ValRec].valindex = ValIndex; dataOut[FirstValRec].vallength = ValCount + 1; crc16_calc = crc16_byte(crc16_calc, (dataInp[indexin].data >> 8) & 0xff); crc16_calc_history[ValCount % 3] = crc16_calc; dataOut[FirstValRec].crc16_calc = 0xffffu ^ crc16_calc_history[(ValCount + 1) % 3]; dataOut[FirstValRec].crc16 >>= 8; dataOut[FirstValRec].crc16 |= dataInp[indexin].data & 0xff00; ValIndex++; ValCount++; } else { if (USBdata == 1) { dataOut[indexout].time = dataInp[indexin].time; dataOut[indexout].flags |= Fdata; /* copy over token value from first value record */ dataOut[indexout].token = dataOut[FirstValRec].token; ValIndex = 0; ValRec = indexout; dataOut[ValRec].valcount = ValCount; dataOut[ValRec].val[ValIndex] = (uint08_t) ((dataInp[indexin].data >> 8) & 0xff); dataOut[ValRec].valindex = ValIndex; dataOut[FirstValRec].vallength = ValCount + 1; crc16_calc = crc16_byte(crc16_calc, (dataInp[indexin].data >> 8) & 0xff); crc16_calc_history[ValCount % 3] = crc16_calc; dataOut[FirstValRec].crc16_calc = 0xffffu ^ crc16_calc_history[(ValCount + 1) % 3]; dataOut[FirstValRec].crc16 >>= 8; dataOut[FirstValRec].crc16 |= dataInp[indexin].data & 0xff00; ValIndex++; ValCount++; indexout++; } } } } } } return indexout; } /** * Stage Two: Display address message. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to a record entry generated by process1 */ static void display2_addr(protoContext context, protoPtr pdata) { stageTwoData *printData = (stageTwoData *) pdata; PROTO_Printf(context, " A:%02X EP:%1X CRC5:%02X", printData->val[0] & 0x7f, ((printData->val[0] >> 7) & 0x1) | ((printData->val[1] << 1) & 0xE), (printData->val[1] >> 3) & 0x1F); } /** * Stage Two: Display data message. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to a record entry generated by process1 */ static void display2_data(protoContext context, protoPtr pdata) { protoSizeT i; stageTwoData *printData = (stageTwoData *) pdata; PROTO_Printf(context, "0x%03X ", printData->valcount); for (i = 0; i <= printData->valindex; i++) PROTO_Printf(context, "%02X ", printData->val[i]); } /** * Stage Two Helper: Return Protocol ID name. * * @param pid Protocol ID * * @returns ASCII representation of PID name. */ static const char *getPidName(uint08_t pid) { switch (pid) { case USB2_RESVD: return "RESVD"; break; case USB2_USBOUT: return "OUT"; break; case USB2_ACK: return "ACK"; break; case USB2_DATA0: return "DATA0"; break; case USB2_PING: return "PING"; break; case USB2_SOF: return "SOF"; break; case USB2_NYET: return "NYET"; break; case USB2_DATA2: return "DATA2"; break; case USB2_SPLIT: return "SPLIT"; break; case USB2_USBIN: return "IN"; break; case USB2_NAK: return "NAK"; break; case USB2_DATA1: return "DATA1"; break; case USB2_ERR: return "ERR/PRE"; break; case USB2_SETUP: return "SETUP"; break; case USB2_STALL: return "STALL"; break; case USB2_MDATA: return "MDATA"; break; default: return "idle"; } } /** * Stage Two: Display one entry of layer 2 information in the ListProtocol window. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to a record entry generated by process2 * @param localdata pointer to allocated local data block */ static void PROTOAPI display2(protoContext context, protoPtr pdata, protoPtr localdata) { stageTwoData *printData = (stageTwoData *) pdata; if ((printData->flags) & Fpidstart) { PROTO_Printf(context, "% 5s", getPidName(printData->token)); switch (printData->token) { case USB2_RESVD: break; case USB2_USBOUT: display2_addr(context, printData); break; case USB2_ACK: break; case USB2_DATA0: PROTO_Printf(context, " DATA-Length: 0x%03X CRC16:%04X", printData->vallength - 2, printData->crc16); if (printData->crc16 == printData->crc16_calc) PROTO_Printf(context, " OK"); else PROTO_Printf(context, " FAIL(%04X)", printData->crc16_calc); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " "); display2_data(context, printData); break; case USB2_PING: display2_addr(context, printData); break; case USB2_SOF: PROTO_Printf(context, " Frame: %03X CRC5:%02X ", (printData-> val[0] & 0xff) | ((printData->val[1] << 8) & 0x700), (printData->val[1] >> 3) & 0x1F); PROTO_Control(context, PROTO_CONTROL_LINETILLEND); break; case USB2_NYET: break; case USB2_DATA2: PROTO_Printf(context, " DATA-Length: 0x%03X CRC16:%04X", printData->vallength - 2, printData->crc16); if (printData->crc16 == printData->crc16_calc) PROTO_Printf(context, " OK"); else PROTO_Printf(context, " FAIL(%04X)", printData->crc16_calc); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " "); display2_data(context, printData); break; case USB2_SPLIT: break; case USB2_USBIN: display2_addr(context, printData); break; case USB2_NAK: break; case USB2_DATA1: PROTO_Printf(context, " DATA-Length: 0x%03X CRC16:%04X", printData->vallength - 2, printData->crc16); if (printData->crc16 == printData->crc16_calc) PROTO_Printf(context, " OK"); else PROTO_Printf(context, " FAIL(%04X)", printData->crc16_calc); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " "); display2_data(context, printData); break; case USB2_ERR: break; case USB2_SETUP: display2_addr(context, printData); break; case USB2_STALL: break; case USB2_MDATA: PROTO_Printf(context, " DATA-Length: 0x%03X CRC16:%04X", printData->vallength - 2, printData->crc16); if (printData->crc16 == printData->crc16_calc) PROTO_Printf(context, " OK"); else PROTO_Printf(context, " FAIL(%04X)", printData->crc16_calc); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " "); display2_data(context, printData); break; default: PROTO_Printf(context, "PID: !! PID-ERROR !! %02X", printData->token); break; } } if ((printData->flags) & Fdata) { PROTO_Printf(context, " "); display2_data(context, printData); } } /** * Stage Three: Semantic interpretation (USB descriptors). * * @param context communication structure needed to call other Trace32 functions * @param arrayOut pointer to output array * @param arrayOutSize number of allocated entries in output array * @param arrayIn pointer to input array * @param arrayInSize number of valid entries in input array * @param localdata pointer to allocated local data block * * @returns number of last valid data record in arrayOut */ static protoSizeT PROTOAPI process3(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { stageTwoData *dataInp; stageThreeData *dataOut; protoSizeT indexin, indexout; uint08_t token; dataInp = (stageTwoData *) arrayIn; dataOut = (stageThreeData *) arrayOut; indexout = 0; for (indexin = 0; indexin < arrayInSize; indexin++) { if ((indexin & 0xfff) == 0) if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; if (indexout >= arrayOutSize) return PROTO_PROCESS_OUTOFMEMORY; token = dataInp[indexin].token; dataOut[indexout].time = dataInp[indexin].time; dataOut[indexout].dataflags = 0; dataOut[indexout].datatype = token; dataOut[indexout].datalen = 0; dataOut[indexout].resplen = 0; switch (token) { case USB2_SETUP: /* look for config descriptors and reponses */ { uint08_t endpoint; /* uint08_t address; address = dataInp[indexin].val[0] & 0x7f; -- not used */ endpoint = ((dataInp[indexin].val[0] >> 7) & 0x1) | ((dataInp[indexin].val[1] << 1) & 0xE); if (endpoint == 0) { /* control pipe */ /* we are only interested if there are only data and ACK statements following */ int xflag = 0; int maxidx; maxidx = indexin + 1; while (!xflag && (maxidx < arrayInSize)) { FILE_TRACE((" - loop waitack token 0x%02x", dataInp[maxidx].token)); switch (dataInp[maxidx].token & 0xff) { case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: case USB2_PING: case USB2_SOF: /* ok, no change */ ; break; case USB2_ACK: xflag = 1; break; default: xflag = 2; } maxidx++; } FILE_TRACE((" -> maxidx = %d, xflag = %d", maxidx, xflag)); if (xflag == 1) { /* found SETUP ... DATA ... ACK */ uint16_t v_cnt; /* immediate following record should contain data header and first value chunk */ indexin++; v_cnt = dataInp[indexin].vallength; if (v_cnt < DataIndexMax) { uint08_t v_ofs, i; /* range is 0..16 */ dataOut[indexout].dataflags = Frequest; dataOut[indexout].datalen = (uint08_t) v_cnt; v_ofs = 0; for (i = 0; i < v_cnt; i++) { dataOut[indexout].data[i] = dataInp[indexin].val[v_ofs]; v_ofs++; if (v_ofs >= ValIndexMax) { v_ofs = 0; /* next data record - should follow immediately */ indexin++; } } } else { indexout++; /* at least show the input request */ break; /* else: do nothing - default setup packages are always 8 bytes long */ } /* skip ACK record after DATA, maxidx is one higher than last hit */ indexin = maxidx; /* now get response data block */ xflag = 0; maxidx = indexin; while (!xflag && (maxidx < arrayInSize)) { FILE_TRACE((" - loop waitrespdata token 0x%02x", dataInp[maxidx].token)); switch (dataInp[maxidx].token & 0xff) { case USB2_USBIN: case USB2_NAK: case USB2_SOF: case USB2_PING: /* no change */ ; break; case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: xflag = 1; break; default: xflag = 2; } maxidx++; } FILE_TRACE((" -> maxidx = %d, xflag = %d", maxidx, xflag)); if (xflag == 1) { /* found SETUP ... DATA ... ACK ... DATA */ uint08_t v_ofs, i; /* range is 0..16 */ indexin = maxidx - 1; v_cnt = (uint16_t) dataInp[indexin].vallength; /* strip the CRC value at the end */ v_cnt = (v_cnt >= 2) ? (v_cnt - 2) : 0; FILE_TRACE((" -> resplen = %d (max %d)", v_cnt, RespIndexMax)); if (v_cnt >= RespIndexMax) v_cnt = RespIndexMax; dataOut[indexout].resplen = v_cnt; v_ofs = 0; for (i = 0; i < v_cnt; i++) { dataOut[indexout].resp[i] = dataInp[indexin].val[v_ofs]; v_ofs++; if (v_ofs >= ValIndexMax) { v_ofs = 0; /* next data record - should follow immediately */ indexin++; } } indexout++; /* record is complete now */ } else { indexout++; /* at least show the input request */ break; /* else: do nothing - default setup packages are always 8 bytes long */ } /* if SETUP response data found */ } /* if SETUP request data found */ } } break; case USB2_USBIN: switch (dataInp[indexin + 1].token & 0xff) { case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: indexout++; /* new entry for USBIN followed by DATA */ break; default: /*do nothing */ ; } break; case USB2_USBOUT: switch (dataInp[indexin + 1].token & 0xff) { case USB2_DATA0: case USB2_DATA1: case USB2_DATA2: case USB2_MDATA: indexout++; /* new entry for USBOUT followed by DATA */ break; default: /*do nothing */ ; } break; default: /* ignore unwanted information */ ; } } return indexout; } static uint16_t getWordLE(uint08_t * ptr) { return (ptr[0] | ptr[1] << 8); } static const char *getDescriptorName(uint08_t desctype) { static const char *DescriptorTypeStrings[] = { "UNKNOWN", "DEVICE", "CONFIGURATION", "STRING", "INTERFACE", "ENDPOINT", "DEVICE_QUALIFIER", "OTHER_SPEED_CONFIGURATION", "INTERFACE_POWER" }; if (desctype > 8) desctype = 0; return (DescriptorTypeStrings[desctype]); } static uint08_t *display3_printDescriptor(protoContext context, uint08_t * dc, stageThreeData * printData) { PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " "); if (printData->resplen < 1) { PROTO_Printf(context, " R:EMPTY"); return (dc + 1); } if (printData->resplen < 2) { PROTO_Printf(context, " R:len=%-2d ", dc[0]); return (dc + printData->resplen); } if ((printData->resplen) < dc[0]) { PROTO_Printf(context, " R:len=%-2d type=%s", dc[0], getDescriptorName(dc[1])); return (dc + printData->resplen); } switch (dc[1]) { case 1: PROTO_Printf(context, "DEVICE "); PROTO_Printf(context, " bcdUSB=%04x", getWordLE(&dc[2])); PROTO_Printf(context, " bDeviceClass=0x%02x", dc[4]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bDeviceSubClass=0x%02x", dc[5]); PROTO_Printf(context, " bDeviceProtocol=0x%02x", dc[6]); PROTO_Printf(context, " bMaxPacketSize0=0x%02x", dc[7]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " idVendor=0x%04x", getWordLE(&dc[8])); PROTO_Printf(context, " idProduct=0x%04x", getWordLE(&dc[10])); PROTO_Printf(context, " bcdDevice=%04x", getWordLE(&dc[12])); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " iManufacturer=%d", dc[14]); PROTO_Printf(context, " iProduct=%d", dc[15]); PROTO_Printf(context, " iSerialNumber=%d", dc[16]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bNumConfigurations=%d", dc[17]); break; case 7: PROTO_Printf(context, "OTHER_SPEED_"); /* fallthrough to case 2!! */ case 2: PROTO_Printf(context, "CONFIGURATION "); PROTO_Printf(context, " wTotalLength=%d", getWordLE(&dc[2])); PROTO_Printf(context, " bNumInterfaces=%d", dc[4]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bConfigurationValue=%d", dc[5]); PROTO_Printf(context, " iConfiguration=%d", dc[6]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bmAttributes=0x%02x", dc[7]); if (dc[7] & 0x40) PROTO_Printf(context, " Self-Powered"); if (dc[7] & 0x20) PROTO_Printf(context, " Remote Wakeup"); PROTO_Printf(context, " bMaxPower=%dmA", dc[8] * 2); break; case 3: PROTO_Printf(context, "STRING "); if (printData->data[2] == 0) { /* language IDs */ uint16_t i; PROTO_Printf(context, "Language IDs:"); for (i = 2; i < dc[0]; i += 2) { PROTO_Printf(context, " %04x", getWordLE(&dc[i])); } } else { uint16_t i; PROTO_Printf(context, "\""); for (i = 2; i < dc[0]; i += 2) { PROTO_Printf(context, "%c", getWordLE(&dc[i])); } PROTO_Printf(context, "\""); } break; case 4: PROTO_Printf(context, "INTERFACE "); PROTO_Printf(context, " bInterfaceNumber=0x%02x", dc[2]); PROTO_Printf(context, " bAlternateSetting=0x%02x", dc[3]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bNumEndpoints=0x%02x", dc[4]); PROTO_Printf(context, " bInterfaceClass=0x%02x", dc[5]); PROTO_Printf(context, " bInterfaceSubClass=0x%02x", dc[6]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bInterfaceProtocol=0x%02x", dc[7]); PROTO_Printf(context, " iInterface=%d", dc[8]); break; case 5: PROTO_Printf(context, "ENDPOINT "); PROTO_Printf(context, " bEndpointAddress=0x%02x (%d %3s)", dc[2], dc[2] & 0x0f, ((dc[2] & 0x80) ? "IN" : "OUT")); PROTO_Printf(context, " bmAttributes=0x%02x (", dc[3]); switch (dc[3] & 3) { case 0: PROTO_Printf(context, "Control)"); break; case 1: PROTO_Printf(context, "Isochronous: "); switch ((dc[3] >> 2) & 3) { case 0: PROTO_Printf(context, "No_Synchronization,"); break; case 1: PROTO_Printf(context, "Asynchronous,"); break; case 2: PROTO_Printf(context, "Adaptive,"); break; case 3: PROTO_Printf(context, "Synchronous,"); break; } switch ((dc[3] >> 4) & 3) { case 0: PROTO_Printf(context, "Data endpoint)"); break; case 1: PROTO_Printf(context, "Feedback endpoint)"); break; case 2: PROTO_Printf(context, "Implicit feedback data endpoint)"); break; case 3: PROTO_Printf(context, "Reserved)"); break; } break; case 2: PROTO_Printf(context, "Bulk)"); break; case 3: PROTO_Printf(context, "Interrupt)"); break; } PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " wMaxPacketSize=0x%04x (%d bytes, ato/mf=%d)", getWordLE(&dc[4]), getWordLE(&dc[4]) & 0x07ff, (getWordLE(&dc[4]) >> 10) & 3); PROTO_Printf(context, " bInterval=0x%02x", dc[6]); break; case 6: PROTO_Printf(context, "DEVICE_QUALIFIER "); PROTO_Printf(context, " bcdUSB=%04x", getWordLE(&dc[2])); PROTO_Printf(context, " bDeviceClass=0x%02x", dc[4]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bDeviceSubClass=0x%02x", dc[5]); PROTO_Printf(context, " bDeviceProtocol=0x%02x", dc[6]); PROTO_Printf(context, " bMaxPacketSize0=0x%02x", dc[7]); PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, " bNumConfigurations=%d", dc[8]); PROTO_Printf(context, " bReserved=0x%02x (Zero)", dc[9]); break; case 8: PROTO_Printf(context, "INTERFACE_POWER "); break; default: { PROTO_Printf(context, "UNKNOWN len=%-2d type=0x%02x", dc[0], dc[1]); } } if (dc[0] > 0) { return (dc + dc[0]); } else { return dc + 1; /* avoid endless loop */ } } /** * Stage Three: Display descriptors and other data. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to a record entry generated by process2 * @param localdata pointer to allocated local data block */ static void PROTOAPI display3(protoContext context, protoPtr pdata, protoPtr localdata) { stageThreeData *printData = (stageThreeData *) pdata; switch (printData->datatype) { case USB2_SETUP: { if (printData->dataflags & Frequest) { int bmRequestType, bRequest; bmRequestType = printData->data[0]; bRequest = printData->data[1]; FILE_TRACE(("*** display3_SETUP request %d", bRequest)); switch (bRequest) { case 1: PROTO_Printf(context, "CLEAR_FEATURE 0x%04x ", getWordLE(&printData->data[2])); switch (bmRequestType) { case 0: PROTO_Printf(context, "Zero"); break; case 1: PROTO_Printf(context, "Interface"); break; case 2: PROTO_Printf(context, "Endpoint"); break; default: PROTO_Printf(context, "Unknown (bmRequestType=0x%02x)", bmRequestType); } break; case 5: PROTO_Printf(context, "SET_ADDRESS %d", getWordLE(&printData->data[2])); if (bmRequestType != 0) { PROTO_Printf(context, " ??? (bmRequestType=0x%02x)", bmRequestType); } break; case 6: PROTO_Printf(context, "GET_DESCRIPTOR %s, idx=%d, lang=%02x%02x, len=%d", getDescriptorName(printData->data[3]), printData->data[2], printData->data[5], printData->data[4], getWordLE(&printData->data[6])); if (bmRequestType != 0x80) { PROTO_Printf(context, " ??? (bmRequestType=0x%02x)", bmRequestType); break; /* DO NOT try to evaluate response data */ } if (printData->resplen) { uint08_t *dc; dc = printData->resp; while (dc <= &printData->resp[printData->resplen - 1]) { dc = display3_printDescriptor(context, dc, printData); } } else { PROTO_Control(context, PROTO_CONTROL_LINEFEED); PROTO_Printf(context, "no response data"); } break; case 9: PROTO_Printf(context, "SET_CONFIGURATION %d", getWordLE(&printData->data[2])); if (bmRequestType != 0) { PROTO_Printf(context, " ??? (bmRequestType=0x%02x)", bmRequestType); } break; case 11: PROTO_Printf(context, "SET_INTERFACE alt=%d interface=%d", getWordLE(&printData->data[2]), getWordLE(&printData->data[4])); if (bmRequestType != 1) { PROTO_Printf(context, " ??? (bmRequestType=0x%02x)", bmRequestType); } break; default: PROTO_Printf(context, "UNKNOWN_COMMAND bmRequestType=0x%02x bRequest=0x%02x", bmRequestType, bRequest); } } if (printData->dataflags & Fresponse) { PROTO_Printf(context, "RSP "); } } break; case USB2_USBIN: PROTO_Printf(context, "<>DATA>>OUT"); break; default: PROTO_Printf(context, "Unhandled data type %04X", (uint32_t) printData->datatype); break; } } /** * Get signal name for chart display. * * @param context communication structure needed to call other Trace32 functions * @param pdata pointer to a record entry * @param localdata pointer to allocated local data block * * @returns alphanumeric description for signal. */ static const char *PROTO_Chart(protoContext context, protoPtr pdata, protoPtr localdata) { stageOneData *printData = (stageOneData *) pdata; if ((printData->flags) == Fpidstart) { return getPidName(printData->data & 0xff); } else { if ((printData->flags) == Fdata) return "DATA"; else return "idle"; } } /** * Protocol Parser Initialisation. * * @param context communication structure needed to call other Trace32 functions * @param command not used * * @returns PROTO_OK */ int PROTOAPI PROTO_Init(protoContext context, int command) { #define PP_NULL ((protoPtr)0) protoPtr localdata = PP_NULL; /* no private data */ PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, PP_NULL, "", PROTO_PARSE_CHANNEL); /* Register callback functions for the implemented processing stages */ PROTO_RegisterProcessCallback(context, process1, localdata, sizeof(stageOneData), 1); PROTO_RegisterDisplayCallback(context, display1, localdata, 1); PROTO_RegisterProcessCallback(context, process2, localdata, sizeof(stageTwoData), 2); PROTO_RegisterDisplayCallback(context, display2, localdata, 2); PROTO_RegisterProcessCallback(context, process3, localdata, sizeof(stageThreeData), 3); PROTO_RegisterDisplayCallback(context, display3, localdata, 3); /* Register callback function for the CHART display */ PROTO_RegisterChartCallback(context, PROTO_Chart, localdata, 1); /* Set default display to show Stage Three data */ PROTO_SetDefaultLevel(context, 3); return PROTO_OK; } /* EOF */