blob: 6cc72ae916520bde4774d141bd5e52c75cf6bebb [file] [log] [blame]
Nicholas Chin197b7c72022-10-23 13:10:31 -06001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2022 Nicholas Chin <nic.c3.14@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <string.h>
18#include <stdlib.h>
19#include <libusb.h>
20#include "platform.h"
21#include "programmer.h"
22#include "flash.h"
23
24#define CH347_CMD_SPI_SET_CFG 0xC0
25#define CH347_CMD_SPI_CS_CTRL 0xC1
26#define CH347_CMD_SPI_OUT_IN 0xC2
27#define CH347_CMD_SPI_IN 0xC3
28#define CH347_CMD_SPI_OUT 0xC4
29#define CH347_CMD_SPI_GET_CFG 0xCA
30
31#define CH347_CS_ASSERT 0x00
32#define CH347_CS_DEASSERT 0x40
33#define CH347_CS_CHANGE 0x80
34#define CH347_CS_IGNORE 0x00
35
36#define WRITE_EP 0x06
37#define READ_EP 0x86
38
Nicholas Chin197b7c72022-10-23 13:10:31 -060039/* The USB descriptor says the max transfer size is 512 bytes, but the
40 * vendor driver only seems to transfer a maximum of 510 bytes at once,
41 * leaving 507 bytes for data as the command + length take up 3 bytes
42 */
43#define CH347_PACKET_SIZE 510
44#define CH347_MAX_DATA_LEN (CH347_PACKET_SIZE - 3)
45
46struct ch347_spi_data {
47 struct libusb_device_handle *handle;
Nico Hubere39549b2024-07-27 23:58:32 +020048 unsigned int iface;
Nicholas Chin197b7c72022-10-23 13:10:31 -060049};
50
51/* TODO: Add support for HID mode */
52static const struct dev_entry devs_ch347_spi[] = {
53 {0x1A86, 0x55DB, OK, "QinHeng Electronics", "USB To UART+SPI+I2C"},
Nico Huber448457a2024-07-28 00:02:54 +020054 {0x1A86, 0x55DE, OK, "QinHeng Electronics", "USB To UART+SPI+I2C+JTAG"},
Nicholas Chin197b7c72022-10-23 13:10:31 -060055 {0}
56};
57
58static int ch347_spi_shutdown(void *data)
59{
60 struct ch347_spi_data *ch347_data = data;
61
Nico Hubere39549b2024-07-27 23:58:32 +020062 libusb_release_interface(ch347_data->handle, ch347_data->iface);
63 libusb_attach_kernel_driver(ch347_data->handle, ch347_data->iface);
Nicholas Chin197b7c72022-10-23 13:10:31 -060064 libusb_close(ch347_data->handle);
65 libusb_exit(NULL);
66
67 free(data);
68 return 0;
69}
70
71static int ch347_cs_control(struct ch347_spi_data *ch347_data, uint8_t cs1, uint8_t cs2)
72{
73 uint8_t cmd[13] = {
74 [0] = CH347_CMD_SPI_CS_CTRL,
75 /* payload length, uint16 LSB: 10 */
76 [1] = 10,
77 [3] = cs1,
78 [8] = cs2
79 };
80
Arthur Heymans317dd912026-07-09 20:59:35 +020081 int transferred;
82 int32_t ret = libusb_bulk_transfer(ch347_data->handle, WRITE_EP, cmd, sizeof(cmd), &transferred, 1000);
Nicholas Chin197b7c72022-10-23 13:10:31 -060083 if (ret < 0) {
84 msg_perr("Could not change CS!\n");
85 return -1;
86 }
87 return 0;
88}
89
90
91static int ch347_write(struct ch347_spi_data *ch347_data, unsigned int writecnt, const uint8_t *writearr)
92{
93 unsigned int data_len;
94 int packet_len;
95 int transferred;
96 int ret;
97 uint8_t resp_buf[4] = {0};
98 uint8_t buffer[CH347_PACKET_SIZE] = {0};
99 unsigned int bytes_written = 0;
100
101 while (bytes_written < writecnt) {
102 data_len = min(CH347_MAX_DATA_LEN, writecnt - bytes_written );
103 packet_len = data_len + 3;
104
105 buffer[0] = CH347_CMD_SPI_OUT;
106 buffer[1] = (data_len) & 0xFF;
107 buffer[2] = ((data_len) & 0xFF00) >> 8;
108 memcpy(buffer + 3, writearr + bytes_written, data_len);
109
110 ret = libusb_bulk_transfer(ch347_data->handle, WRITE_EP, buffer, packet_len, &transferred, 1000);
111 if (ret < 0 || transferred != packet_len) {
112 msg_perr("Could not send write command\n");
113 return -1;
114 }
115
Arthur Heymans317dd912026-07-09 20:59:35 +0200116 ret = libusb_bulk_transfer(ch347_data->handle, READ_EP, resp_buf, sizeof(resp_buf), &transferred, 1000);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600117 if (ret < 0) {
118 msg_perr("Could not receive write command response\n");
119 return -1;
120 }
121 bytes_written += data_len;
122 }
123 return 0;
124}
125
126static int ch347_read(struct ch347_spi_data *ch347_data, unsigned int readcnt, uint8_t *readarr)
127{
128 uint8_t *read_ptr = readarr;
129 int ret;
130 int transferred;
131 unsigned int bytes_read = 0;
132 uint8_t buffer[CH347_PACKET_SIZE] = {0};
133 uint8_t command_buf[7] = {
134 [0] = CH347_CMD_SPI_IN,
135 [1] = 4,
136 [2] = 0,
137 [3] = readcnt & 0xFF,
138 [4] = (readcnt & 0xFF00) >> 8,
139 [5] = (readcnt & 0xFF0000) >> 16,
140 [6] = (readcnt & 0xFF000000) >> 24
141 };
142
143 ret = libusb_bulk_transfer(ch347_data->handle, WRITE_EP, command_buf, sizeof(command_buf), &transferred, 1000);
144 if (ret < 0 || transferred != sizeof(command_buf)) {
145 msg_perr("Could not send read command\n");
146 return -1;
147 }
148
149 while (bytes_read < readcnt) {
150 ret = libusb_bulk_transfer(ch347_data->handle, READ_EP, buffer, CH347_PACKET_SIZE, &transferred, 1000);
151 if (ret < 0) {
152 msg_perr("Could not read data\n");
153 return -1;
154 }
155 if (transferred > CH347_PACKET_SIZE) {
156 msg_perr("libusb bug: bytes received overflowed buffer\n");
157 return -1;
158 }
159 /* Response: u8 command, u16 data length, then the data that was read */
160 if (transferred < 3) {
161 msg_perr("CH347 returned an invalid response to read command\n");
162 return -1;
163 }
164 int ch347_data_length = read_le16(buffer, 1);
165 if (transferred - 3 < ch347_data_length) {
166 msg_perr("CH347 returned less data than data length header indicates\n");
167 return -1;
168 }
169 bytes_read += ch347_data_length;
170 if (bytes_read > readcnt) {
171 msg_perr("CH347 returned more bytes than requested\n");
172 return -1;
173 }
174 memcpy(read_ptr, buffer + 3, ch347_data_length);
175 read_ptr += ch347_data_length;
176 }
177 return 0;
178}
179
Nico Huber610c1aa2023-02-15 02:56:05 +0100180static int ch347_spi_send_command(const struct spi_master *mst, unsigned int writecnt,
Nicholas Chin197b7c72022-10-23 13:10:31 -0600181 unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr)
182{
Nico Huber610c1aa2023-02-15 02:56:05 +0100183 struct ch347_spi_data *ch347_data = mst->data;
Nicholas Chin197b7c72022-10-23 13:10:31 -0600184 int ret = 0;
185
186 ch347_cs_control(ch347_data, CH347_CS_ASSERT | CH347_CS_CHANGE, CH347_CS_IGNORE);
187 if (writecnt) {
188 ret = ch347_write(ch347_data, writecnt, writearr);
189 if (ret < 0) {
190 msg_perr("CH347 write error\n");
191 return -1;
192 }
193 }
194 if (readcnt) {
195 ret = ch347_read(ch347_data, readcnt, readarr);
196 if (ret < 0) {
197 msg_perr("CH347 read error\n");
198 return -1;
199 }
200 }
201 ch347_cs_control(ch347_data, CH347_CS_DEASSERT | CH347_CS_CHANGE, CH347_CS_IGNORE);
202
203 return 0;
204}
205
Nicholas Chindac42392024-07-30 20:01:59 -0600206static int32_t ch347_spi_config(struct ch347_spi_data *ch347_data, uint8_t divisor, uint8_t mode)
Nicholas Chin197b7c72022-10-23 13:10:31 -0600207{
208 int32_t ret;
Arthur Heymans317dd912026-07-09 20:59:35 +0200209 int transferred;
Nicholas Chin197b7c72022-10-23 13:10:31 -0600210 uint8_t buff[29] = {
211 [0] = CH347_CMD_SPI_SET_CFG,
212 [1] = (sizeof(buff) - 3) & 0xFF,
213 [2] = ((sizeof(buff) - 3) & 0xFF00) >> 8,
214 /* Not sure what these two bytes do, but the vendor
215 * drivers seem to unconditionally set these values
216 */
217 [5] = 4,
218 [6] = 1,
219 /* Clock polarity: bit 1 */
Nicholas Chindac42392024-07-30 20:01:59 -0600220 [9] = mode & 2,
Nicholas Chin197b7c72022-10-23 13:10:31 -0600221 /* Clock phase: bit 0 */
Nicholas Chindac42392024-07-30 20:01:59 -0600222 [11] = mode & 1,
Nicholas Chin197b7c72022-10-23 13:10:31 -0600223 /* Another mystery byte */
224 [14] = 2,
225 /* Clock divisor: bits 5:3 */
226 [15] = (divisor & 0x7) << 3,
227 /* Bit order: bit 7, 0=MSB */
228 [17] = 0,
229 /* Yet another mystery byte */
230 [19] = 7,
231 /* CS polarity: bit 7 CS2, bit 6 CS1. 0 = active low */
232 [24] = 0
233 };
234
Arthur Heymans317dd912026-07-09 20:59:35 +0200235 ret = libusb_bulk_transfer(ch347_data->handle, WRITE_EP, buff, sizeof(buff), &transferred, 1000);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600236 if (ret < 0) {
237 msg_perr("Could not configure SPI interface\n");
238 }
239
240 /* FIXME: Not sure if the CH347 sends error responses for
241 * invalid config data, if so the code should check
242 */
Arthur Heymans317dd912026-07-09 20:59:35 +0200243 ret = libusb_bulk_transfer(ch347_data->handle, READ_EP, buff, sizeof(buff), &transferred, 1000);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600244 if (ret < 0) {
245 msg_perr("Could not receive configure SPI command response\n");
246 }
247 return ret;
248}
249
250static const struct spi_master spi_master_ch347_spi = {
251 .features = SPI_MASTER_4BA,
252 .max_data_read = MAX_DATA_READ_UNLIMITED,
253 .max_data_write = MAX_DATA_WRITE_UNLIMITED,
254 .command = ch347_spi_send_command,
255 .multicommand = default_spi_send_multicommand,
256 .read = default_spi_read,
257 .write_256 = default_spi_write_256,
258 .write_aai = default_spi_write_aai,
259 .shutdown = ch347_spi_shutdown,
260 .probe_opcode = default_spi_probe_opcode,
261};
262
Nico Huberc32e9542023-02-21 00:46:37 +0000263static unsigned int ch347_div_to_khz(unsigned int div)
264{
265 /* divisor is a power of two starting from 2 for `div == 0` */
266 const unsigned int clk_khz = 120*1000;
267 return clk_khz / (1 << (div + 1));
268}
269
Nicholas Chin197b7c72022-10-23 13:10:31 -0600270/* Largely copied from ch341a_spi.c */
Nico Hubere3a26882023-01-11 21:45:51 +0100271static int ch347_spi_init(struct flashprog_programmer *const prog)
Nicholas Chin197b7c72022-10-23 13:10:31 -0600272{
273 struct ch347_spi_data *ch347_data = calloc(1, sizeof(*ch347_data));
274 if (!ch347_data) {
275 msg_perr("Could not allocate space for SPI data\n");
276 return 1;
277 }
278
Nico Huberc32e9542023-02-21 00:46:37 +0000279 unsigned int div = 3; /* Default to 7.5MHz */
280 char *const spispeed = extract_programmer_param("spispeed");
281 if (spispeed) {
282 char *endptr;
283 const unsigned long khz = strtoul(spispeed, &endptr, 10);
284 if (*endptr != '\0' || endptr == spispeed) {
285 msg_perr("Invalid `spispeed` argument, please provide the frequency in kHz.\n");
286 free(spispeed);
287 free(ch347_data);
288 return 1;
289 }
290 free(spispeed);
291
292 for (div = 0; div < 7; ++div) {
293 if (ch347_div_to_khz(div) <= khz)
294 break;
295 }
296 msg_pinfo("Using spispeed of %ukHz.\n", ch347_div_to_khz(div));
297 } else {
298 msg_pdbg("Using default spispeed of %ukHz.\n", ch347_div_to_khz(div));
299 }
Nicholas Chindac42392024-07-30 20:01:59 -0600300 char *const spimode = extract_programmer_param("spimode");
301 uint8_t mode = 0;
302 if (spimode) {
303 char *endptr;
304 mode = strtoul(spimode, &endptr, 10);
305 if (*endptr != '\0' || endptr == spimode || mode > 3) {
306 msg_perr("Invalid `spimode` argument.\n");
307 free(spimode);
308 free(ch347_data);
309 return 1;
310 }
311 free(spimode);
312
313 msg_pinfo("Using spimode of %u.\n", mode);
314 } else {
315 msg_pdbg("Using default spimode of 0.\n");
316 }
Nico Huberc32e9542023-02-21 00:46:37 +0000317
Nicholas Chin197b7c72022-10-23 13:10:31 -0600318 int32_t ret = libusb_init(NULL);
319 if (ret < 0) {
320 msg_perr("Could not initialize libusb!\n");
321 free(ch347_data);
322 return 1;
323 }
324
325 /* Enable information, warning, and error messages (only). */
326#if LIBUSB_API_VERSION < 0x01000106
327 libusb_set_debug(NULL, 3);
328#else
329 libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
330#endif
331
Nico Huber448457a2024-07-28 00:02:54 +0200332 const struct dev_entry *dev_entry;
333 for (dev_entry = devs_ch347_spi; dev_entry->vendor_id != 0; ++dev_entry) {
334 ch347_data->handle = libusb_open_device_with_vid_pid(
335 NULL, dev_entry->vendor_id, dev_entry->device_id);
336 if (ch347_data->handle)
337 break;
338 }
Nicholas Chin197b7c72022-10-23 13:10:31 -0600339 if (ch347_data->handle == NULL) {
Nico Huber448457a2024-07-28 00:02:54 +0200340 msg_perr("Couldn't find CH347 device.\n");
Nicholas Chin197b7c72022-10-23 13:10:31 -0600341 free(ch347_data);
342 return 1;
343 }
344
Nico Hubere39549b2024-07-27 23:58:32 +0200345 struct libusb_config_descriptor *config;
346 ret = libusb_get_active_config_descriptor(libusb_get_device(ch347_data->handle), &config);
347 if (ret != LIBUSB_SUCCESS) {
348 msg_perr("Couldn't get config descriptor: %s (%d)\n", libusb_strerror(ret), ret);
349 ret = 1;
350 goto error_exit;
351 }
Nicholas Chin197b7c72022-10-23 13:10:31 -0600352
Nico Hubere39549b2024-07-27 23:58:32 +0200353 unsigned int iface;
354 for (iface = 0; iface < config->bNumInterfaces; ++iface) {
355 if (config->interface[iface].altsetting[0].bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC)
356 break;
357 }
358 if (iface == config->bNumInterfaces) {
359 msg_perr("Couldn't find compatible interface.\n");
360 ret = 1;
361 goto error_exit;
362 }
363 ch347_data->iface = iface;
364
365 ret = libusb_detach_kernel_driver(ch347_data->handle, iface);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600366 if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND)
367 msg_pwarn("Cannot detach the existing USB driver. Claiming the interface may fail. %s\n",
368 libusb_error_name(ret));
369
Nico Hubere39549b2024-07-27 23:58:32 +0200370 ret = libusb_claim_interface(ch347_data->handle, iface);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600371 if (ret != 0) {
372 msg_perr("Failed to claim interface 2: '%s'\n", libusb_error_name(ret));
373 goto error_exit;
374 }
375
376 struct libusb_device *dev;
377 if (!(dev = libusb_get_device(ch347_data->handle))) {
378 msg_perr("Failed to get device from device handle.\n");
379 goto error_exit;
380 }
381
382 struct libusb_device_descriptor desc;
383 ret = libusb_get_device_descriptor(dev, &desc);
384 if (ret < 0) {
385 msg_perr("Failed to get device descriptor: '%s'\n", libusb_error_name(ret));
386 goto error_exit;
387 }
388
389 msg_pdbg("Device revision is %d.%01d.%01d\n",
390 (desc.bcdDevice >> 8) & 0x00FF,
391 (desc.bcdDevice >> 4) & 0x000F,
392 (desc.bcdDevice >> 0) & 0x000F);
393
Nico Huberc32e9542023-02-21 00:46:37 +0000394 /* TODO: add programmer cfg for things like CS pin */
Nicholas Chindac42392024-07-30 20:01:59 -0600395 if (ch347_spi_config(ch347_data, div, mode) < 0)
Nicholas Chin197b7c72022-10-23 13:10:31 -0600396 goto error_exit;
397
Nico Huber89569d62023-01-12 23:31:40 +0100398 return register_spi_master(&spi_master_ch347_spi, 0, ch347_data);
Nicholas Chin197b7c72022-10-23 13:10:31 -0600399
400error_exit:
401 ch347_spi_shutdown(ch347_data);
402 return 1;
403}
404
405const struct programmer_entry programmer_ch347_spi = {
406 .name = "ch347_spi",
407 .type = USB,
408 .devs.dev = devs_ch347_spi,
409 .init = ch347_spi_init,
410};