// Outputs any packets saved during the collapsing process static void output_saved (int * packetnum, int * signal_errors, CollapseInfo * collapse_info, PacketQueue * pkt_q) { usb_print_summary_packet(packetnum, collapse_info, signal_errors); while (pkt_q->head != pkt_q->tail) { usb_print_packet(*packetnum, &pkt_q->pkt[pkt_q->head], 0); *packetnum += 1; pkt_q->head = (pkt_q->head + 1) % QUEUE_SIZE; } }
int usb_recv(vfs_dev_t *dev, unsigned char endpoint, int max_bytes) { int transferred = 0; assert(max_bytes <= sizeof(dev->recv_buf)); int r = libusb_bulk_transfer( dev->devh, endpoint, dev->recv_buf, max_bytes, &dev->recv_len, VALIDITY_DEFAULT_WAIT_TIMEOUT ); #ifdef DEBUG usb_print_packet(0, r, dev->recv_buf, dev->recv_len); #endif if (r < 0) return r; return 0; }
int usb_send(vfs_dev_t *dev, const unsigned char *data, int length) { int transferred = 0; int r = libusb_bulk_transfer( dev->devh, VALIDITY_SEND_ENDPOINT, (unsigned char *)data, length, &transferred, VALIDITY_DEFAULT_WAIT_TIMEOUT ); #ifdef DEBUG usb_print_packet(1, r, data, length); #endif assert(r == 0); if (r < 0) return r; if (transferred < length) return r; return 0; }
// The main USB dump routine static void usb_dump (int num_packets) { // Packets will be saved during the collapse process PacketQueue pkt_q; // Points to the current packet PacketInfo * cur_pkt; // Collapsing counts and time collapsing started CollapseInfo collapse_info; // Collapsing of packets is handled through a state machine. // Sometimes the machine will need to be re-run to properly // handle the sequence of packets. enum CollapseStates { IDLE = 0, IN, PING, SPLIT, SPLIT_IN, SPLIT_OUT, SPLIT_SETUP } state = IDLE; u08 re_run = FALSE; u08 pid = 0; int signal_errors = 0; int packetnum = 0; // Initialize structures memset(&pkt_q, 0, sizeof(PacketQueue)); memset(&collapse_info, 0, sizeof(CollapseInfo)); samplerate_khz = bg_samplerate(beagle, 0); // Configure Beagle 480 for realtime capture bg_usb480_capture_configure(beagle, BG_USB480_CAPTURE_REALTIME, BG_USB2_AUTO_SPEED_DETECT); // Filter packets intended for the Beagle analyzer. This is only // relevant when one host controller is being used. bg_usb480_hw_filter_config(beagle, BG_USB2_HW_FILTER_SELF); if (bg_enable(beagle, BG_PROTOCOL_USB) != BG_OK) { printf("error: could not enable USB capture; exiting...\n"); exit(1); } // Output the header... printf("index,time(ns),USB,status,pid,data0 ... dataN(*)\n"); fflush(stdout); // ...then start decoding packets while (packetnum < num_packets || !num_packets) { cur_pkt = &pkt_q.pkt[pkt_q.tail]; cur_pkt->length = bg_usb480_read(beagle, &cur_pkt->status, &cur_pkt->events, &cur_pkt->time_sop, &cur_pkt->time_duration, &cur_pkt->time_dataoffset, 1024, cur_pkt->data); cur_pkt->time_sop_ns = TIMESTAMP_TO_NS(cur_pkt->time_sop, samplerate_khz); // Exit if observed end of capture if (cur_pkt->status & BG_READ_USB_END_OF_CAPTURE) { usb_print_summary_packet(&packetnum, &collapse_info, &signal_errors); break; } // Check for invalid packet or Beagle error if (cur_pkt->length < 0) { char error_status[32]; sprintf(error_status, "error=%d", cur_pkt->length); usb_print_packet(packetnum, cur_pkt, error_status); break; } // Check for USB error if (cur_pkt->status == BG_READ_USB_ERR_BAD_SIGNALS) ++signal_errors; // Set the PID for collapsing state machine below. Treat // KEEP_ALIVEs as packets. if (cur_pkt->length > 0) pid = cur_pkt->data[0]; else if ((cur_pkt->events & BG_EVENT_USB_KEEP_ALIVE) && !(cur_pkt->status & BG_READ_USB_ERR_BAD_PID)) { pid = KEEP_ALIVE; } else pid = 0; // Collapse these packets approprietly: // KEEP_ALIVE* SOF* (IN (ACK|NAK))* (PING NAK)* // (SPLIT (OUT|SETUP) NYET)* (SPLIT IN (ACK|NYET|NACK))* // If the time elapsed since collapsing began is greater than // the threshold, output the counts and zero out the counters. if ((int)(cur_pkt->time_sop - collapse_info.time_sop) >= IDLE_SAMPLES) usb_print_summary_packet(&packetnum, &collapse_info, &signal_errors); while(1) { re_run = FALSE; switch (state) { // The initial state of the state machine. Collapse SOFs // and KEEP_ALIVEs. Save IN, PING, or SPLIT packets and // move to the next state for the next packet. Otherwise, // print the collapsed packet counts and the current packet. case IDLE: switch (pid) { case KEEP_ALIVE: COLLAPSE(KEEP_ALIVE); break; case BG_USB_PID_SOF: COLLAPSE(SOF); break; case BG_USB_PID_IN: SAVE_PACKET(); state = IN; break; case BG_USB_PID_PING: SAVE_PACKET(); state = PING; break; case BG_USB_PID_SPLIT: SAVE_PACKET(); state = SPLIT; break; default: usb_print_summary_packet(&packetnum, &collapse_info, &signal_errors); if (cur_pkt->length > 0 || cur_pkt->events || (cur_pkt->status != 0 && cur_pkt->status != BG_READ_TIMEOUT)) { usb_print_packet(packetnum, cur_pkt, 0); packetnum++; } } break; // Collapsing IN+ACK or IN+NAK. Otherwise, output any // saved packets and rerun the collapsing state machine // on the current packet. case IN: state = IDLE; switch (pid) { case BG_USB_PID_ACK: COLLAPSE(IN_ACK); break; case BG_USB_PID_NAK: COLLAPSE(IN_NAK); break; default: re_run = TRUE; } break; // Collapsing PING+NAK case PING: state = IDLE; switch (pid) { case BG_USB_PID_NAK: COLLAPSE(PING_NAK); break; default: re_run = TRUE; } break; // Expecting an IN, OUT, or SETUP case SPLIT: switch (pid) { case BG_USB_PID_IN: SAVE_PACKET(); state = SPLIT_IN; break; case BG_USB_PID_OUT: SAVE_PACKET(); state = SPLIT_OUT; break; case BG_USB_PID_SETUP: SAVE_PACKET(); state = SPLIT_SETUP; break; default: state = IDLE; re_run = TRUE; } break; // Collapsing SPLIT+IN+NYET, SPLIT+IN+NAK, SPLIT+IN+ACK case SPLIT_IN: state = IDLE; switch (pid) { case BG_USB_PID_NYET: COLLAPSE(SPLIT_IN_NYET); break; case BG_USB_PID_NAK: COLLAPSE(SPLIT_IN_NAK); break; case BG_USB_PID_ACK: COLLAPSE(SPLIT_IN_ACK); break; default: re_run = TRUE; } break; // Collapsing SPLIT+OUT+NYET case SPLIT_OUT: state = IDLE; switch (pid) { case BG_USB_PID_NYET: COLLAPSE(SPLIT_OUT_NYET); break; default: re_run = TRUE; } break; // Collapsing SPLIT+SETUP+NYET case SPLIT_SETUP: state = IDLE; switch (pid) { case BG_USB_PID_NYET: COLLAPSE(SPLIT_SETUP_NYET); break; default: re_run = TRUE; } break; } if (re_run == FALSE) break; // The state machine is about to be re-run. This // means that a complete packet sequence wasn't collapsed // and there are packets in the queue that need to be // output before we can process the current packet. OUTPUT_SAVED(); } } // Stop the capture bg_disable(beagle); }