aboutsummaryrefslogtreecommitdiff
path: root/inc/ch375.h
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2026-10-26 00:50:06 +0100
committerJavier <dev.git@javispedro.com>2026-10-26 00:50:06 +0100
commit6b8a368c51082fdd010ef5e1b0be4eb9b5059d1a (patch)
treee274d41a8dd5e912e20617a937a1fda5af91f8de /inc/ch375.h
parent9b1cf601360fb194047e6dcc2af75c2f38c611ec (diff)
downloadchu-6b8a368c51082fdd010ef5e1b0be4eb9b5059d1a.tar.gz
chu-6b8a368c51082fdd010ef5e1b0be4eb9b5059d1a.zip
refactor common parts into inc/ch375.h and add win9x, linux drivers
Diffstat (limited to 'inc/ch375.h')
-rw-r--r--inc/ch375.h361
1 files changed, 361 insertions, 0 deletions
diff --git a/inc/ch375.h b/inc/ch375.h
new file mode 100644
index 0000000..5ce90a2
--- /dev/null
+++ b/inc/ch375.h
@@ -0,0 +1,361 @@
+/*
+ * CH375 hardware header
+ * Copyright (C) 2024 Javier S. Pedro
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef CH375_H
+#define CH375_H
+
+#include "usb.h"
+
+/** Standard defines */
+
+struct ch375;
+
+enum {
+ CH375_PORT_DATA = 0,
+ CH375_PORT_CMD = 1,
+};
+
+enum ch375_command {
+ /** Returns chip and firmware version in bit 5-0.
+ * output: 1byte.
+ * C375B is 0x37. */
+ CH375_GET_IC_VERSION = 0x01,
+ /** (Host mode) Set serial port baud rate. */
+ CH375_SET_BAUDRATE = 0x02,
+ /** Enter low power mode. Wakes up with USB or host activity. */
+ CH375_ENTER_SLEEP = 0x03,
+ /** Manualy force USB speed.
+ * Input: 1 byte:
+ * 0: Full speed
+ * 1: Full speed with low speed clock frequency (??)
+ * 2: Low speed */
+ CH375_SET_USB_SPEED = 0x04,
+ /** Hardware reset. May take up to 40ms. */
+ CH375_RESET = 0x05,
+ /** Simple eco function. Returns input byte negated.
+ * Input: 1 byte: x
+ * Output: 1 byte: ~x */
+ CH375_CHECK_EXIST = 0x06,
+
+ CH375_READ_REG = 0x0A,
+ CH375_WRITE_REG = 0x0B,
+ /** Sets a 100us timer.
+ * Output: infinite bytes. 0 while timer not expired, non-0 when expired. */
+ CH375_DELAY_100US = 0x0F,
+ /** (Device mode) set our USB vid and PID.
+ * Input: 4 bytes: VID LSB, VID MSB, PID LSB, PID MSB */
+ CH375_SET_USB_ID = 0x12,
+ /** (Device mode) set USB address. ?? */
+ CH375_SET_USB_ADDR = 0x13,
+ /** Set USB mode. Usually takes 20µs.
+ * Input: 1 byte:
+ * 0 : Device mode disabled (i.e. device plug-in detection)
+ * 1 : Device mode with external firmware
+ * 2 : Device mode with built-in firmware
+ * 4 : Host mode disabled (i.e. device plug-in detection)
+ * 5 : Host mode without automatic SOF packets.
+ * 6 : Host mode with automatic SOF packets.
+ * 7 : Host mode, initiate bus reset
+ * Output: 1 byte:
+ * CMD_RET_SUCCESS if OK
+ * CMD_RET_ABORT, etc. if error
+ */
+ CH375_SET_USB_MODE = 0x15,
+ /** (Host mode) Check the connection status.
+ * Output: 1 byte: either USB_INT_CONNECT or USB_INT_DISCONNECT or USB_INT_USB_READY. */
+ CH375_TEST_CONNECT = 0x16,
+ /** Abandon retrying and return NAK. */
+ CH375_ABORT_NAK = 0x17,
+
+ CH375_SET_ENDP2 = 0x18,
+ CH375_SET_ENDP3 = 0x19,
+ CH375_SET_ENDP4 = 0x1A,
+ CH375_SET_ENDP5 = 0x1B,
+ CH375_SET_ENDP6 = 0x1C,
+ CH375_SET_ENDP7 = 0x1D,
+
+ /** Get interrupt status and ack pending interrupts.
+ * Output: 1 byte */
+ CH375_GET_INT_STATUS = 0x22,
+ /** (Device mode) Release current USB buffer. */
+ CH375_UNLOCK_USB = 0x23,
+ /** Read buffer of endpoint of current interrupt.
+ * Output: length byte + data. */
+ CH375_RD_USB_DATA0 = 0x27,
+ /** Equivalent to RD_USB_DATA0 + UNLOCK_USB. */
+ CH375_RD_USB_DATA = 0x28,
+ CH375_WR_USB_DATA3 = 0x29,
+ CH375_WR_USB_DATA5 = 0x2A,
+ CH375_WR_USB_DATA7 = 0x2B,
+ /** (Host mode) Clear error state on endpoint.
+ * Input: 1 byte: endpoint number.
+ * On done: Triggers interrupt. */
+ CH375_CLR_STALL = 0x41,
+ /** (Host mode) Set USB address.
+ * Input: USB address.
+ * On done: Triggers interrupt. */
+ CH375_SET_ADDRESS = 0x45,
+ CH375_GET_DESCR = 0x46,
+ CH375_SET_CONFIG = 0x49,
+ CH375_AUTO_SETUP = 0x4D,
+ CH375_ISSUE_TKN_X = 0x4E,
+ CH375_ISSUE_TOKEN = 0x4F,
+
+ CH375_DISK_BOC = 0x50,
+ CH375_DISK_INIT = 0x51,
+ CH375_DISK_RESET = 0x52,
+ CH375_DISK_SIZE = 0x53,
+ CH375_DISK_READ = 0x54,
+ CH375_DISK_RD_GO = 0x55,
+ CH375_DISK_WRITE = 0x56,
+ CH375_DISK_WR_GO = 0x57,
+ CH375_DISK_INQUIRY = 0x58,
+ CH375_DISK_READY = 0x59,
+ CH375_DISK_R_SENSE = 0x5A,
+};
+
+enum ch375_cmd_ret {
+ CH375_RET_SUCESS = 0x51,
+ CH375_RET_ABORT = 0x5F
+};
+
+enum ch375_int {
+ /** if (x & mask) == val, this is a USB device mode interrupt. */
+ CH375_INT_DEVICE_MASK = 0xF0,
+ CH375_INT_DEVICE_VAL = 0x00,
+
+ CH375_INT_HOST_MASK = 0xE0,
+ CH375_INT_HOST_VAL = 0x20,
+
+ CH375_INT_BUS_RESET1 = 0x03,
+ CH375_INT_BUS_RESET2 = 0x07,
+ CH375_INT_BUS_RESET3 = 0x0B,
+ CH375_INT_BUS_RESET4 = 0x0F,
+
+ /** USB bus suspend event. */
+ CH375_INT_USB_SUSPEND = 0x05,
+ /** Wakeup from USB suspend. */
+ CH375_INT_USB_WAKE_UP = 0x06,
+ CH375_INT_USB_SUCCESS = 0x14,
+ CH375_INT_USB_CONNECT = 0x15,
+ CH375_INT_USB_DISCONNECT = 0x16,
+ CH375_INT_USB_BUF_OVER = 0x17,
+ CH375_INT_USB_READY = 0x18,
+ CH375_INT_USB_DISK_READ = 0x1D,
+ CH375_INT_USB_DISK_WRITE = 0x1E,
+ CH375_INT_USB_DISK_ERR = 0x1F,
+};
+
+enum CH375_REG {
+ /** (Host mode) Get data rate of current USB device.
+ * Bit 4: if 1= Low Speed. if 0 = Full Speed.*/
+ CH375_REG_DEV_RATE = 0x0A,
+ /** (Device mode): how to check USB bus suspend status.
+ * 0: Do not check
+ * 4: Check at 50ms intervals
+ * 5: Check at 10ms intervals */
+ CH375_REG_CHK_SUSPEND = 0x10,
+ CH375_REG_TOGGLE = 0x1A,
+
+ /** ???
+ * Bit 5 : set after disk is online? but never unset... */
+ CH375_REG_DISK_STATUS = 0x20,
+ /** (Host mode) Set the number of retries for USB transactions.
+ * If bit 7 is 0, no retry is performed when NAK is received.
+ * If bit 7 is 1 and bit 6 is 0, unlimited retries are performed when NAK is received.
+ * If bit 7 is 1 and bit 6 is 1, the maximum retry is 2 seconds when NAK is received.
+ * Bits 5-0 are the number of retries after timeout. */
+ CH375_REG_RETRY = 0x25,
+ /** (Host disk mode) Set the current LUN. */
+ CH375_REG_DISK_LUN = 0x34,
+ CH375_REG_DISK_MAX_LUN = 0x38,
+ /** (Host disk mode) Set the total number of 64-byte packets per sector.
+ * E.g. 8 = 512 byte sectors. */
+ CH375_REG_DISK_PKT_P_SEC = 0x39,
+ /// Drive letter (in ASCII) currently assigned. This is set by software.
+ CH375_REG_DISK_NAME = 0x3E
+
+ // 0x1B what's on this register ? 0x7F , 0x80 values seen
+ // 0x20 ?? some type of status ?
+ // 0x31, 0x32, 0x33 ??
+
+};
+
+#define CH375_BLOCK_SIZE 64
+
+// HELPER ROUTINES
+
+/* You have to define these routines for the helper functions to work. */
+
+static inline uint8_t ch375_read_data(struct ch375 *dev);
+static inline void ch375_write_data(struct ch375 *dev, uint8_t data);
+
+static inline void ch375_send_command(struct ch375 *dev, enum ch375_command cmd);
+static inline bool ch375_poll_interrupt(struct ch375 *dev);
+
+// GLOBAL COMMANDS
+
+static inline uint8_t ch375_get_version(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_GET_IC_VERSION);
+ return ch375_read_data(dev) & 0x3F;
+}
+
+static inline void ch375_enter_sleep(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_ENTER_SLEEP);
+}
+
+static inline void ch375_reset(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_RESET);
+}
+
+static inline uint8_t ch375_check_exists(struct ch375 *dev, uint8_t val)
+{
+ ch375_send_command(dev, CH375_CHECK_EXIST);
+ ch375_write_data(dev, val);
+ return ch375_read_data(dev);
+}
+
+static inline uint8_t ch375_get_int_status(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_GET_INT_STATUS);
+ return ch375_read_data(dev);
+}
+
+static uint8_t ch375_read_reg(struct ch375 *dev, enum CH375_REG reg)
+{
+ ch375_send_command(dev, CH375_READ_REG);
+ ch375_write_data(dev, reg);
+ return ch375_read_data(dev);
+}
+
+static void ch375_write_reg(struct ch375 *dev, enum CH375_REG reg, uint8_t value)
+{
+ ch375_send_command(dev, CH375_WRITE_REG);
+ ch375_write_data(dev, reg);
+ ch375_write_data(dev, value);
+}
+
+enum ch375_usb_mode {
+ CH375_USB_MODE_DEVICE_DISABLED = 0,
+ CH375_USB_MODE_DEVICE_EXTERNAL_FIRMWARE = 1,
+ CH375_USB_MODE_DEVICE_INTERNAL_FIRMWARE = 2,
+ CH375_USB_MODE_HOST_DISABLED = 4,
+ CH375_USB_MODE_HOST = 5,
+ CH375_USB_MODE_HOST_AUTO_SOF = 6,
+ CH375_USB_MODE_HOST_RESET = 7
+};
+
+static void ch375_set_usb_mode(struct ch375 *dev, enum ch375_usb_mode mode) {
+ ch375_send_command(dev, CH375_SET_USB_MODE);
+ ch375_write_data(dev, mode);
+}
+
+enum ch375_usb_speed {
+ CH375_USB_SPEED_FULL = 0,
+ CH375_USB_SPEED_LOW = 2
+};
+
+// Seems to be for CH375B only. See below for CH375A alternative.
+static void ch375_set_usb_speed(struct ch375 *dev, enum ch375_usb_speed speed) {
+ ch375_send_command(dev, CH375_SET_USB_SPEED);
+ ch375_write_data(dev, speed);
+}
+
+static void ch375a_set_low_speed(struct ch375 *dev) {
+ // Undocumented.
+ ch375_write_reg(dev, 0x17, 0xd8);
+}
+
+// HOST COMMANDS
+
+static void ch375_test_connect(struct ch375 *dev) {
+ ch375_send_command(dev, CH375_TEST_CONNECT);
+}
+
+static void ch375_auto_setup(struct ch375 *dev) {
+ ch375_send_command(dev, CH375_AUTO_SETUP);
+}
+
+enum ch375_retry_mode {
+ CH375_RETRY_NAK_INFINITE = 0x80,
+ CH375_RETRY_NAK_2S = 0xC0,
+ CH375_RETRY_NAK_NONE = 0x00,
+
+ CH375_RETRY_TIMEOUT_MAX = 0x1F
+};
+
+static void ch375_set_retry(struct ch375 *dev, enum ch375_retry_mode mode) {
+ ch375_write_reg(dev, CH375_REG_RETRY, mode);
+}
+
+static void ch375_get_descriptor(struct ch375 *dev, usb_descriptor_type type) {
+ ch375_send_command(dev, CH375_GET_DESCR);
+ ch375_write_data(dev, type);
+}
+
+// DISK COMMANDS
+
+enum ch375_reg_disk_status {
+ CH375_REG_DISK_STATUS_READY = 0x20
+};
+
+static inline bool ch375_is_disk_ready(struct ch375 *dev)
+{
+ return ch375_read_reg(dev, CH375_REG_DISK_STATUS) & CH375_REG_DISK_STATUS_READY;
+}
+
+static void ch375_disk_init(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_DISK_INIT);
+}
+
+static void ch375_disk_read(struct ch375 *dev, uint32_t lba, uint8_t nsectors)
+{
+ ch375_send_command(dev, CH375_DISK_READ);
+ ch375_write_data(dev, (lba >> 0) & 0xFF);
+ ch375_write_data(dev, (lba >> 8) & 0xFF);
+ ch375_write_data(dev, (lba >> 16) & 0xFF);
+ ch375_write_data(dev, (lba >> 24) & 0xFF);
+ ch375_write_data(dev, nsectors);
+}
+
+static void ch375_disk_write(struct ch375 *dev, uint32_t lba, uint8_t nsectors)
+{
+ ch375_send_command(dev, CH375_DISK_WRITE);
+ ch375_write_data(dev, (lba >> 0) & 0xFF);
+ ch375_write_data(dev, (lba >> 8) & 0xFF);
+ ch375_write_data(dev, (lba >> 16) & 0xFF);
+ ch375_write_data(dev, (lba >> 24) & 0xFF);
+ ch375_write_data(dev, nsectors);
+}
+
+static void ch375_test_disk_ready(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_DISK_READY);
+}
+
+static void ch375_disk_r_sense(struct ch375 *dev)
+{
+ ch375_send_command(dev, CH375_DISK_R_SENSE);
+}
+
+#endif