diff --git a/.gitignore b/.gitignore index 36befee..e4c47d6 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,6 @@ debian/krode.substvars # Junk .c* + +# Built binary +krode diff --git a/README.md b/README.md index d1afb2d..0b54e31 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # krode -Delete onboard recordings from the **Rode Interview PRO** wireless microphone transmitter on Linux. +Delete onboard recordings from supported **Rode** wireless microphone transmitters on Linux. Rode only officially supports this through their proprietary **Rode Central** software (Windows/macOS). This tool replicates the delete function via the device's USB HID control interface, which is accessible on Linux. @@ -46,7 +46,7 @@ sudo usermod -aG plugdev $USER ## Usage -Connect the Interview PRO TX unit via USB, then: +Connect a supported TX unit via USB, then: ``` krode delete @@ -56,7 +56,7 @@ This deletes all onboard recordings. The device confirms with a status code of 1 ## Protocol -The Interview PRO presents as a composite USB device: +Supported transmitters present a USB HID control interface: - **Mass Storage (SCSI)** — read-only; used to copy WAV files to the host - **HID (vendor-specific)** — used for control commands @@ -67,6 +67,7 @@ Device identifiers: |------|-----|-----| | Interview PRO TX (unit 1) | `0x19F7` | `0x0063` | | Interview PRO TX (unit 2) | `0x19F7` | `0x0068` | +| Wireless PRO TX (both units) | `0x19F7` | `0x0056` | ### Delete command @@ -85,7 +86,7 @@ HID Output Report, 17 bytes, sent via SET_REPORT: ### Device ACK -Response arrives on endpoint `0x81` (URB_INTERRUPT IN), 17 bytes: +Response arrives on the HID interrupt IN endpoint, 17 bytes: ``` 02 4A 41 64 00 00 00 00 00 00 00 00 00 00 00 00 00 @@ -101,6 +102,8 @@ Response arrives on endpoint `0x81` (URB_INTERRUPT IN), 17 bytes: Reverse-engineered from USB traffic captured with Wireshark + USBPcap on Windows. Confirmed working on TX unit 1 (PID `0x0063`). PID `0x0068` is untested for delete. +Wireless PRO TX PID `0x0056` is supported by device ID. + ## Support For questions, troubleshooting, or general discussion, please use the diff --git a/krode b/krode deleted file mode 100755 index 96396f5..0000000 Binary files a/krode and /dev/null differ diff --git a/krode.c b/krode.c index 7ab7f46..5cc1fc3 100644 --- a/krode.c +++ b/krode.c @@ -1,5 +1,5 @@ /* - * krode - delete onboard recordings from Rode Interview PRO on Linux + * krode - delete onboard recordings from supported Rode transmitters on Linux * * Usage: krode delete * @@ -18,15 +18,28 @@ #define RODE_VID 0x19F7 -/* Both TX units seen on Interview PRO */ -#define RODE_PID_TX1 0x0063 -#define RODE_PID_TX2 0x0068 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +struct rode_device { + unsigned short pid; + const char *name; +}; + +static const struct rode_device rode_devices[] = { + { 0x0063, "Interview PRO TX (unit 1)" }, + { 0x0068, "Interview PRO TX (unit 2)" }, + + // Note: Unlike Interview PRO TX units, Wireless PRO TX units, as-tested, advertise + // the same USB PID. Matching on hidraw paths is required when both Wireless + // PRO TX units are plugged in. + { 0x0056, "Wireless PRO TX" }, +}; #define REPORT_SIZE 17 #define CMD_DELETE 0x4A #define DELETE_ALL 0x01 -static int delete_recordings(hid_device *dev, unsigned short pid) +static int delete_recordings(hid_device *dev, const struct rode_device *device) { unsigned char buf[REPORT_SIZE]; int ret; @@ -36,7 +49,7 @@ static int delete_recordings(hid_device *dev, unsigned short pid) buf[1] = CMD_DELETE; /* 0x4A - delete command */ buf[2] = DELETE_ALL; /* 0x01 - delete all */ - printf("Sending delete command to PID 0x%04X...\n", pid); + printf("Sending delete command to %s (PID 0x%04X)...\n", device->name, device->pid); ret = hid_write(dev, buf, sizeof(buf)); if (ret < 0) { @@ -62,30 +75,63 @@ static int delete_recordings(hid_device *dev, unsigned short pid) } /* Print raw response for debugging */ - printf("Response: "); - for (int i = 0; i < ret; i++) - printf("%02x ", buf[i]); - printf("\n"); + fprintf(stderr, "Unexpected response: "); + + for (int i = 0; i < ret; i++) { + fprintf(stderr, "%02x ", buf[i]); + } - return 0; + fprintf(stderr, "\n"); + + return -1; } -static int run_on_pid(unsigned short pid) +static int run_on_device(const struct rode_device *device) { + struct hid_device_info *devs; + struct hid_device_info *cur; hid_device *dev; + int found = 0; + int failed = 0; - dev = hid_open(RODE_VID, pid, NULL); - if (!dev) + devs = hid_enumerate(RODE_VID, device->pid); + if (!devs) return -1; /* not found, not an error if other PID works */ - delete_recordings(dev, pid); - hid_close(dev); - return 0; + // Enumerate all found devices + for (cur = devs; cur; cur = cur->next) { + if (!cur->path) + continue; + + found++; + + printf("Opening %s (PID 0x%04X, path %s)...\n", device->name, device->pid, cur->path); + + dev = hid_open_path(cur->path); + if (!dev) { + fprintf(stderr, "hid_open_path failed for %s\n", cur->path); + failed++; + continue; + } + + if (delete_recordings(dev, device) != 0) + failed++; + + hid_close(dev); + } + + hid_free_enumeration(devs); + + if (found == 0) + return -1; + + return failed == 0 ? 0 : -2; } int main(int argc, char *argv[]) { int found = 0; + int failed = 0; if (argc < 2 || strcmp(argv[1], "delete") != 0) { fprintf(stderr, "Usage: %s delete\n", argv[0]); @@ -98,12 +144,21 @@ int main(int argc, char *argv[]) return 1; } - if (run_on_pid(RODE_PID_TX1) == 0) found++; - if (run_on_pid(RODE_PID_TX2) == 0) found++; + for (size_t i = 0; i < ARRAY_SIZE(rode_devices); i++) { + int ret = run_on_device(&rode_devices[i]); + + if (ret == -1) + continue; + + found++; + if (ret == 0) + continue; + failed++; + } if (found == 0) { fprintf(stderr, - "No Rode Interview PRO found. Check USB connection.\n"); + "No supported Rode transmitter found. Check USB connection.\n"); fprintf(stderr, "You may need to run as root or add a udev rule.\n"); hid_exit(); @@ -111,5 +166,5 @@ int main(int argc, char *argv[]) } hid_exit(); - return 0; + return failed == 0 ? 0 : 1; }