/* MAG160C headless FFC cadence test - replicates the official demo FFC * timing from analysis/captures/libusb0_trace.txt: * init: 66b 66c 66f -> FFC(0) -> START * FFC(1) after ~10th complete frame (switch stream to type=0) * then every FFC_PERIOD frames: FFC(0), and FFC_GAP frames later FFC(1) * Target: type=0 stream keeps flowing for 1000+ frames without stalling. * Prints per-second progress; exits with 0 on success. */ #include #include #include #include #include #define FFC_PERIOD 400 #define FFC_GAP 9 #define WANT_FRAMES 1400 static libusb_context *g_ctx; static libusb_device_handle *g_h; static int sendcmd(unsigned magic, unsigned param, int len) { unsigned char cmd[8] = {0}; cmd[0] = (unsigned char)(magic); cmd[1] = (unsigned char)(magic >> 8); cmd[2] = (unsigned char)(magic >> 16); cmd[3] = (unsigned char)(magic >> 24); if (len >= 8) { cmd[4] = (unsigned char)(param); cmd[5] = (unsigned char)(param >> 8); cmd[6] = (unsigned char)(param >> 16); cmd[7] = (unsigned char)(param >> 24); } int xfer = 0; if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1; unsigned char resp[0x1000]; if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1; return 0; } int main(void) { printf("MAG160C FFC cadence test (want %d frames)\n", WANT_FRAMES); if (libusb_init(&g_ctx)) { printf("libusb_init failed\n"); return 1; } libusb_device **list = NULL; ssize_t cnt = libusb_get_device_list(g_ctx, &list); for (ssize_t i = 0; i < cnt && !g_h; ++i) { struct libusb_device_descriptor d; libusb_get_device_descriptor(list[i], &d); if (d.idVendor == 0x833c) libusb_open(list[i], &g_h); } libusb_free_device_list(list, 1); if (!g_h) { printf("no device\n"); libusb_exit(g_ctx); return 1; } libusb_set_configuration(g_h, 2); libusb_set_configuration(g_h, 1); if (libusb_claim_interface(g_h, 0)) { printf("claim failed\n"); return 1; } if (sendcmd(0x6bb6b66b, 0, 4)) { printf("66b failed\n"); return 1; } if (sendcmd(0x6bb6b66c, 0, 4)) { printf("66c failed\n"); return 1; } if (sendcmd(0x6bb6b66f, 0, 4)) { printf("66f failed\n"); return 1; } if (sendcmd(0x6bb6b672, 0, 8)) { printf("FFC(0) pre failed\n"); return 1; } Sleep(100); if (sendcmd(0x6bb6b672, 0, 8)) { printf("FFC(0) pre2 failed\n"); return 1; } Sleep(300); if (sendcmd(0x6bb6b673, 0, 4)) { printf("START failed\n"); return 1; } Sleep(700); unsigned char frame[40000]; unsigned prev = 0xffffffff; int nread = 0, n0 = 0, n1 = 0; int ffc_started = 0, ffc_cycle = 0, ffc_wait1 = 0; int stall_wakes = 0; DWORD t_start = GetTickCount(); DWORD last_frame = t_start, last_wake = t_start; DWORD last_report = t_start; while (n0 < WANT_FRAMES) { unsigned char hdr[64]; int xfer = 0; if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) { DWORD now = GetTickCount(); if (now - last_frame > 3000 && now - last_wake > 5000) { sendcmd(0x6bb6b672, 1, 8); last_wake = now; stall_wakes++; printf(" [stall watchdog] FFC(1) sent (%d)\n", stall_wakes); } continue; } if (xfer < 28) continue; unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) | ((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24); if (m != 0x1bb1b11b) continue; unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) | ((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24); if (c == prev) continue; prev = c; if (libusb_bulk_transfer(g_h, 0x81, frame, sizeof(frame), &xfer, 200) || xfer < 38400) { continue; } unsigned type = (unsigned)hdr[12]; nread++; last_frame = GetTickCount(); if (type == 0) n0++; else n1++; if (!ffc_started && nread == 10) { sendcmd(0x6bb6b672, 1, 8); ffc_started = 1; ffc_cycle = 0; printf(" frame 10: FFC(1) sent -> type=0 mode\n"); continue; } if (ffc_started) { ffc_cycle++; if (ffc_wait1 && ffc_cycle >= FFC_GAP) { sendcmd(0x6bb6b672, 1, 8); ffc_wait1 = 0; ffc_cycle = 0; } else if (!ffc_wait1 && ffc_cycle >= FFC_PERIOD) { sendcmd(0x6bb6b672, 0, 8); ffc_wait1 = 1; ffc_cycle = 0; } } DWORD now = GetTickCount(); if (now - last_report >= 5000) { double secs = (now - t_start) / 1000.0; printf(" t=%6.1fs frames=%d (type0=%d type1=%d) fps=%.1f\n", secs, nread, n0, n1, n0 / secs); last_report = now; } } DWORD t_end = GetTickCount(); double secs = (t_end - t_start) / 1000.0; printf("\nDONE: %d frames in %.1fs (fps=%.1f) type0=%d type1=%d stall_wakes=%d\n", nread, secs, nread / secs, n0, n1, stall_wakes); int pass = (n0 >= 1000) ? 1 : 0; printf("%s\n", pass ? "PASS: type=0 stream >= 1000 frames" : "FAIL: type=0 stream < 1000 frames"); Sleep(300); libusb_clear_halt(g_h, 0x03); libusb_clear_halt(g_h, 0x82); sendcmd(0x6bb6b674, 0, 4); libusb_close(g_h); libusb_exit(g_ctx); return pass ? 0 : 2; }