2 * Copyright 2011 Drew Fisher <drew.m.fisher@gmail.com>. All rights reserved.
3 * Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL DREW FISHER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of Drew Fisher.
42 #define KINECT_AUDIO_VID 0x045e
43 #define KINECT_AUDIO_PID 0x02ad
44 #define KINECT_AUDIO_CONFIGURATION 1
45 #define KINECT_AUDIO_INTERFACE 0
46 #define KINECT_AUDIO_IN_EP 0x81
47 #define KINECT_AUDIO_OUT_EP 0x01
49 static libusb_device_handle *dev;
50 static unsigned int seq;
67 #define LOG(...) printf(__VA_ARGS__)
69 #if __BYTE_ORDER == __BIG_ENDIAN
70 static inline uint32_t fn_le32(uint32_t d)
72 return (d<<24) | ((d<<8)&0xFF0000) | ((d>>8)&0xFF00) | (d>>24);
75 #define fn_le32(x) (x)
78 static void dump_bl_cmd(bootloader_command cmd) {
80 for (i = 0; i < 24; i++)
81 LOG("%02X ", ((unsigned char*)(&cmd))[i]);
85 static int get_first_reply(void) {
86 unsigned char buffer[512];
90 res = libusb_bulk_transfer(dev, KINECT_AUDIO_IN_EP, buffer, 512, &transferred, 0);
92 LOG("Error reading first reply: %d\ttransferred: %d (expected %d)\n", res, transferred, 0x60);
95 LOG("Reading first reply: ");
97 for (i = 0; i < transferred; ++i) {
98 LOG("%02X ", buffer[i]);
104 static int get_reply(void) {
107 /* The following is needed because libusb_bulk_transfer might
108 * fail when working on a buffer smaller than 512 bytes.
110 unsigned char dump[512];
115 res = libusb_bulk_transfer(dev, KINECT_AUDIO_IN_EP, reply.dump, 512, &transferred, 0);
116 if (res != 0 || transferred != sizeof(status_code)) {
117 LOG("Error reading reply: %d\ttransferred: %d (expected %zu)\n", res, transferred, sizeof(status_code));
120 if (fn_le32(reply.buffer.magic) != 0x0a6fe000) {
121 LOG("Error reading reply: invalid magic %08X\n", reply.buffer.magic);
124 if (fn_le32(reply.buffer.seq) != seq) {
125 LOG("Error reading reply: non-matching sequence number %08X (expected %08X)\n", reply.buffer.seq, seq);
128 if (fn_le32(reply.buffer.status) != 0) {
129 LOG("Notice reading reply: last uint32_t was nonzero: %d\n", reply.buffer.status);
132 LOG("Reading reply: ");
134 for (i = 0; i < transferred; ++i) {
135 LOG("%02X ", reply.dump[i]);
142 static int upload_firmware(FILE *fw) {
147 bootloader_command cmd;
148 cmd.magic = fn_le32(0x06022009);
149 cmd.seq = fn_le32(seq);
150 cmd.bytes = fn_le32(0x60);
151 cmd.cmd = fn_le32(0);
152 cmd.write_addr = fn_le32(0x15);
153 cmd.unk = fn_le32(0);
155 LOG("About to send: ");
160 res = libusb_bulk_transfer(dev, KINECT_AUDIO_OUT_EP, (unsigned char *)&cmd, sizeof(cmd), &transferred, 0);
161 if (res != 0 || transferred != sizeof(cmd)) {
162 LOG("Error: res: %d\ttransferred: %d (expected %zu)\n", res, transferred, sizeof(cmd));
166 // This first one doesn't have the usual magic bytes at the beginning,
167 // and is 96 bytes long - much longer than the usual 12-byte replies.
168 res = get_first_reply();
170 LOG("get_first_reply() failed");
174 // I'm not sure why we do this twice here, but maybe it'll make sense
178 LOG("First get_reply() failed");
183 // Split addr declaration and assignment in order to compile as C++,
184 // otherwise this would give "jump to label '...' crosses initialization"
188 unsigned char page[0x4000];
191 read = (int)fread(page, 1, 0x4000, fw);
196 cmd.seq = fn_le32(seq);
197 cmd.bytes = fn_le32((unsigned int)read);
198 cmd.cmd = fn_le32(0x03);
199 cmd.write_addr = fn_le32(addr);
200 LOG("About to send: ");
204 res = libusb_bulk_transfer(dev, KINECT_AUDIO_OUT_EP, (unsigned char *)&cmd, sizeof(cmd), &transferred, 0);
205 if (res != 0 || transferred != sizeof(cmd)) {
206 LOG("Error: res: %d\ttransferred: %d (expected %zu)\n", res, transferred, sizeof(cmd));
210 while (bytes_sent < read) {
211 int to_send = (read - bytes_sent > 512 ? 512 : read - bytes_sent);
213 res = libusb_bulk_transfer(dev, KINECT_AUDIO_OUT_EP, &page[bytes_sent], to_send, &transferred, 0);
214 if (res != 0 || transferred != to_send) {
215 LOG("Error: res: %d\ttransferred: %d (expected %d)\n", res, transferred, to_send);
218 bytes_sent += to_send;
222 LOG("get_reply failed");
226 addr += (uint32_t)read;
230 cmd.seq = fn_le32(seq);
231 cmd.bytes = fn_le32(0);
232 cmd.cmd = fn_le32(0x04);
233 cmd.write_addr = fn_le32(0x00080030);
236 res = libusb_bulk_transfer(dev, KINECT_AUDIO_OUT_EP, (unsigned char *)&cmd, sizeof(cmd), &transferred, 0);
237 if (res != 0 || transferred != sizeof(cmd)) {
238 LOG("Error: res: %d\ttransferred: %d (expected %zu)\n", res, transferred, sizeof(cmd));
248 int main(int argc, char *argv[]) {
249 char default_filename[] = "firmware.bin";
250 char *filename = default_filename;
257 FILE *fw = fopen(filename, "rb");
259 fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno));
263 ret = libusb_init(NULL);
265 fprintf(stderr, "libusb_init failed: %s\n",
266 libusb_error_name(ret));
270 libusb_set_debug(NULL, 3);
272 dev = libusb_open_device_with_vid_pid(NULL, KINECT_AUDIO_VID, KINECT_AUDIO_PID);
274 fprintf(stderr, "libusb_open failed: %s\n", strerror(errno));
276 goto out_libusb_exit;
279 int current_configuration = -1;
280 ret = libusb_get_configuration(dev, ¤t_configuration);
282 fprintf(stderr, "libusb_get_configuration failed: %s\n",
283 libusb_error_name(ret));
284 goto out_libusb_close;
287 if (current_configuration != KINECT_AUDIO_CONFIGURATION) {
288 ret = libusb_set_configuration(dev, KINECT_AUDIO_CONFIGURATION);
290 fprintf(stderr, "libusb_set_configuration failed: %s\n",
291 libusb_error_name(ret));
292 fprintf(stderr, "Cannot set configuration %d\n",
293 KINECT_AUDIO_CONFIGURATION);
294 goto out_libusb_close;
298 libusb_set_auto_detach_kernel_driver(dev, 1);
300 ret = libusb_claim_interface(dev, KINECT_AUDIO_INTERFACE);
302 fprintf(stderr, "libusb_claim_interface failed: %s\n",
303 libusb_error_name(ret));
304 fprintf(stderr, "Cannot claim interface %d\n",
305 KINECT_AUDIO_INTERFACE);
306 goto out_libusb_close;
310 * Checking that the configuration has not changed, as suggested in
311 * http://libusb.sourceforge.net/api-1.0/caveats.html
313 current_configuration = -1;
314 ret = libusb_get_configuration(dev, ¤t_configuration);
316 fprintf(stderr, "libusb_get_configuration after claim failed: %s\n",
317 libusb_error_name(ret));
318 goto out_libusb_release_interface;
321 if (current_configuration != KINECT_AUDIO_CONFIGURATION) {
322 fprintf(stderr, "libusb configuration changed (expected: %d, current: %d)\n",
323 KINECT_AUDIO_CONFIGURATION, current_configuration);
325 goto out_libusb_release_interface;
328 ret = upload_firmware(fw);
329 // Now the device reenumerates.
331 out_libusb_release_interface:
332 libusb_release_interface(dev, KINECT_AUDIO_INTERFACE);