From 4961a6d716e9096751dd7db7bed5c1b18592107e Mon Sep 17 00:00:00 2001 From: John Hopper Date: Mon, 8 Jun 2026 16:29:56 -0700 Subject: [PATCH] remove krode binary from repo; adjust C code for Rode Wireless PRO PID and hidraw path enumeration; documentation updates --- .gitignore | 3 ++ README.md | 11 +++--- krode | Bin 16512 -> 0 bytes krode.c | 97 +++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 86 insertions(+), 25 deletions(-) delete mode 100755 krode 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 96396f56e76fa2c4fc794db6074b6ae874500da0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 zcmeHOdyo{x8SmW#IT3dc9})1uXbuZZZn@(Xz(RNL0ej|v3mnE54s*A&cN_P1w`OPI zpe#9aN?Z{oi7`e?V`<7tti&Q#LXeoSiXbtTLMmlpMH4E5*^4iHzuEl0p8l4(@ivvJ zRPu+lUA6P|_xm2*{Y}qo_jLEhwvNSqpHFbA5f=%`o#G&2B`C~i4I#o}z6jttRh%PE z06Yf2uo8P|U5z7PTC4O3CcE2}+)K|=dQ7>7M9HpFYCO@AFcozUPj+J|t9UQHUO8pT z@^X0^86#=(TBa}h9iFM%j$%`2aO8zWBk49}w_j1VBbMy;E4%&5j_FaV5E4_4Csm(* zohqJQszF9clUE_Ub{S>orS(dmDV0HqDE%HgkGRiMO`^xkW4o0{>=@;}^<&;^$Mx(~ zcDJ1CIQG&xsywD#-Zx-J@%&E{caIgSyh`^ktnM3Abz5Xi$OrYcCCgR`|DY&PUEK21 zz*D$MesIC=W2;)y{jbEYShVS9Ke*!Vo1bZ$nM`!gn=>;RYe*(ixq*g(`STj)%?V}G zp;^*yqVgx(<-1;pHNsDrdSt}`^V$BNfBogcg10yG;{)l))CrdL-IA>p@M|jIcUHiu zy&8Vr)D2*?av!OH=PKaORlt8)0Y4kKhMzYr1~6KEHdeqduYm8XfPWRZhMzY*3P3$N zBSg=Gn@BGM&4IZeKP2%fVleFB{9FjVy=$o+GcB_>k+n^$YiUa|oie+O?xZPny|+J| z(zCW<*}Cqij;h#~i0SLBgl!7Sr88zqX3T+vEi;Lf%9xgwwj|V(OlKW+%QRxTo#;2y zIa_3Mc2A#Sp=fmC9#wR|kw}TGjaphFp0N@sI}U}O0Yi@`Qbsbd!4z?)_^fU9^k-1h zAZ$2?I=L_YzgXP45izIz8AuNpDd=U}-h3H-D8MICrb{66RVa7cBced>M9_7a|3dT)2EsIqE`l;Zz2v>0u{F7{`u~#xPxlAYA>mOMZS> z5{Zv@vho^s;S*eV)P9_D6@FtleF1T50an5S6AqgKKbV7&+qmNIrb1gf#65;%TTbykGJ=iKijL za6$5q5Klvc;cb$ChJ+rT$z$kr~IL`J>Ut-?%vX_=h!7-`?nfV>Y5bcByhuTRIdE zPHjDI|4#6nj|UgM1hL3{WmR-&(FK%^=8xDDa917#Q=CB!i=n=I$Nn0;Z#Bv;mrwS? z>turmVJIS(HvK8GD$=!TW$-tD+x5X@+=9R2_G`s2e^x4?3BemafL42c?+)K3B-`6s z@At*?W9re6gBVaY725NMqffQg%fT*Xel$>BxUj>;eyOH$4m}9(=R# z=niy2M<&(aw@w6SjHxd!gl2vZ>C#gdyy0G42d}O7U6LCI;*Z!}DEfhuspzJ^6d(Jv zRO;B$a4&cnr$})7oU=OeZ#EU8L%V&&dmvu$(#GLk^Z*ofQbuKd2ucc1YFDm~ToqXz zxq5eSyIo%c$5+WV6-{Wo`sbm2rB`1dgLz;PULCVnbo!5=6U{$?exD+>U!ZrjL9%li z%HS^c8k$$Ujl{Q-gZDm>4$g5LJXdzm0FB`UZijsDjCj+)Sv6H7P>nz}0@VmqBT$V% zH3HQLR3lK0!2dJ?y#BG$OkugDSBsfR(>Ar9bbr5*ifMLQ>uhh;8V9B|&bcfU3emJe zrY>i_M_XqkY_f}|E!eJ2^q87uW;5wj)|ASvSd0p3E6kp>MgFs@fS5KNt5S9@yHJ}J zn*mLYOV%1VXC#waI@O&vtQdT<166}%X>CP1W@_yzEPAa?nCrC870XeJTq+jQTKdeM zYqV7>o6(L`%IvWd>6EJa<>{Q(Z>-l+rYT!zd*3bka7SjwZhn8qoE=f(G z1u?lcrNxaz(u|?P$*cp@!q-T2g=+djO-yT?HJ}M6oMKhh=rv)<&3Ei+RZZE;R{74X zxsYb>^fBc2H%g^zL1S;0Nhn)25Q0{)x4b&|TOu8g^(z@C~aq*N3FPL2~-H<(|=VAx+ z-0L)9U{~RHAIiIlK7KR@>TdP71}1H(X%1+&jEMxMZyFm3G!C7xEYRuy$M`@akVrsl z4ore(bD%bIl9*2R_$A@{Lr@V3)NS%d0+WVnmIO5aH^&DiwFK%SQkTSI@S{&8R2;3P zQ=(}(eSE5>Y6Pkgs79a~focS*5vWF>8i8sAzK9XveP_JSjK;#0jyR;ehl}>MQNl`$ zlW5?KgmomD2Gqm=3t}?y{tkxqHTVKeYLIv@ z*dZn73FTR;;=HGAlA|t8SCok|6GgSJjmP?$vgi2??=3q+8P=%^GCp5rxZVeq9`7;Z z^0AI3+XJyw#ag4}Yn45gvLw%O$Mi??FRF%qqnImir%5YrBLf692J`tOwb zr-&Wyj{wSOwD|0k`d<>>{&GOtOccEDUbzs5rT(Py^T_ze$aq2{MKx&nX!scw@N+BR z7XjC}Fc-a6;*-UiunR=?3gC5OvdDOF1Ndn5pnI5EWhudl<^WT7gx@lS^+;FIJMV%A7=sA%GSp-9l*~! z&X#jAs=puqHlFuXFZo9t2E&dj|38S8J(k@RN@L%;(Vft3qgPyG;R9r-N8l5pj$QVd zL`*;@Yvvq46-SR0j3*%T??@gw=jies4(^ghDasvV%TQl_4HHJ;?qiWDL z`Gjs*ma$$pQ?|8U#4V%W)ML5+{`D|%aXNie9HFY~i&sRJw&`ulTImcx>*dQLOWRvu zg->RBTU1G-tt*7Sq+@w=q(fi6c=5`%F1;(#+|j1X51{fyfZEz0=H2$R#h$*7I%fht zx8c96hh6#q_K<=ZGi*b8lE(~WV+!J(f-D zefXeFhayIvEhvBA6$ouimOJ7=gRXaHv#NA?jzKn=4n{af7ChzR`~^pv4rYvc+G6C< zhY=2(LlPq(5z4Obw~cO4+j3|h=Td3g4E3gRp^SxFWZCOQs0?-I5;#GGP8q^^3)9L7 zsn};^`$QP;hKn*+E~Cxud4 z-p-KOr@Ccd3{IF+^@}vOH_#&_9XY+#vFAL>Gx`%fs7%_Xa!^(3Bq7`j5u(KjJO<@w z`M=4~cz}j-cX7!}F(JkDpz z<9evbqd zk;ne1QeevUWjD+-z29Tc|L;r(RDWc9E}!|IdhB_;#gyk`tnY3A!^)oTpU{-1qIng6 zg_h&b{B~qWh3$EK%XAJaD(Wr&NsoQuTt|`VryhHb8^g~c<9`3}Jcg;piie-U^9 zKMpg`+xgt8w>;*V7GWE7+4DSPA9m7GVm*gD&NKcl1k`uAe4aP(`DH$*%>A3~xF6gD zTZ%v1^LeC|f!iwqtq%jNSHY!j9gWMu&1e9qxGh znV$e-^2PNZ3_Frc1$)gAm#-65`AWTvv;EXE;$*0qwp9{Al*~f6dXD;xX{B;$N*BJmvrZ 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; }