summaryrefslogtreecommitdiff
path: root/rbutil/jztool/src/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/jztool/src/usb.c')
-rw-r--r--rbutil/jztool/src/usb.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c
new file mode 100644
index 0000000000..7e4a5f3388
--- /dev/null
+++ b/rbutil/jztool/src/usb.c
@@ -0,0 +1,203 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool_private.h"
23#include <stdlib.h>
24#include <stdbool.h>
25
26#define VR_GET_CPU_INFO 0
27#define VR_SET_DATA_ADDRESS 1
28#define VR_SET_DATA_LENGTH 2
29#define VR_FLUSH_CACHES 3
30#define VR_PROGRAM_START1 4
31#define VR_PROGRAM_START2 5
32
33int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id)
34{
35 int rc;
36 jz_usbdev* dev = NULL;
37 libusb_device_handle* usb_handle = NULL;
38 libusb_device** dev_list = NULL;
39 ssize_t dev_index = -1, dev_count;
40
41 rc = jz_context_ref_libusb(jz);
42 if(rc < 0)
43 return rc;
44
45 dev = malloc(sizeof(struct jz_usbdev));
46 if(!dev) {
47 rc = JZ_ERR_OUT_OF_MEMORY;
48 goto error;
49 }
50
51 dev_count = libusb_get_device_list(jz->usb_ctx, &dev_list);
52 if(dev_count < 0) {
53 jz_log(jz, JZ_LOG_ERROR, "libusb_get_device_list: %s", libusb_strerror(dev_count));
54 rc = JZ_ERR_USB;
55 goto error;
56 }
57
58 for(ssize_t i = 0; i < dev_count; ++i) {
59 struct libusb_device_descriptor desc;
60 rc = libusb_get_device_descriptor(dev_list[i], &desc);
61 if(rc < 0) {
62 jz_log(jz, JZ_LOG_WARNING, "libusb_get_device_descriptor: %s",
63 libusb_strerror(rc));
64 continue;
65 }
66
67 if(desc.idVendor != vend_id || desc.idProduct != prod_id)
68 continue;
69
70 if(dev_index >= 0) {
71 /* not the best, but it is the safest thing */
72 jz_log(jz, JZ_LOG_ERROR, "Multiple devices match ID %04x:%04x",
73 (unsigned int)vend_id, (unsigned int)prod_id);
74 jz_log(jz, JZ_LOG_ERROR, "Please ensure only one player is plugged in, and try again");
75 rc = JZ_ERR_NO_DEVICE;
76 goto error;
77 }
78
79 dev_index = i;
80 }
81
82 if(dev_index < 0) {
83 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found",
84 (unsigned int)vend_id, (unsigned int)prod_id);
85 rc = JZ_ERR_NO_DEVICE;
86 goto error;
87 }
88
89 rc = libusb_open(dev_list[dev_index], &usb_handle);
90 if(rc < 0) {
91 jz_log(jz, JZ_LOG_ERROR, "libusb_open: %s", libusb_strerror(rc));
92 rc = JZ_ERR_USB;
93 goto error;
94 }
95
96 rc = libusb_claim_interface(usb_handle, 0);
97 if(rc < 0) {
98 jz_log(jz, JZ_LOG_ERROR, "libusb_claim_interface: %s", libusb_strerror(rc));
99 rc = JZ_ERR_USB;
100 goto error;
101 }
102
103 dev->jz = jz;
104 dev->handle = usb_handle;
105 *devptr = dev;
106 rc = JZ_SUCCESS;
107
108 exit:
109 if(dev_list)
110 libusb_free_device_list(dev_list, true);
111 return rc;
112
113 error:
114 if(dev)
115 free(dev);
116 if(usb_handle)
117 libusb_close(usb_handle);
118 jz_context_unref_libusb(jz);
119 goto exit;
120}
121
122void jz_usb_close(jz_usbdev* dev)
123{
124 libusb_release_interface(dev->handle, 0);
125 libusb_close(dev->handle);
126 jz_context_unref_libusb(dev->jz);
127 free(dev);
128}
129
130static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
131{
132 int rc = libusb_control_transfer(dev->handle,
133 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
134 req, arg >> 16, arg & 0xffff, NULL, 0, 1000);
135
136 if(rc < 0) {
137 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc));
138 rc = JZ_ERR_USB;
139 } else {
140 rc = JZ_SUCCESS;
141 }
142
143 return rc;
144}
145
146static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
147{
148 int xfered = 0;
149 int ep = write ? LIBUSB_ENDPOINT_OUT|1 : LIBUSB_ENDPOINT_IN|1;
150 int rc = libusb_bulk_transfer(dev->handle, ep, buf, len, &xfered, 10000);
151
152 if(rc < 0) {
153 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: %s", libusb_strerror(rc));
154 rc = JZ_ERR_USB;
155 } else if(xfered != (int)len) {
156 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered");
157 rc = JZ_ERR_USB;
158 } else {
159 rc = JZ_SUCCESS;
160 }
161
162 return rc;
163}
164
165static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
166 size_t len, void* data)
167{
168 int rc;
169 rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr);
170 if(rc < 0)
171 return rc;
172
173 rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len);
174 if(rc < 0)
175 return rc;
176
177 return jz_usb_transfer(dev, write, len, data);
178}
179
180int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
181{
182 return jz_usb_sendrecv(dev, true, addr, len, (void*)data);
183}
184
185int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
186{
187 return jz_usb_sendrecv(dev, false, addr, len, data);
188}
189
190int jz_usb_start1(jz_usbdev* dev, uint32_t addr)
191{
192 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr);
193}
194
195int jz_usb_start2(jz_usbdev* dev, uint32_t addr)
196{
197 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr);
198}
199
200int jz_usb_flush_caches(jz_usbdev* dev)
201{
202 return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0);
203}