diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d90124 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/__pycache__/ +FlashGBX/config/*bak \ No newline at end of file diff --git a/FlashGBX/FlashGBX_CLI.py b/FlashGBX/FlashGBX_CLI.py index 3c0c4b9..bfb87d2 100644 --- a/FlashGBX/FlashGBX_CLI.py +++ b/FlashGBX/FlashGBX_CLI.py @@ -16,8 +16,8 @@ from .PocketCamera import PocketCamera from .Util import APPNAME, ANSI from . import Util -from . import hw_GBxCartRW, hw_GBFlash, hw_JoeyJr -hw_devices = [hw_GBxCartRW, hw_GBFlash, hw_JoeyJr] +from . import hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr +hw_devices = [hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr] class FlashGBX_CLI(): ARGS = {} diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py index 003953e..ce51fb6 100644 --- a/FlashGBX/FlashGBX_GUI.py +++ b/FlashGBX/FlashGBX_GUI.py @@ -12,8 +12,8 @@ from .UserInputDialog import UserInputDialog from .Util import APPNAME, VERSION, VERSION_PEP440 from . import Util -from . import hw_GBxCartRW, hw_GBFlash, hw_JoeyJr -hw_devices = [hw_GBxCartRW, hw_GBFlash, hw_JoeyJr] +from . import hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr +hw_devices = [hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr] class FlashGBX_GUI(QtWidgets.QWidget): CONN = None diff --git a/FlashGBX/LK_Device.py b/FlashGBX/LK_Device.py index 5473304..9ca9947 100644 --- a/FlashGBX/LK_Device.py +++ b/FlashGBX/LK_Device.py @@ -626,7 +626,7 @@ def _cart_write_flash(self, commands, flashcart=False): buffer.extend(struct.pack("B", 1 if flashcart else 0)) buffer.extend(struct.pack("B", num)) for i in range(0, num): - dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d})".format(commands[i][0], commands[i][1], i+1, num)) + dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d}) flashcart={:s}".format(commands[i][0], commands[i][1], i+1, num, str(flashcart))) if self.MODE == "AGB" and flashcart: buffer.extend(struct.pack(">I", commands[i][0] >> 1)) else: @@ -807,8 +807,14 @@ def SetVarState(self, var_state): self._write(self.DEVICE_CMD["SET_VAR_STATE"]) time.sleep(0.2) self.DEVICE.write(var_state) - + def GetMode(self): + mode = self._getMode() + if mode is None: + return self._getMode() + return mode + + def _getMode(self): if time.time() < self.LAST_CHECK_ACTIVE + 1: return self.MODE if self.CheckActive() is False: return None if self.MODE is None: return None @@ -919,6 +925,7 @@ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True): print("{:s}Error: No mode was set.{:s}".format(ANSI.RED, ANSI.RESET)) return False + dprint("Reading ROM header") header = self.ReadROM(0, 0x180) if ".dev" in Util.VERSION_PEP440 or Util.DEBUG: @@ -1036,7 +1043,7 @@ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True): data["rtc_string"] = "Not available" if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0: _agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle) - if self.FW["fw_ver"] >= 12: + if self.FW["fw_ver"] >= 12 and self.DEVICE_NAME != "Bacon": # Bacon has a different RTC implementation self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"]) temp = self._read(8) data["has_rtc"] = _agb_gpio.HasRTC(temp) is True @@ -1424,6 +1431,8 @@ def ReadFlashSaveID(self): return (agb_flash_chip, agb_flash_chip_name) def ReadROM(self, address, length, skip_init=False, max_length=64): + if self.DEVICE_NAME == "Bacon": + max_length = length num = math.ceil(length / max_length) dprint("Reading 0x{:X} bytes from cartridge ROM at 0x{:X} in {:d} iteration(s)".format(length, address, num)) if length > max_length: length = max_length @@ -1491,6 +1500,8 @@ def ReadROM_GBAMP(self, address, length, max_length=64): return self.ReadROM(address=addr, length=length, max_length=max_length) def ReadRAM(self, address, length, command=None, max_length=64): + if self.DEVICE_NAME == "Bacon": + max_length = length num = math.ceil(length / max_length) dprint("Reading 0x{:X} bytes from cartridge RAM in {:d} iteration(s)".format(length, num)) if length > max_length: length = max_length @@ -1577,6 +1588,8 @@ def ReadRAM_TAMA5(self): def WriteRAM(self, address, buffer, command=None, max_length=256): length = len(buffer) + if self.DEVICE_NAME == "Bacon": + max_length = length num = math.ceil(length / max_length) dprint("Writing 0x{:X} bytes to cartridge RAM in {:d} iteration(s)".format(length, num)) if length > max_length: length = max_length @@ -1702,8 +1715,10 @@ def WriteRAM_TAMA5(self, buffer): def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False, max_length=MAX_BUFFER_WRITE): length = len(buffer) + if self.DEVICE_NAME == "Bacon": + max_length = length num = math.ceil(length / max_length) - dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num)) + dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s) flash_buffer_size=0x{:X} skip_init={:s}".format(length, num, flash_buffer_size, str(skip_init))) if length == 0: dprint("Length is zero?") return False @@ -3401,7 +3416,7 @@ def _BackupRestoreRAM(self, args): elif self.MODE == "AGB": _agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle) rtc_buffer = None - if self.FW["fw_ver"] >= 12: + if self.FW["fw_ver"] >= 12 and self.DEVICE_NAME != "Bacon": # Bacon has a different RTC implementation self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"]) rtc_buffer = self._read(8) if len(rtc_buffer) == 8 and _agb_gpio.HasRTC(rtc_buffer) is True: @@ -4287,6 +4302,8 @@ def _FlashROM(self, args): sector_size = se_ret dprint("Next sector size: 0x{:X}".format(sector_size)) skip_init = False + if "Bacon" in self.DEVICE_NAME: + self.DEVICE.cache_rom(pos, [0xFF]*buffer_len) # ↑↑↑ Sector erase if se_ret is not False: @@ -4646,7 +4663,12 @@ def TransferData(self, args, signal): if args['mode'] == 1: ret = self._BackupROM(args) elif args['mode'] == 2: ret = self._BackupRestoreRAM(args) elif args['mode'] == 3: ret = self._BackupRestoreRAM(args) - elif args['mode'] == 4: ret = self._FlashROM(args) + elif args['mode'] == 4: + if "Bacon" in self.DEVICE_NAME: + self.DEVICE.cache_rom_reset() + ret = self._FlashROM(args) + if "Bacon" in self.DEVICE_NAME: + self.DEVICE.cache_rom_reset() elif args['mode'] == 5: ret = self._DetectCartridge(args) elif args['mode'] == 0xFF: self.Debug() if self.FW is None: return False diff --git a/FlashGBX/bacon/.gitignore b/FlashGBX/bacon/.gitignore new file mode 100644 index 0000000..c8fdf1b --- /dev/null +++ b/FlashGBX/bacon/.gitignore @@ -0,0 +1,2 @@ +**/__pycache__/ +gba*.bin \ No newline at end of file diff --git a/FlashGBX/bacon/__init__.py b/FlashGBX/bacon/__init__.py new file mode 100644 index 0000000..202c16d --- /dev/null +++ b/FlashGBX/bacon/__init__.py @@ -0,0 +1,55 @@ +""" +CH347 Module +------------ + +The `ch347` module provides functions for interacting with the CH347 USB-UART/SPI/I2C/JTAG bridge. + +Initialization: +---------------- + +- Use `ch347.init()` to initialize the CH347 device. + +Configuration: +-------------- + +- Use `ch347.set_spi_config(clock: int, mode: int)` to configure the SPI parameters. + +SPI Communication: +------------------ + +- Use `ch347.spi_write(device_index: int, chip_select: int, write_data: bytes, write_step: int)` + to write data to the SPI bus. + +- Use `ch347.spi_read(device_index: int, chip_select: int, write_data: bytes, read_length: int)` + to read data from the SPI bus. + +Closing: +-------- + +- Use `ch347.close()` to close the CH347 device. + +Usage Example: +-------------- + +import ch347 + +# Initialize the CH347 device +ch347.init() + +# Configure the SPI parameters +ch347.set_spi_config(clock=1000000, mode=0) + +# Write data to the SPI bus +ch347.spi_write(device_index=0, chip_select=1, write_data=b'\x01\x02\x03', write_step=3) + +# Read data from the SPI bus +data = ch347.spi_read(device_index=0, chip_select=1, write_data=b'\x00', read_length=3) + +# Close the CH347 device +ch347.close() +""" + +from .ch347 import * +from .bacon import * +from .command import * +from .serial import * \ No newline at end of file diff --git a/FlashGBX/bacon/bacon.py b/FlashGBX/bacon/bacon.py new file mode 100644 index 0000000..d87e68e --- /dev/null +++ b/FlashGBX/bacon/bacon.py @@ -0,0 +1,452 @@ +# -*- coding: utf-8 -*- +# bacon +# Author: ChisBread (github.com/ChisBread) + +import platform +import random +import time +import os + +from .command import * + + +ROM_MAX_SIZE = 0x2000000 # 32MB +RAM_MAX_SIZE = 0x20000 # 128KB (with bank) + +class BaconWritePipeline: + def __init__(self, dev, flush_func): + self.cmds = "" + self.dev = dev + self.flush_func = flush_func + self.MAX_LEN = 0x1000 + + def WillFlush(self, cmd: str): + if (len(self.cmds) + 1 + len(cmd))//8 >= self.MAX_LEN: + return True + return False + + def Write(self, cmd: str): + if (len(self.cmds) + 1 + len(cmd))//8 >= self.MAX_LEN: + self.flush_func(command2bytes(self.cmds)) + self.cmds = "" + if self.cmds: + self.cmds += "0" + self.cmds += cmd + return self + + def IsEmpty(self): + return len(self.cmds) == 0 + + def Flush(self): + if self.cmds: + self.flush_func(command2bytes(self.cmds)) + self.cmds = "" + return self + + + + +class BaconDevice: + GPIO_REG_DAT = 0xC4 # Data low 4bit + GPIO_REG_CNT = 0xC6 # IO Select 1:Write to GPIO Device 0:Read from GPIO Device + GPIO_REG_RE = 0xC8 # Read Enable Flag Register 1:Enable 0:Disable + + def __init__(self, hid_device=None, ch347_device=None, gpio_device=None): + self.hid_device = hid_device + self.ch347_device = ch347_device + self.gpio_device = gpio_device + self.power = 0 + self.MAX_LEN = 0x1000 + # if all device is None, find a device + ## check if windows + if self.ch347_device is None and self.hid_device is None and self.gpio_device is None: + if platform.system() == "Windows": + if self.ch347_device is None: + #WCHAPI + from .ch347 import SPIConfig, CH347 + ch347 = CH347(device_index=0, dll_path=os.path.join(os.path.dirname(__file__), "lib/CH347DLLA64.DLL")) + ch347.close_device() + ch347.open_device() + spi_config = SPIConfig( + Mode=3, + Clock=0, + ByteOrder=1, + SpiWriteReadInterval=0, + SpiOutDefaultData=0xFF, + ChipSelect=0x80, + CS1Polarity=0, + CS2Polarity=0, + IsAutoDeative=1, + ActiveDelay=0, + DelayDeactive=0, + ) + if ch347.spi_init(spi_config): # True if successful, False otherwise. + self.ch347_device = ch347 + # cannot find wch device, try hid device + if self.ch347_device is None: + try: + #HIDAPI + from ch347api import SPIDevice, SPIClockFreq + self.hid_device = SPIDevice(clock_freq_level=SPIClockFreq.f_60M, is_16bits=False, mode=3, is_MSB=True) + except Exception as e: + print(e) + elif platform.system() == "Linux" and os.path.exists("/dev/spidev3.0"): + try: + import spidev + spi = spidev.SpiDev() + spi.open(3, 0) # bus 3, device 0 + spi.max_speed_hz = 60000000 + spi.mode = 0b11 + spi.bits_per_word = 8 + spi.lsbfirst = False + spi.cshigh = False + self.gpio_device = spi + except Exception as e: + print(e) + + + if self.ch347_device is None and self.hid_device is None and self.gpio_device is None: + raise ValueError("No device found") + self.pipeline = BaconWritePipeline(self, self.WriteRead) + self.pipeline.MAX_LEN = self.MAX_LEN + +######## Low Level API ######## + def Close(self): + if self.gpio_device is not None: + self.gpio_device.close() + if self.ch347_device is not None: + self.ch347_device.close_device() + if self.hid_device is not None: + self.hid_device.dev.close() + self.ch347_device = None + self.hid_device = None + self.gpio_device = None + + def Write(self, data: bytes) -> bool: + if len(data) > self.MAX_LEN: + raise ValueError("Data length must be less than 0x%04X" % self.MAX_LEN) + if self.gpio_device is not None: + self.gpio_device.xfer(data) + return True + if self.ch347_device is not None: + return self.ch347_device.spi_write(0x80, data) + if self.hid_device is not None: + return self.hid_device.write_CS1(data) > 0 + raise ValueError("No device found") + + def WriteRead(self, data: bytes) -> bytes: + if len(data) > self.MAX_LEN: + raise ValueError("Data length must be less than 0x%04X" % self.MAX_LEN) + if self.gpio_device is not None: + return bytes(self.gpio_device.xfer(data)) + if self.ch347_device is not None: + return bytes(self.ch347_device.spi_write_read(0x80, data)) + if self.hid_device is not None: + return bytes(self.hid_device.writeRead_CS1(data)) + raise ValueError("No device found") + + def PiplelineFlush(self): + return self.pipeline.Flush() + + def ResetAndDelayNS(self, ns: int, v16bit=b"\x00\x00", v8bit=b"\x00", phi=False, req=False, wr=True, rd=True, cs1=True, cs2=True) -> BaconWritePipeline: + # 1clk=16.67ns + # 1reset=35clk + for i in range(ns//int(16.6*35)+1): + if self.pipeline.Write(make_cart_30bit_write_command( + phi=phi, req=req, wr=wr, rd=rd, cs1=cs1, cs2=cs2, + v16bit=v16bit, v8bit=v8bit, postfunc=echo_all + )).IsEmpty(): + return self.pipeline + return self.pipeline + + def PowerControl(self, v3_3v: bool, v5v: bool) -> BaconWritePipeline: + if v3_3v and v5v: + raise ValueError("v3_3v and v5v can't be both enabled") + if v3_3v: + self.power = 3 + elif v5v: + self.power = 5 + else: + self.power = 0 + # return self.WriteRead(make_power_control_command(not v3_3v, v5v)) + return self.pipeline.Write(make_power_control_command(not v3_3v, v5v, postfunc=echo_all)) + +######## High Level API ######## + + def AGBReadROM(self, addr: int, size: int, reset=True, callback=None) -> bytes: + if size % 2 != 0: + raise ValueError("Size must be a multiple of 2") + if addr % 2 != 0: + raise ValueError("Address must be a multiple of 2") + if addr + size > ROM_MAX_SIZE: + #raise ValueError("Address + Size must be less than 0x2000000 address:0x%08X size:0x%08X" % (addr, size)) + pass + if self.power != 3: + raise ValueError("Power must be 3.3v") + # prepare chip + ## to halfword + addr = addr // 2 + size = size // 2 + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]), postfunc=echo_all + )) + self.pipeline.Write(make_gba_rom_cs_write(cs=False, postfunc=echo_all)).Flush() + lowaddr = addr & 0xFFFF + highaddr = (addr >> 16) & 0xFF + # if lowaddr+1 == 0x10000, highaddr+1, and reset lowaddr + # prepare WriteRead stream + readbytes = [] + cycle_times = 0 + MAX_TIMES = self.MAX_LEN//(len(make_rom_read_cycle_command(times=1))+1) + cnt = 0 + for i in range(size): + cycle_times += 1 + lowaddr += 1 + cnt += 2 + if callback is not None and cnt != size*2: + callback(cnt, readbytes) + if lowaddr == 0x10000: + if cycle_times > 0: + ret = self.WriteRead(make_rom_read_cycle_command(times=cycle_times)) + exteds = extract_read_cycle_data(ret, times=cycle_times) + for exted in exteds: + readbytes.append(exted >> 8) + readbytes.append(exted & 0xFF) + cycle_times = 0 + highaddr += 1 + lowaddr = 0 + if highaddr <= 0xFF: + # cs1 re-falling? + self.WriteRead(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=False, cs2=True, + v16bit=b"\x00\x00", v8bit=bytes([highaddr]) + )) + if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0: + ret = self.WriteRead(make_rom_read_cycle_command(times=cycle_times)) + exteds = extract_read_cycle_data(ret, times=cycle_times) + for exted in exteds: + readbytes.append(exted >> 8) + readbytes.append(exted & 0xFF) + cycle_times = 0 + if callback is not None: + callback(cnt, readbytes) + # reset chip + if reset: + self.WriteRead(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00" + )) + return bytes(readbytes) + + def ResetChip(self) -> BaconWritePipeline: + if self.power != 3: + raise ValueError("Power must be 3.3v") + return self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all + )) + + def AGBWriteROMSequential(self, addr, data: bytes, reset=True, callback=None) -> BaconWritePipeline: + if addr % 2 != 0: + raise ValueError("Address must be a multiple of 2") + if len(data) % 2 != 0: + raise ValueError("Data length must be a multiple of 2") + if self.power != 3: + raise ValueError("Power must be 3.3v") + addr = addr // 2 + # prepare chip + self.pipeline.Write( + make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]), + postfunc=echo_all + ) + ) + self.pipeline.Write(make_gba_rom_cs_write(cs=False, postfunc=echo_all)) + lowaddr = addr & 0xFFFF + highaddr = (addr >> 16) & 0xFF + cnt = 0 + for i in range(0, len(data), 2): + self.pipeline.Write(make_rom_write_cycle_command_sequential(datalist=[int.from_bytes(data[i:i+2], byteorder='little')], postfunc=echo_all)) + lowaddr += 1 + cnt += 2 + if callback is not None and cnt != len(data): + callback(cnt, None) + if lowaddr == 0x10000: + highaddr += 1 + lowaddr = 0 + if highaddr <= 0xFF: + # cs1 re-falling? + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=False, cs2=True, + v16bit=b"\x00\x00", v8bit=bytes([highaddr]), postfunc=echo_all + )) + if callback is not None: + callback(cnt, None) + # reset chip. + if reset: + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all + )) + return self.pipeline + + def AGBWriteROMWithAddress(self, commands: list, callback=None) -> BaconWritePipeline: + if self.power != 3: + raise ValueError("Power must be 3.3v") + cnt = 0 + # addr这里使用绝对地址,需要//2 + # 如果commands中的data为bytes,需要转换为int(小端) + for i in range(len(commands)): + if type(commands[i][1]) == bytes: + commands[i] = (commands[i][0]//2, int.from_bytes(commands[i][1], byteorder='little')) + else: + commands[i] = (commands[i][0]//2, commands[i][1]) + for i in range(len(commands)): + self.pipeline.Write(make_rom_write_cycle_command_with_addr(addrdatalist=[commands[i]], postfunc=echo_all)) + cnt += 1 + if callback is not None and cnt != len(commands): + callback(cnt, None) + if callback is not None: + callback(cnt, None) + # reset chip + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all + )) + return self.pipeline + + def AGBReadRAM(self, addr: int, size: int, bankswitch=None, callback=None) -> bytes: + if addr + size > RAM_MAX_SIZE: + raise ValueError("Address + Size must be less than 0x20000") + if addr + size > RAM_MAX_SIZE/2 and bankswitch is None: + raise ValueError("Address + Size must be less than 0x10000 or bankswitch must be provided address:0x%08X size:0x%08X" % (addr, size)) + if self.power != 3: + raise ValueError("Power must be 3.3v") + # prepare chip + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=False, + cs1=True, cs2=False, + v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=b"\x00", postfunc=echo_all + )).Flush() + readbytes = [] + cycle_times = 0 + MAX_TIMES = self.MAX_LEN//(len(make_ram_read_cycle_command(addr=0, times=1))+1) + cnt = 0 + start_addr = addr + for i in range(size): + cycle_times += 1 + cnt += 1 + if callback is not None and cnt != size: + callback(cnt, readbytes) + if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0: + ret = self.WriteRead(make_ram_read_cycle_command(addr=start_addr, times=cycle_times)) + exteds = extract_ram_read_cycle_data(ret, times=cycle_times) + for exted in exteds: + readbytes.append(exted) + start_addr += cycle_times + cycle_times = 0 + if callback is not None: + callback(cnt, readbytes) + # reset chip + self.WriteRead(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00" + )) + return bytes(readbytes) + + def AGBWriteRAM(self, addr: int, data: bytes, bankswitch=None, callback=None) -> bool: + size = len(data) + if addr + size > RAM_MAX_SIZE: + raise ValueError("Address + Size must be less than 0x20000") + if addr + size > RAM_MAX_SIZE/2 and bankswitch is None: + raise ValueError("Address + Size must be less than 0x10000 or bankswitch must be provided address:0x%08X size:0x%08X" % (addr, size)) + if self.power != 3: + raise ValueError("Power must be 3.3v") + readbytes = [] + cycle_times = 0 + MAX_TIMES = self.MAX_LEN//(len(make_ram_write_cycle_command(addr=0, data=b"\00"))+1) + cnt = 0 + start_addr = addr + for i in range(size): + cycle_times += 1 + cnt += 1 + if callback is not None and cnt != size: + callback(cnt, readbytes) + if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0: + ret = self.WriteRead(make_ram_write_cycle_command(addr=start_addr, data=data[start_addr-addr:start_addr-addr+cycle_times])) + readbytes = readbytes + ([0]*cycle_times) + start_addr += cycle_times + cycle_times = 0 + if callback is not None: + callback(cnt, readbytes) + # reset chip + self.WriteRead(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00" + )) + return bytes(readbytes) + + def AGBWriteRAMWithAddress(self, commands: list, reset=True, callback=None) -> BaconWritePipeline: + if self.power != 3: + raise ValueError("Power must be 3.3v") + cnt = 0 + for i in range(len(commands)): + self.pipeline.Write(make_ram_write_cycle_with_addr(addrdatalist=[commands[i]], postfunc=echo_all)) + cnt += 1 + if callback is not None and cnt != len(commands): + callback(cnt, None) + if callback is not None: + callback(cnt, None) + # reset chip + if reset: + self.pipeline.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all + )) + return self.pipeline + + def AGBCustomWriteCommands(self, commands: list, callback=None) -> bool: + pass + + def AGBGPIOEnable(self) -> BaconWritePipeline: + if self.power != 3: + raise ValueError("Power must be 3.3v") + return self.AGBWriteROMSequential(self.GPIO_REG_RE, b"\x01") + + def AGBGPIOSetDirection(self, direction: int) -> BaconWritePipeline: + if self.power != 3: + raise ValueError("Power must be 3.3v") + return self.AGBWriteROMSequential(self.GPIO_REG_CNT, bytes([direction])) + + def AGBGPIORead(self) -> int: + if self.power != 3: + raise ValueError("Power must be 3.3v") + return self.AGBReadROM(self.GPIO_REG_DAT, 1)[0] + + def DMGReadROM(self, addr: int, size: int, reset=True, callback=None) -> bytes: + pass \ No newline at end of file diff --git a/FlashGBX/bacon/ch347.py b/FlashGBX/bacon/ch347.py new file mode 100644 index 0000000..6b9fbc5 --- /dev/null +++ b/FlashGBX/bacon/ch347.py @@ -0,0 +1,665 @@ +import ctypes +from typing import List + + +class DeviceInfo(ctypes.Structure): + MAX_PATH = 260 + _fields_ = [ + ("DeviceIndex", ctypes.c_ubyte), # 当前打开序号 + ("DevicePath", ctypes.c_char * MAX_PATH), # 设备路径 + ( + "UsbClass", + ctypes.c_ubyte, + ), # USB设备类别: 0=CH341 Vendor; 1=CH347 Vendor; 2=HID + ("FuncType", ctypes.c_ubyte), # 设备功能类型: 0=UART1; 1=SPI+I2C; 2=JTAG+I2C + ("DeviceID", ctypes.c_char * 64), # USB设备ID: USB\VID_xxxx&PID_xxxx + ( + "ChipMode", + ctypes.c_ubyte, + ), # 芯片模式: 0=Mode0(UART*2); 1=Mode1(Uart1+SPI+I2C); 2=Mode2(HID Uart1+SPI+I2C); 3=Mode3(Uart1+Jtag+I2C) + ("DevHandle", ctypes.c_void_p), # 设备句柄 + ("BulkOutEndpMaxSize", ctypes.c_ushort), # 上传端点大小 + ("BulkInEndpMaxSize", ctypes.c_ushort), # 下传端点大小 + ("UsbSpeedType", ctypes.c_ubyte), # USB速度类型: 0=FS; 1=HS; 2=SS + ("CH347IfNum", ctypes.c_ubyte), # USB接口号 + ("DataUpEndp", ctypes.c_ubyte), # 端点地址 + ("DataDnEndp", ctypes.c_ubyte), # 端点地址 + ("ProductString", ctypes.c_char * 64), # USB产品字符串 + ("ManufacturerString", ctypes.c_char * 64), # USB厂商字符串 + ("WriteTimeout", ctypes.c_ulong), # USB写超时 + ("ReadTimeout", ctypes.c_ulong), # USB读超时 + ("FuncDescStr", ctypes.c_char * 64), # 接口功能描述符 + ("FirmwareVer", ctypes.c_ubyte), # 固件版本 + ] + + +class SPIConfig(ctypes.Structure): + _fields_ = [ + ("Mode", ctypes.c_ubyte), # 0-3: SPI Mode0/1/2/3 + ( + "Clock", + ctypes.c_ubyte, + ), # 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz + ("ByteOrder", ctypes.c_ubyte), # 0=LSB first(LSB), 1=MSB first(MSB) + ( + "SPIWriteReadInterval", + ctypes.c_ushort, + ), # Regular interval for SPI read/write commands, in microseconds + ( + "SPIOutDefaultData", + ctypes.c_ubyte, + ), # Default output data when reading from SPI + ( + "ChipSelect", + ctypes.c_ulong, + ), # Chip select control. Bit 7 as 0 ignores chip select control, + # Bit 7 as 1 makes the parameters valid: + # Bit 1 and Bit 0 as 00/01 selects CS1/CS2 pin as the active low chip select. + ( + "CS1Polarity", + ctypes.c_ubyte, + ), # Bit 0: CS1 polarity control, 0: active low, 1: active high + ( + "CS2Polarity", + ctypes.c_ubyte, + ), # Bit 0: CS2 polarity control, 0: active low, 1: active high + ( + "IsAutoDeativeCS", + ctypes.c_ushort, + ), # Automatically de-assert chip select after the operation is completed + ( + "ActiveDelay", + ctypes.c_ushort, + ), # Delay time for executing read/write operations after chip select is set, in microseconds + ( + "DelayDeactive", + ctypes.c_ulong, + ), # Delay time for executing read/write operations after chip select is de-asserted, in microseconds + ] + + +class CH347: + + # MAX devices number + MAX_DEVICE_NUMBER = 8 + + # Define the callback function type + NOTIFY_ROUTINE = ctypes.CFUNCTYPE(None, ctypes.c_ulong) + + INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value + + def __init__(self, device_index=0, dll_path="ch347/lib/CH347DLLA64.DLL"): + self.ch347dll = ctypes.WinDLL(dll_path) + self.device_index = device_index + + # 创建回调函数对象并绑定到实例属性 + self.callback_func = self.NOTIFY_ROUTINE(self.event_callback) + + # Set the function argument types and return type for CH347OpenDevice + self.ch347dll.CH347OpenDevice.argtypes = [ctypes.c_ulong] + self.ch347dll.CH347OpenDevice.restype = ctypes.c_void_p + + # Set the function argument types and return type for CH347CloseDevice + self.ch347dll.CH347CloseDevice.argtypes = [ctypes.c_ulong] + self.ch347dll.CH347CloseDevice.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347GetDeviceInfor + self.ch347dll.CH347GetDeviceInfor.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(DeviceInfo), + ] + self.ch347dll.CH347GetDeviceInfor.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347GetVersion + self.ch347dll.CH347GetVersion.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_ubyte), + ctypes.POINTER(ctypes.c_ubyte), + ctypes.POINTER(ctypes.c_ubyte), + ctypes.POINTER(ctypes.c_ubyte), + ] + self.ch347dll.CH347GetVersion.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SetDeviceNotify + self.ch347dll.CH347SetDeviceNotify.argtypes = [ + ctypes.c_ulong, + ctypes.c_char_p, + ctypes.CFUNCTYPE(None, ctypes.c_ulong), + ] + self.ch347dll.CH347SetDeviceNotify.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347ReadData + self.ch347dll.CH347ReadData.argtypes = [ + ctypes.c_ulong, + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_ulong), + ] + self.ch347dll.CH347ReadData.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347WriteData + self.ch347dll.CH347WriteData.argtypes = [ + ctypes.c_ulong, + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_ulong), + ] + self.ch347dll.CH347WriteData.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SetTimeout + self.ch347dll.CH347SetTimeout.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ] + self.ch347dll.CH347SetTimeout.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_Init + self.ch347dll.CH347SPI_Init.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(SPIConfig), + ] + self.ch347dll.CH347SPI_Init.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_GetCfg + self.ch347dll.CH347SPI_GetCfg.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(SPIConfig), + ] + self.ch347dll.CH347SPI_GetCfg.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_ChangeCS + self.ch347dll.CH347SPI_ChangeCS.argtypes = [ctypes.c_ulong, ctypes.c_ubyte] + self.ch347dll.CH347SPI_ChangeCS.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_SetChipSelect + self.ch347dll.CH347SPI_SetChipSelect.argtypes = [ + ctypes.c_ulong, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ] + self.ch347dll.CH347SPI_SetChipSelect.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_Write + self.ch347dll.CH347SPI_Write.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_void_p, + ] + self.ch347dll.CH347SPI_Write.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_Read + self.ch347dll.CH347SPI_Read.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_ulong), + ctypes.c_void_p, + ] + self.ch347dll.CH347SPI_Read.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347SPI_WriteRead + self.ch347dll.CH347SPI_WriteRead.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_void_p, + ] + self.ch347dll.CH347SPI_WriteRead.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347StreamSPI4 + self.ch347dll.CH347StreamSPI4.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_void_p, + ] + self.ch347dll.CH347StreamSPI4.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347I2C_Set + self.ch347dll.CH347I2C_Set.argtypes = [ctypes.c_ulong, ctypes.c_ulong] + self.ch347dll.CH347I2C_Set.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347I2C_SetDelaymS + self.ch347dll.CH347I2C_SetDelaymS.argtypes = [ctypes.c_ulong, ctypes.c_ulong] + self.ch347dll.CH347I2C_SetDelaymS.restype = ctypes.c_bool + + # Set the function argument types and return type for CH347StreamI2C + self.ch347dll.CH347StreamI2C.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_void_p, + ctypes.c_ulong, + ctypes.c_void_p, + ] + self.ch347dll.CH347StreamI2C.restype = ctypes.c_bool + + def list_devices(self): + # List all devices + num_devices = 0 + dev_info = DeviceInfo() + for i in range(self.MAX_DEVICE_NUMBER): + if self.ch347dll.CH347OpenDevice(i) == self.INVALID_HANDLE_VALUE: + break + num_devices += 1 + if self.ch347dll.CH347GetDeviceInfor(i, ctypes.byref(dev_info)): + for field_name, _ in dev_info._fields_: + value = getattr(dev_info, field_name) + print(f"{field_name}: {value}") + print("-" * 40) + self.ch347dll.CH347CloseDevice(i) + print(f"Number of devices: {num_devices}") + return num_devices + + @staticmethod + def event_callback(self, event_status): + # Callback function implementation + print("Callback event status:", event_status) + if event_status == 0: + # Device unplug event + print("Device unplugged") + elif event_status == 3: + # Device insertion event + print("Device inserted") + + def open_device(self): + """ + Open USB device. + + Returns: + int: Handle to the opened device if successful, None otherwise. + """ + handle = self.ch347dll.CH347OpenDevice(self.device_index) + if handle != self.INVALID_HANDLE_VALUE: + return handle + else: + return None + + def close_device(self): + """ + Close USB device. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347CloseDevice(self.device_index) + return result + + def get_device_info(self): + """ + Get device information. + + Returns: + bool: True if successful, False otherwise. + DeviceInfo: Device information. + """ + dev_info = DeviceInfo() + result = self.ch347dll.CH347GetDeviceInfor( + self.device_index, ctypes.byref(dev_info) + ) + if result: + return dev_info + else: + return None + + def get_version(self): + """ + Obtain driver version, library version, device version, and chip type. + + This method retrieves various versions related to the CH347 device and returns them as a tuple. + + Returns: + tuple or None: A tuple containing the following information if successful: + - driver_ver (int): The driver version. + - dll_ver (int): The library version. + - device_ver (int): The device version. + - chip_type (int): The chip type. + Returns None if the retrieval fails. + """ + # Create variables to store the version information + driver_ver = ctypes.c_ubyte() + dll_ver = ctypes.c_ubyte() + device_ver = ctypes.c_ubyte() + chip_type = ctypes.c_ubyte() + + # Call the CH347GetVersion function + result = self.ch347dll.CH347GetVersion( + self.device_index, + ctypes.byref(driver_ver), + ctypes.byref(dll_ver), + ctypes.byref(device_ver), + ctypes.byref(chip_type), + ) + if result: + return driver_ver.value, dll_ver.value, device_ver.value, chip_type.value + else: + return None + + def set_device_notify(self, device_id, notify_routine=event_callback): + """ + Configure device event notifier. + + Args: + device_id (str): Optional parameter specifying the ID of the monitored device. + notify_routine (callable): Callback function to handle device events. + + Returns: + bool: True if successful, False otherwise. + """ + callback = self.NOTIFY_ROUTINE(notify_routine) + result = self.ch347dll.CH347SetDeviceNotify( + self.device_index, device_id, callback + ) + return result + + def read_data(self, buffer, length): + """ + Read USB data block. + + Args: + buffer (ctypes.c_void_p): Pointer to a buffer to store the read data. + length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Contains the length to be read as input and the actual read length after return. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347ReadData(self.device_index, buffer, length) + return result + + def write_data(self, buffer, length): + """ + Write USB data block. + + Args: + buffer (ctypes.c_void_p): Pointer to a buffer containing the data to be written. + length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Input length is the intended length, and the return length is the actual length. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347WriteData(self.device_index, buffer, length) + return result + + def set_timeout(self, write_timeout, read_timeout): + """ + Set the timeout of USB data read and write. + + Args: + write_timeout (int): Timeout for USB to write data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default). + read_timeout (int): Timeout for USB to read data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default). + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347SetTimeout( + self.device_index, write_timeout, read_timeout + ) + return result + + def spi_init(self, spi_config: SPIConfig) -> bool: + """ + Initialize the SPI Controller. + + Args: + spi_config (SPIConfig): The configuration for the SPI controller. + + Returns: + bool: True if initialization is successful, False otherwise. + """ + result = self.ch347dll.CH347SPI_Init( + self.device_index, ctypes.byref(spi_config) + ) + return result + + def spi_get_config(self): + """ + Get SPI controller configuration information. + + Returns: + tuple: A tuple containing a boolean value indicating if the operation was successful + and the SPI configuration structure. + + The first element (bool) represents whether the operation was successful or not. + - True: The operation was successful. + - False: The operation failed. + + The second element (SPIConfig): An instance of the SPIConfig class, representing the + SPI configuration structure. If the operation was successful, this object will contain + the configuration information retrieved from the SPI controller. Otherwise, it will be + an empty object with default values. + """ + spi_config = SPIConfig() + result = self.ch347dll.CH347SPI_GetCfg( + self.device_index, ctypes.byref(spi_config) + ) + if result: + return spi_config + else: + return None + + def spi_change_cs(self, status): + """ + Change the chip selection status. + + Args: + status (int): Chip selection status. 0 = Cancel the piece to choose, 1 = Set piece selected. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347SPI_ChangeCS(self.device_index, status) + return result + + def spi_set_chip_select( + self, + enable_select, + chip_select, + is_auto_deactive_cs, + active_delay, + delay_deactive, + ): + """ + Set SPI chip selection. + + Args: + enable_select (int): Enable selection status. The lower octet is CS1 and the higher octet is CS2. + A byte value of 1 sets CS, 0 ignores this CS setting. + chip_select (int): Chip selection status. The lower octet is CS1 and the higher octet is CS2. + A byte value of 1 sets CS, 0 ignores this CS setting. + is_auto_deactive_cs (int): Auto deactivation status. The lower 16 bits are CS1 and the higher 16 bits are CS2. + Whether to undo slice selection automatically after the operation is complete. + active_delay (int): Latency of read/write operations after chip selection, in microseconds. + The lower 16 bits are CS1 and the higher 16 bits are CS2. + delay_deactive (int): Delay time for read and write operations after slice selection, in microseconds. + The lower 16 bits are CS1 and the higher 16 bits are CS2. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347SPI_SetChipSelect( + self.device_index, + enable_select, + chip_select, + is_auto_deactive_cs, + active_delay, + delay_deactive, + ) + return result + + def spi_write( + self, chip_select: int, write_data: List[int], write_step: int = 512 + ) -> bool: + result = self.spi_write(chip_select, bytes(write_data), write_step) + def spi_write( + self, chip_select: int, write_data: bytes, write_step: int = 512 + ) -> bool: + """ + SPI write data. + + Args: + chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored. + When bit 7 is 1, chip selection operation is performed. + write_data (List[int]): List of integers to write. + write_step (int, optional): The length of a single block to be read. Default is 512. + + Returns: + bool: True if successful, False otherwise. + """ + write_length = len(write_data) + write_buffer = ctypes.create_string_buffer(write_data) + result = self.ch347dll.CH347SPI_Write( + self.device_index, chip_select, write_length, write_step, write_buffer + ) + return result + + def spi_read( + self, chip_select: int, write_data: List[int], read_length: int + ) -> List[int]: + """ + SPI read data. + + Args: + chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored. + When bit 7 is 1, chip selection operation is performed. + write_data (List[int]): List of integers to write. + read_length (int): Number of bytes to read. + + Returns: + List[int]: Data read in from the SPI stream if successful, None otherwise. + """ + write_length = len(write_data) + + # Create ctypes buffer for write data + write_buffer = ctypes.create_string_buffer(bytes(write_data)) + + # Create ctypes buffer for read data + read_buffer = ctypes.create_string_buffer(read_length) + + # Create combined buffer for read and write data + combined_buffer = ctypes.create_string_buffer( + write_buffer.raw[:write_length] + read_buffer.raw + ) + + result = self.ch347dll.CH347SPI_Read( + self.device_index, + chip_select, + write_length, + ctypes.byref(ctypes.c_ulong(read_length)), + combined_buffer, + ) + + if result: + # Extract the read data from the combined buffer + read_data = list(combined_buffer[:read_length]) + return read_data + else: + return None + + def spi_write_read(self, chip_select: int, write_data: bytes) -> bytes: + io_buffer = ctypes.create_string_buffer(write_data) + result = self.ch347dll.CH347SPI_WriteRead( + self.device_index, chip_select, len(write_data), io_buffer + ) + # transform the ctypes buffer to bytes + io_data = io_buffer.raw[: len(write_data)] + return io_data + + def _spi_write_read(self, chip_select: int, length: int, io_buffer: ctypes.c_void_p) -> bool: + """ + Handle SPI data stream 4-wire interface. + + Args: + chip_select (int): Selection control. If the film selection control bit 7 is 0, ignore the film selection control. + If bit 7 is 1, perform the film selection. + length (int): Number of bytes of data to be transferred. + io_buffer (ctypes.c_void_p): Points to a buffer that places the data to be written out from DOUT. + Returns the data read in from DIN. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347SPI_WriteRead( + self.device_index, chip_select, length, io_buffer + ) + return result + + def stream_spi4(self, chip_select, length, io_buffer): + """ + Handle SPI data stream 4-wire interface. + + Args: + chip_select (int): Film selection control. If bit 7 is 0, slice selection control is ignored. + If bit 7 is 1, the parameter is valid: Bit 1 bit 0 is 00/01/10. + Select D0/D1/D2 pins as low-level active chip options, respectively. + length (int): Number of bytes of data to be transferred. + io_buffer (ctypes.c_void_p): Points to a buffer that places data to be written out from DOUT. + Returns data to be read in from DIN. + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347StreamSPI4( + self.device_index, chip_select, length, io_buffer + ) + return result + + def i2c_set(self, interface_speed): + """ + Set the serial port flow mode. + + Args: + interface_speed (int): I2C interface speed / SCL frequency. Bit 1-bit 0: + 0 = low speed / 20KHz + 1 = standard / 100KHz (default) + 2 = fast / 400KHz + 3 = high speed / 750KHz + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347I2C_Set(self.device_index, interface_speed) + return result + + def i2c_set_delay_ms(self, delay_ms): + """ + Set the hardware asynchronous delay to a specified number of milliseconds before the next stream operation. + + Args: + delay_ms (int): Delay duration in milliseconds (ms). + + Returns: + bool: True if successful, False otherwise. + """ + result = self.ch347dll.CH347I2C_SetDelaymS(self.device_index, delay_ms) + return result + + def stream_i2c(self, write_data, read_length): + """ + Process I2C data stream. + + Args: + write_data (bytes): Data to write. The first byte is usually the I2C device address and read/write direction bit. + read_length (int): Number of bytes of data to read. + + Returns: + bytes: Data read from the I2C stream. + """ + write_length = len(write_data) + + # Convert write_data to ctypes buffer + write_buffer = ctypes.create_string_buffer(bytes(write_data)) + + # Create ctypes buffer for read data + read_buffer = ctypes.create_string_buffer(read_length) + + result = self.ch347dll.CH347StreamI2C( + self.device_index, write_length, write_buffer, read_length, read_buffer + ) + + if result: + return read_buffer[:read_length] + else: + return None diff --git a/FlashGBX/bacon/command.py b/FlashGBX/bacon/command.py new file mode 100644 index 0000000..16ddbdd --- /dev/null +++ b/FlashGBX/bacon/command.py @@ -0,0 +1,292 @@ +# -*- coding: utf-8 -*- +# bacon +# Author: ChisBread (github.com/ChisBread) + +# 命令结束后需要跟一个0bit + +# -- | Command Value (5 bits) | Command Name | Description | Input Bits | Input Description | Output Bits | Output Description | +# -- |------------------------|--------------|-------------|------------|-------------------|-------------|--------------------| +# -- | 00001 | CART_30BIT_WRITE | 30位数据写入 | 30 | 30位数据 | - | 无返回 | +# -- | 00010 | CART_30BIT_READ | 30位数据读取 | - | 无输入 | 30 | 30位数据 | +# -- | 00011 | MOD_WRITE | 控制电源 | 2 | 10: 都不启用
00: 启用3.3v(GBA)
11: 启用5v(GB)
01: 无效 | - | 无返回 | +# -- | 00100 | MOD_READ | 读取电源状态 | - | 无输入 | 2 | 0: 都不启用
01: 启用3.3v(GBA)
10: 启用5v(GB)
11: 无效 | +# -- | 00101 | GBA_WR/RD_WRITE | GBA 2bit寄存器操作,每次上升沿会使锁存的16bit地址自增1 | 2 | 0: 无效
01: 启用WR
10: 启用RD
11: 默认 | - | 无返回 | +# -- | 00110 | GBA_WR/RD_READ | 读取GBA 2bit寄存器状态 | - | 无输入 | 2 | 0: 无效
01: 启用WR
10: 启用RD
11: 默认 | +# -- | 00111 | GBA_ROM_EN_WRITE | GBA ROM使能 | 1 | 使能 | - | 无返回 | +# -- | 01000 | GBA_ROM_ADDR_READ | 读取GBA R高8位地址 | - | 无输入 | 8 | 8位数据 | +# -- | 01001 | GBA_ROM_DATA_WRITE | GBA ROM写16位数据 | 16 | 16位数据 | - | 无返回 | +# -- | 01010 | GBA_ROM_DATA_READ | 读取GBA ROM数据 | - | 无输入 | 16 | 16位数据 | +# -- | 01011 | GBA_ROM_DATA_READ_FLIP | 读取GBA ROM数据 | - | 无输入 | 16 | 16位数据 | +# -- | 01100 | GBA_ROM_DATA_WRITE_FLIP | GBA ROM写16位数据 | 16 | 16位数据 | - | 无返回 | +# -- | 10000 - 11111 | RESERVED | 预留命令 | - | - | - | - | +import traceback +from bitarray import bitarray + +def command2bytes(command, endclk=True) -> bytes: + if isinstance(command, str): + command = bitarray(command) + if endclk and len(command) % 8 == 0: + # end clk cycle + command += "0" + if len(command) % 8: + command += bitarray("0" * (8 - len(command) % 8)) + return command.tobytes() + +def echo_all(data): + return data + +def bytes2command(data: bytes): + ret = bitarray() + ret.frombytes(data) + return ret + +def make_power_control_command( + # 3.3v低电平有效, 5v高电平有效 + not_v3_3v: bool = True, v5v: bool = False, postfunc=command2bytes) -> bytes: + # 不能两个都有效 + if not not_v3_3v and v5v: + raise ValueError("v3_3v and v5v can't be both enabled") + command = "00011" + command += "1" if v5v else "0" + command += "1" if not_v3_3v else "0" + return postfunc(command) + +def make_power_read_command(postfunc=command2bytes) -> bytes: + command = "00100" + return postfunc(command) + +def make_cart_30bit_write_command( + phi: bool = False, req: bool = False, + # 低电平有效 + wr: bool = True, rd: bool = True, + cs1: bool = True, cs2: bool = True, + # 小端模式 + v16bit: bytes = b"\00\00", v8bit: bytes = b"\00", postfunc=command2bytes) -> bytes: + command = "00001" + command += "1" if req else "0" + command += "1" if cs2 else "0" + if len(v16bit) != 2: + raise ValueError("v16bit must be 2 bytes") + if len(v8bit) != 1: + raise ValueError("v8bit must be 1 byte") + command += bin(v8bit[0])[2:].rjust(8, "0") + command += bin(v16bit[1])[2:].rjust(8, "0") + command += bin(v16bit[0])[2:].rjust(8, "0") + command += "1" if cs1 else "0" + command += "1" if rd else "0" + command += "1" if wr else "0" + command += "1" if phi else "0" + return postfunc(command) + +def make_cart_30bit_read_command(postfunc=command2bytes) -> bytes: + command = "00010" + command += "0" * 30 + return postfunc(command) + +def make_gba_wr_rd_write_command( + wr: bool = False, rd: bool = False, postfunc=command2bytes) -> bytes: + command = "00101" + command += "1" if wr else "0" + command += "1" if rd else "0" + return postfunc(command) + +def extract_cart_30bit_read_data(data: bytes) -> dict: + if len(data) != 5: + raise ValueError("data must be 5 bytes, but got %d" % len(data)) + ret = {} + # 输出和输出是反的,且有6bit无效数据,所以 + # 000000 00 | 10 010101 | 011010101001010101100010 + ret["phi"] = bool(data[0] >> 1 & 1) + ret["wr"] = bool(data[0] & 1) + ret["rd"] = bool(data[1] >> 7) + ret["cs1"] = bool(data[1] >> 6 & 1) + # 16bit数据, 其中6bit在data[1]的低位, 2bit在data[2]的高位 + v16bitB0 = ((data[1] & 0b00111111) << 2) | (data[2] >> 6) + # 16bit数据, 其中6bit在data[2]的低位, 2bit在data[3]的高位 + v16bitB1 = ((data[2] & 0b00111111) << 2) | (data[3] >> 6) + # 8bit数据, 其中6bit在data[3]的低位, 2bit在data[4]的高位 + v8bit = ((data[3] & 0b00111111) << 2) | (data[4] >> 6) + # 3byte都需要按位翻转 + # v16bitB0 = reverse_bits(v16bitB0) + # v16bitB1 = reverse_bits(v16bitB1) + v8bit = reverse_bits(v8bit) + # ret["v16bit"] = (v16bitB0 << 8) | v16bitB1 + ret["v16bit"] = reverse_bits_16bit((v16bitB1 << 8) | v16bitB0) + ret["v8bit"] = v8bit + ret["cs2"] = bool(data[4] >> 5 & 1) + ret["req"] = bool(data[4] >> 4 & 1) + return ret + + +def make_v16bit_data_write_command(data: int, flip=False, postfunc=command2bytes) -> bytes: + if data > 0xFFFF: + raise ValueError("data must be less than 0xFFFF") + command = ("01100" if flip else "01001") + bin(data)[2:].rjust(16, "0") + return postfunc(command) + +def make_gba_rom_data_write_command(data: int, flip=False, postfunc=command2bytes) -> bytes: + return make_v16bit_data_write_command(data, flip, postfunc) + +__readcyclecmd_30bit = "0".join([ + make_gba_wr_rd_write_command(wr=True, rd=False, postfunc=echo_all), + make_cart_30bit_read_command(postfunc=echo_all), + make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all)]) +def make_rom_read_cycle_command_30bit(times=1, postfunc=command2bytes) -> bytes: + # 1. pull down RD + # 2. read data + # 3. pull up RD + return postfunc("0".join([__readcyclecmd_30bit for i in range(times)])) + +def extract_read_cycle_data_30bit(data: bytes, times=1): + ret = [] + if len(data)*8 < (len(__readcyclecmd_30bit)+1) * times: + raise ValueError("data must be %d bytes, but got %d" % ((len(__readcyclecmd_30bit)+1) * times, len(data)*8)) + bytesstr = bytes2command(data) + # 每隔len(__readcyclecmd_30bit)+1bit取一次数据 + for i in range(0, len(bytesstr), len(__readcyclecmd_30bit) + 1): + one = command2bytes( bytesstr[i + 8: i + len(__readcyclecmd_30bit) + 1], endclk=False) + ret.append(extract_cart_30bit_read_data(one[:len(one)-1])) + if len(ret) >= times: + break + return ret + +def make_gba_rom_data_read_command(flip=False, postfunc=command2bytes) -> bytes: + command = "0101" + ("1" if flip else "0") + "0" * 16 + return postfunc(command) + +def extract_gba_rom_read_data(data: bytes) -> int: + if len(data) < 3: + raise ValueError("data must be 3 bytes, but got %d" % len(data)) + # 6bit无效数据 + #ret = (reverse_bits(data[0] << 6 | data[1] >> 2) << 8) | reverse_bits(data[1] << 6 | data[2] >> 2) + ret = reverse_bits_16bit(((data[1] << 6 | data[2] >> 2) << 8) | (data[0] << 6 | data[1] >> 2)) + return ret + +__readcyclecmd = "0".join([ + make_gba_wr_rd_write_command(wr=True, rd=False, postfunc=echo_all), + make_gba_rom_data_read_command(flip=True, postfunc=echo_all), + #make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all), + ]) +def make_rom_read_cycle_command(times=1, postfunc=command2bytes) -> bytes: + # 1. pull down RD + # 2. read data + # 3. pull up RD + return postfunc("0".join([__readcyclecmd for i in range(times)])) + +def extract_read_cycle_data(data: bytes, times=1): + ret = [] + if len(data)*8 < (len(__readcyclecmd)+1) * times: + raise ValueError("data must be %d bits, but got %d" % ((len(__readcyclecmd)+1) * times, len(data)*8)) + bytesstr = bytes2command(data) + # 每隔len(__readcyclecmd)+1bit取一次数据 + for i in range(0, len(bytesstr), len(__readcyclecmd) + 1): + one = command2bytes(bytesstr[i + 8: i + len(__readcyclecmd) + 1], endclk=False) + ret.append(extract_gba_rom_read_data(one[:len(one)])) + if len(ret) >= times: + break + return ret + +def make_rom_write_cycle_command_with_addr(addrdatalist: list, flip=True, postfunc=command2bytes) -> bytes: + readram = "0".join(["0".join([ + # 1. write addr, reset cs1 and wr + make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]), postfunc=echo_all), + # 2. pull down cs1 + make_gba_rom_cs_write(cs=False, postfunc=echo_all), + # 3. write data + make_gba_rom_data_write_command(data, flip=flip, postfunc=echo_all), + # 4. pull down wr + # make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all), + ]) + ("0"+make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all) if not flip else "") for addr, data in addrdatalist]) + return postfunc(readram) + +def make_rom_write_cycle_command_sequential(datalist: list, flip=True, postfunc=command2bytes) -> bytes: + readram = "0".join(["0".join([ + # 1. reset wr + make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all), + # 2. write data + make_gba_rom_data_write_command(data, flip=flip, postfunc=echo_all), + # 3. pull down wr + # make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all), + ]) + ("0"+make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all) if not flip else "") for data in datalist]) + return postfunc(readram) + +def make_gba_rom_cs_write(cs: bool = True, postfunc=command2bytes) -> bytes: + command = "00111" + command += "1" if cs else "0" + return postfunc(command) + +def make_gba_rom_addr_read_command(postfunc=command2bytes) -> bytes: + command = "01000" + "0" * 8 + return postfunc(command) + +def extract_gba_rom_addr_read_data(data: bytes) -> int: + if len(data) != 2: + raise ValueError("data must be 2 byte, but got %d" % len(data)) + return reverse_bits((data[0] << 6 ) | (data[1] >> 2)) + +def make_ram_write_cycle_with_addr(addrdatalist: list, postfunc=command2bytes) -> bytes: + writeram = "0".join(["0".join( + [make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=False, + v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([data]), postfunc=echo_all), + make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all)]) + for addr, data in addrdatalist]) + return postfunc(writeram) + +def make_ram_write_cycle_command(addr, data, postfunc=command2bytes) -> bytes: + cmd = "0".join(["0".join( + [make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=False, + v16bit=bytes([(addr+i) & 0xFF, ((addr+i) >> 8) & 0xFF]), v8bit=bytes([data[i]]), postfunc=echo_all), + make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all)]) + for i in range(len(data))]) + return postfunc(cmd) + +def make_ram_read_cycle_command(addr=0, times=1, postfunc=command2bytes) -> bytes: + cmd = "0".join(["0".join([ + make_v16bit_data_write_command(addr+i, postfunc=echo_all), + make_gba_rom_addr_read_command(postfunc=echo_all) + ]) for i in range(times)]) + return postfunc(cmd) + +__len_of_v16bit_write = len(make_v16bit_data_write_command(data=0, postfunc=echo_all)) +__len_of_v8bit_write = len(make_gba_rom_addr_read_command(postfunc=echo_all)) +def extract_ram_read_cycle_data(data: bytes, times=1): + command = bytes2command(data) + ret = [] + for i in range(0, len(command), __len_of_v16bit_write + __len_of_v8bit_write + 2): + one = command[i + __len_of_v16bit_write + 1: i + __len_of_v16bit_write + 1 + __len_of_v8bit_write + 1] + ret.append(extract_gba_rom_addr_read_data(command2bytes(one, endclk=False))) + if len(ret) >= times: + break + return ret + +def _reverse_bits(byte): + byte = ((byte & 0xF0) >> 4) | ((byte & 0x0F) << 4) + byte = ((byte & 0xCC) >> 2) | ((byte & 0x33) << 2) + byte = ((byte & 0xAA) >> 1) | ((byte & 0x55) << 1) + return byte + +lookup_rev = [_reverse_bits(i) for i in range(256)] + +def reverse_bits(byte): + return lookup_rev[byte & 0xFF] + +def _reverse_bits_16bit(data): + return (reverse_bits(data & 0xFF) << 8) | reverse_bits(data >> 8) + +lookup_rev_16bit = [_reverse_bits_16bit(i) for i in range(0x10000)] + +def reverse_bits_16bit(data): + return lookup_rev_16bit[data & 0xFFFF] + + + \ No newline at end of file diff --git a/FlashGBX/bacon/include/CH347DLL.H b/FlashGBX/bacon/include/CH347DLL.H new file mode 100644 index 0000000..692eb2b --- /dev/null +++ b/FlashGBX/bacon/include/CH347DLL.H @@ -0,0 +1,613 @@ +/***************************************************************************** +** Copyright (C) WCH 2001-2023 ** +** Web: http://wch.cn ** +******************************************************************************/ +// USB߽ӿоƬCH341/7Ӧòӿڿ,CH347480MbpsUSBչUART/SPI/I2C/JTAG/SWD +// CH347-DLL V1.2 +// л: Windows 98/ME, Windows 2000/XP, WIN7/8/10/11,and later. +// support USB chip: CH341, CH341A,CH347 +// USB => Parallel, I2C, SPI, JTAG, SWD, UART ... +//Notes: +//Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd. + + +// +#ifndef _CH347_DLL_H +#define _CH347_DLL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN64 +#define mOFFSET( s, m ) ( (ULONG_PTR) & ( ( ( s * ) 0 ) -> m ) ) // ȡṹԱƫƵַĺ +#else +#define mOFFSET( s, m ) ( (ULONG) & ( ( ( s * ) 0 ) -> m ) ) // ȡṹԱƫƵַĺ +#endif + +#ifndef max +#define max( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) // ϴֵ +#endif + +#ifndef min +#define min( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) // Сֵ +#endif + +#ifdef ExAllocatePool +#undef ExAllocatePool // ɾTAGڴ +#endif + +#ifndef NTSTATUS +typedef LONG NTSTATUS; // ״̬ +#endif + +//CH31DLLCH341WDM +#ifndef _CH341_DLL_H +typedef struct _USB_SETUP_PKT { // USBƴĽ׶εṹ + UCHAR mUspReqType; // 00H + UCHAR mUspRequest; // 01H + union { + struct { + UCHAR mUspValueLow; // 02H ֵֽ + UCHAR mUspValueHigh; // 03H ֵֽ + }; + USHORT mUspValue; // 02H-03H ֵ + }; + union { + struct { + UCHAR mUspIndexLow; // 04H ֽ + UCHAR mUspIndexHigh; // 05H ֽ + }; + USHORT mUspIndex; // 04H-05H + }; + USHORT mLength; // 06H-07H ݽ׶εݳ +} mUSB_SETUP_PKT, *mPUSB_SETUP_PKT; + + +typedef struct _WIN32_COMMAND { // WIN32ӿڽṹ + union { + ULONG mFunction; // ʱָܴ߹ܵ + NTSTATUS mStatus; // ʱز״̬ + }; + ULONG mLength; // ȡ,غݵij + union { + mUSB_SETUP_PKT mSetupPkt; // USBƴĽ׶ε + UCHAR mBuffer[ 512]; // ݻ,Ϊ0255B + }; +} mWIN32_COMMAND, *mPWIN32_COMMAND; +// WIN32Ӧòӿ +#define IOCTL_CH341_COMMAND ( FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED ) // רýӿ + +#define mWIN32_COMMAND_HEAD mOFFSET( mWIN32_COMMAND, mBuffer ) // WIN32ӿڵͷ + +#define mCH341_MAX_NUMBER 32 // ͬʱӵCH341/7豸 + +#define mMAX_BUFFER_LENGTH 0x1000 // ݻ󳤶4096 + +#define mMAX_COMMAND_LENGTH ( mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH ) // ݳȼṹͷij + +#define mDEFAULT_BUFFER_LEN 0x0400 // ݻĬϳ1024 + +#define mDEFAULT_COMMAND_LEN ( mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN ) // Ĭݳȼṹͷij + +// CH341˵ַ +#define mCH347_ENDP_DATA_UP 0x86 // CH347ݿϴ˵ĵַ +#define mCH347_ENDP_DATA_DOWN 0x06 // CH347ݿ´˵ĵַ + +// 豸ӿṩĹܵ +#define mPipeDeviceCtrl 0x00000004 // CH347ۺϿƹܵ +#define mPipeDataUp 0x00000006 // CH347ݿϴܵ +#define mPipeDataDown 0x00000007 // CH347ݿ´ܵ + +// ӦòӿڵĹܴ +#define mFuncNoOperation 0x00000000 // ޲ +#define mFuncGetVersion 0x00000001 // ȡ汾 +#define mFuncGetConfig 0x00000002 // ȡUSB豸 +#define mFuncSetTimeout 0x00000009 // USBͨѶʱ +#define mFuncSetExclusive 0x0000000b // öռʹ +#define mFuncResetDevice 0x0000000c // λUSB豸 +#define mFuncResetPipe 0x0000000d // λUSBܵ +#define mFuncAbortPipe 0x0000000e // ȡUSBܵ +#define mFuncBufferMode 0x00000020 // 趨ϴģʽѯеݳ +#define mFuncBufferModeDn 0x00000021 // 趨´ģʽѯеݳ +#define mFuncGetVersionEx 0x00000022 // ȡ汾żоƬͺ +// USB豸׼ +#define mUSB_CLR_FEATURE 0x01 +#define mUSB_SET_FEATURE 0x03 +#define mUSB_GET_STATUS 0x00 +#define mUSB_SET_ADDRESS 0x05 +#define mUSB_GET_DESCR 0x06 +#define mUSB_SET_DESCR 0x07 +#define mUSB_GET_CONFIG 0x08 +#define mUSB_SET_CONFIG 0x09 +#define mUSB_GET_INTERF 0x0a +#define mUSB_SET_INTERF 0x0b +#define mUSB_SYNC_FRAME 0x0c + +// CH341ƴijר +#define mCH341_VENDOR_READ 0xC0 // ͨƴʵֵCH341רö +#define mCH341_VENDOR_WRITE 0x40 // ͨƴʵֵCH341רд + +#define mCH341A_CMD_I2C_STREAM 0xAA // I2Cӿڵ,ӴֽڿʼΪI2C +#define mCH341A_CMD_UIO_STREAM 0xAB // UIOӿڵ,ӴֽڿʼΪ +#define mCH341A_CMD_PIO_STREAM 0xAE // PIOӿڵ,ӴֽڿʼΪ +// CH341Aƴijר +#define mCH341A_BUF_CLEAR 0xB2 // δɵ +#define mCH341A_I2C_CMD_X 0x54 // I2Cӿڵ,ִ +#define mCH341A_DELAY_MS 0x5E // Ϊλʱָʱ +#define mCH341A_GET_VER 0x5F // ȡоƬ汾 + +#define mCH341A_CMD_I2C_STM_STA 0x74 // I2Cӿڵ:ʼλ +#define mCH341A_CMD_I2C_STM_STO 0x75 // I2Cӿڵ:ֹͣλ +#define mCH341A_CMD_I2C_STM_OUT 0x80 // I2Cӿڵ:,λ5-λ0Ϊ,ֽΪ,0ֻһֽڲӦ +#define mCH341A_CMD_I2C_STM_IN 0xC0 // I2Cӿڵ:,λ5-λ0Ϊ,0ֻһֽڲӦ +#define mCH341A_CMD_I2C_STM_MAX ( min( 0x3F, mCH341_PACKET_LENGTH ) ) // I2Cӿڵݵ󳤶 +#define mCH341A_CMD_I2C_STM_SET 0x60 // I2Cӿڵ:ò,λ2=SPII/O(0=뵥,1=˫˫),λ1λ0=I2Cٶ(00=,01=׼,10=,11=) +#define mCH341A_CMD_I2C_STM_US 0x40 // I2Cӿڵ:΢Ϊλʱ,λ3-λ0Ϊʱֵ +#define mCH341A_CMD_I2C_STM_MS 0x50 // I2Cӿڵ:Ϊλʱ,λ3-λ0Ϊʱֵ +#define mCH341A_CMD_I2C_STM_DLY 0x0F // I2Cӿڵʱֵ +#define mCH341A_CMD_I2C_STM_END 0x00 // I2Cӿڵ:ǰ + +#define mCH341A_CMD_UIO_STM_IN 0x00 // UIOӿڵ:D7-D0 +#define mCH341A_CMD_UIO_STM_DIR 0x40 // UIOӿڵ:趨I/OD5-D0,λ5-λ0Ϊ +#define mCH341A_CMD_UIO_STM_OUT 0x80 // UIOӿڵ:D5-D0,λ5-λ0Ϊ +#define mCH341A_CMD_UIO_STM_US 0xC0 // UIOӿڵ:΢Ϊλʱ,λ5-λ0Ϊʱֵ +#define mCH341A_CMD_UIO_STM_END 0x20 // UIOӿڵ:ǰ + +#define MAX_DEVICE_PATH_SIZE 128 // 豸Ƶַ +#define MAX_DEVICE_ID_SIZE 64 // 豸IDַ +#endif _CH341_DLL_H + +//ӿ +#define CH347_USB_VENDOR 0 +#define CH347_USB_HID 2 +#define CH347_USB_VCP 3 + +//CH347_USB_VENDOR֧CH341/7 +#define CHIP_TYPE_CH341 0 +#define CHIP_TYPE_CH347 1 +#define CHIP_TYPE_CH347F 2 +#define CHIP_TYPE_CH347T CHIP_TYPE_CH347 + +//оƬܽӿ +#define CH347_FUNC_UART 0 +#define CH347_FUNC_SPI_IIC 1 +#define CH347_FUNC_JTAG_IIC 2 +#define CH347_FUNC_JTAG_IIC_SPI 3 //CH347F + +#define DEFAULT_READ_TIMEOUT 500 //Ĭ϶ʱ +#define DEFAULT_WRITE_TIMEOUT 500 //Ĭдʱ + +#define mCH347_PACKET_LENGTH 512 // CH347ֵ֧ݰij + +#pragma pack(1) + +//SPI +typedef struct _SPI_CONFIG{ + UCHAR iMode; // 0-3:SPI Mode0/1/2/3 + UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz7=468.75KHz + UCHAR iByteOrder; // 0=λǰ(LSB), 1=λǰ(MSB) + USHORT iSpiWriteReadInterval; // SPIӿڳȡдλΪuS + UCHAR iSpiOutDefaultData; // SPIʱĬ + ULONG iChipSelect; // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ч: λ1λ0Ϊ00/01ֱѡCS1/CS2Ϊ͵ƽЧƬѡ + UCHAR CS1Polarity; // λ0ƬѡCS1Կƣ0͵ƽЧ1ߵƽЧ + UCHAR CS2Polarity; // λ0ƬѡCS2Կƣ0͵ƽЧ1ߵƽЧ + USHORT iIsAutoDeativeCS; // ɺǷԶƬѡ + USHORT iActiveDelay; // Ƭѡִждʱʱ,λus + ULONG iDelayDeactive; // Ƭѡִждʱʱ,λus +}mSpiCfgS,*mPSpiCfgS; + +//豸Ϣ +typedef struct _DEV_INFOR{ + UCHAR iIndex; // ǰ + UCHAR DevicePath[MAX_PATH]; // 豸,CreateFile + UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP + UCHAR FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C + CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx + UCHAR ChipMode; // оƬģʽ,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC) 4:CH347F(Uart*2+Jtag/SPI/IIC) + HANDLE DevHandle; // 豸 + USHORT BulkOutEndpMaxSize; // ϴ˵С + USHORT BulkInEndpMaxSize; // ´˵С + UCHAR UsbSpeedType; // USBٶͣ0:FS,1:HS,2:SS + UCHAR CH347IfNum; // USBӿں: CH347T: IF0:UART; IF1:SPI/IIC/JTAG/GPIO + // CH347F: IF0:UART0; IF1:UART1; IF 2:SPI/IIC/JTAG/GPIO + UCHAR DataUpEndp; // ϴ˵ַ + UCHAR DataDnEndp; // ´˵ַ + CHAR ProductString[64]; // USBƷַ + CHAR ManufacturerString[64]; // USBַ + ULONG WriteTimeout; // USBдʱ + ULONG ReadTimeout; // USBʱ + CHAR FuncDescStr[64]; // ӿڹ + UCHAR FirewareVer; // ̼汾,ʮֵ +}mDeviceInforS,*mPDeviceInforS; + +#pragma pack() + +// CH347ģʽú,֧CH347ģʽµĴ򿪡رաUSBUSBдHID +//USB豸 +HANDLE WINAPI CH347OpenDevice(ULONG DevI); + +//رUSB豸 +BOOL WINAPI CH347CloseDevice(ULONG iIndex); + +//ȡ豸Ϣ +BOOL WINAPI CH347GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation); + +// ȡCH347оƬ:CHIP_TYPE_CH347T/CHIP_TYPE_CH347F +UCHAR WINAPI CH347GetChipType(ULONG iIndex ); // ָ豸 + +// ȡ汾汾豸汾оƬ(CH341(FS)/CH347HS) +BOOL WINAPI CH347GetVersion(ULONG iIndex, + PUCHAR iDriverVer, + PUCHAR iDLLVer, + PUCHAR ibcdDevice, + PUCHAR iChipType); //CHIP_TYPE_CH341/7 + +typedef VOID ( CALLBACK * mPCH347_NOTIFY_ROUTINE ) ( // 豸֪ͨ¼ص + ULONG iEventStatus ); // 豸¼͵ǰ״̬(ж): 0=豸γ¼, 3=豸¼ + +#define CH347_DEVICE_ARRIVAL 3 // 豸¼,Ѿ +#define CH347_DEVICE_REMOVE_PEND 1 // 豸Ҫγ +#define CH347_DEVICE_REMOVE 0 // 豸γ¼,Ѿγ + +// 趨豸¼֪ͨ +BOOL WINAPI CH347SetDeviceNotify(ULONG iIndex, // ָ豸,0Ӧһ豸 + PCHAR iDeviceID, // ѡ,ַָ,ָص豸ID,ַ\0ֹ + mPCH347_NOTIFY_ROUTINE iNotifyRoutine ); // ָ豸¼ص,ΪNULLȡ¼֪ͨ,ڼ⵽¼ʱøó + +// ȡUSBݿ +BOOL WINAPI CH347ReadData( ULONG iIndex, // ָ豸 + PVOID oBuffer, // ָһ㹻Ļ,ڱȡ + PULONG ioLength ); // ָ򳤶ȵԪ,ʱΪ׼ȡij,غΪʵʶȡij + +// дȡUSBݿ +BOOL WINAPI CH347WriteData(ULONG iIndex, // ָ豸 + PVOID iBuffer, // ָһ,׼д + PULONG ioLength ); // ָ򳤶ȵԪ,ʱΪ׼дij,غΪʵдij + +// USBݶдijʱ +BOOL WINAPI CH347SetTimeout(ULONG iIndex, // ָ豸 + ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + +/***************SPI********************/ +// SPIʼ +BOOL WINAPI CH347SPI_Init(ULONG iIndex,mSpiCfgS *SpiCfg); + +//CH347FSPIʱƵʡiSpiSpeedHz= 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz 7=468.75KHz +BOOL WINAPI CH347SPI_SetFrequency(ULONG iIndex, ULONG iSpiSpeedHz); + +//CH347FSPI֧λ. +BOOL WINAPI CH347SPI_SetDataBits(ULONG iIndex, + UCHAR iDataBits); //iDataBits= 0:8bit, 1:16bit + +//ȡSPIϢ +BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex,mSpiCfgS *SpiCfg); + +//Ƭѡ״̬,ʹǰȵCH347SPI_InitCS +BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // ָ豸 + UCHAR iStatus); // 0=Ƭѡ,1=Ƭѡ + +//SPIƬѡ +BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // ָ豸 + USHORT iEnableSelect, // ͰλΪCS1߰λΪCS2; ֵֽΪ1=CS,Ϊ0=ԴCS + USHORT iChipSelect, // ͰλΪCS1߰λΪCS2;Ƭѡ,0=Ƭѡ,1=Ƭѡ + ULONG iIsAutoDeativeCS, // 16λΪCS116λΪCS2;ɺǷԶƬѡ + ULONG iActiveDelay, // 16λΪCS116λΪCS2;Ƭѡִждʱʱ,λus + ULONG iDelayDeactive); // 16λΪCS116λΪCS2;Ƭѡִждʱʱ,λus + +//SPI4д +BOOL WINAPI CH347SPI_Write(ULONG iIndex, // ָ豸 + ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ + ULONG iLength, // ׼ֽ + ULONG iWriteStep, // ׼ȡĵij + PVOID ioBuffer); // ָһ,׼MOSIд + +//SPI4.дݣЧʽCH347SPI_WriteReadߺܶ +BOOL WINAPI CH347SPI_Read(ULONG iIndex, // ָ豸 + ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ + ULONG oLength, // ׼ֽ + PULONG iLength, // ׼ֽ + PVOID ioBuffer); // ָһ,׼DOUTд,غǴDIN + +// SPI,4߽ӿ +BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // ָ豸 + ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ + ULONG iLength, // ׼ֽ + PVOID ioBuffer ); // ָһ,׼DOUTд,غǴDIN + +// SPI,4߽ӿ +BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // ָ豸 + ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ч + ULONG iLength, // ׼ֽ + PVOID ioBuffer ); // ָһ,׼DOUTд,غǴDIN + +/***************JTAG********************/ +//JTAGӿڳʼģʽٶ +BOOL WINAPI CH347Jtag_INIT(ULONG iIndex, + UCHAR iClockRate); //ͨٶȣЧֵΪ0-5ֵԽͨٶԽ + +//ȡJtagٶ +BOOL WINAPI CH347Jtag_GetCfg(ULONG iIndex, // ָ豸 + UCHAR *ClockRate); //ͨٶȣЧֵΪ0-5ֵԽͨٶԽ + + +//ıTMSֵ״̬л +BOOL WINAPI CH347Jtag_TmsChange(ULONG iIndex, // 豸 + PUCHAR tmsValue, // лTMSλֵ,ֽΪλ + ULONG Step, // tmsValueڴ洢TMSЧλ + ULONG Skip); // Чʼλ + +//shift-dr/ir״̬ждִExit DR/IR +//״̬:Shift-DR/IR.RW.->Exit DR/IR +BOOL WINAPI CH347Jtag_IoScan(ULONG iIndex, + PUCHAR DataBits, //Ҫдλ + ULONG DataBitsNb, //Ҫݵλ + BOOL IsRead); //ǷҪȡ + +//shift-dr/ir״̬жд,ִɺ,һExit DR/IR;,ͣShift-DR/IR +//״̬:Shift-DR/IR.RW..->[Exit DR/IR] +BOOL WINAPI CH347Jtag_IoScanT(ULONG iIndex, // 豸 + PUCHAR DataBits, // Ҫдλ + ULONG DataBitsNb, // Ҫݵλ + BOOL IsRead, // ǷҪȡ + BOOL IsLastPkt); // ǷΪһ + +//CH347 λTap״̬.TCKTMSΪ߽ɽ״̬ΪTest-Logic Reset״̬ +ULONG WINAPI CH347Jtag_Reset(ULONG iIndex); + +//CH347 TRSTӲλ +BOOL WINAPI CH347Jtag_ResetTrst(ULONG iIndex, BOOL TRSTLevel); + +//λʽJTAG IR/DRݶд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹCH347Jtag_WriteRead_Fast +//4096ֽΪλд +//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead(ULONG iIndex, // ָ豸 + BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд + ULONG iWriteBitLength, // д,׼дij + PVOID iWriteBitBuffer, // ָһ,׼д + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡij + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//JTAG IR/DRд,ڶֽдJTAG̼زӲ4KдȲ4096ֽڡСе +//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead_Fast(ULONG iIndex, // ָ豸 + BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд + ULONG iWriteBitLength, // д,׼дij + PVOID iWriteBitBuffer, // ָһ,׼д + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡij + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//λʽJTAG IR/DRݶд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹCH347Jtag_WriteRead_Fast +//4096ֽΪλд +//״̬:Run-Test-> Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteReadEx(ULONG iIndex, // ָ豸 + BOOL IsInDrOrIr, // =TRUE: SHIFT-DR/IR״̬ݽ ==FALSE: Run-Test->Shift-IR/DR.ݽ.->Exit IR/DR -> Run-Test + BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд + ULONG iWriteBitLength, // д,׼дij + PVOID iWriteBitBuffer, // ָһ,׼д + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡij + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//JTAG IR/DRд,ڶֽдJTAG̼زӲ4KдȲ4096ֽڡСе +//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead_FastEx(ULONG iIndex, // ָ豸 + BOOL IsInDrOrIr, // =TRUE: SHIFT-DR/IR״̬ݽ ==FALSE: Run-Test->Shift-IR/DR.ݽ.->Exit IR/DR -> Run-Test + BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд + ULONG iWriteBitLength, // д,׼дij + PVOID iWriteBitBuffer, // ָһ,׼д + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡij + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//лJTAG״̬ +BOOL WINAPI CH347Jtag_SwitchTapState(UCHAR TapState); + +//JTAG DRд,ֽΪλ,ڶֽдJTAG̼ز +//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_ByteWriteDR(ULONG iIndex, // ָ豸 + ULONG iWriteLength, // д,׼дֽڳ + PVOID iWriteBuffer); // ָһ,׼д + +//JTAG DR,ֽΪλ,ֽ +//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_ByteReadDR(ULONG iIndex, // ָ豸 + PULONG oReadLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID oReadBuffer ); // ָһ㹻Ļ,ڱȡ + +//JTAG IRд,ֽΪλ,ֽд +//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_ByteWriteIR(ULONG iIndex, // ָ豸 + ULONG iWriteLength, // д,׼дֽڳ + PVOID iWriteBuffer); // ָһ,׼д + +//JTAG IR,ֽΪλ,ֽд +//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_ByteReadIR(ULONG iIndex, // ָ豸 + PULONG oReadLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID oReadBuffer ); // ָһ㹻Ļ,ڱȡ + +//λʽJTAG DRд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹUSB20Jtag_ByeWriteDR +//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_BitWriteDR(ULONG iIndex, // ָ豸 + ULONG iWriteBitLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID iWriteBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//λʽJTAG IRд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹUSB20Jtag_ByteWriteIR +//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_BitWriteIR(ULONG iIndex, // ָ豸 + ULONG iWriteBitLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID iWriteBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//λʽJTAG IRݶ.ݵĶдָ״̬лȡݴ䣬ʹUSB20Jtag_ByteReadIR +//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_BitReadIR(ULONG iIndex, // ָ豸 + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +//λʽJTAG DRݶ.ݵĶд͸ݴ䣬ʹUSB20Jtag_ByteReadDR +//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_BitReadDR(ULONG iIndex, // ָ豸 + PULONG oReadBitLength, // ָ򳤶ȵԪ,غΪʵʶȡֽڳ + PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ + +/***************GPIO********************/ +//ȡCH347GPIOŵƽֵ +BOOL WINAPI CH347GPIO_Get(ULONG iIndex, // ָ豸 + UCHAR *iDir, // ŷ:GPIO0-7Ӧλ0-7.0룻1 + UCHAR *iData); // GPIO0ƽ:GPIO0-7Ӧλ0-7,0͵ƽ1ߵƽ) + +//CH347GPIOŵƽֵ +BOOL WINAPI CH347GPIO_Set(ULONG iIndex, // ָ豸 + UCHAR iEnable, // Ч־:Ӧλ0-7,ӦGPIO0-7. + UCHAR iSetDirOut, // I/O,ijλ0ӦΪ,ijλ1ӦΪ.GPIO0-7Ӧλ0-7. + UCHAR iSetDataOut); // ,I/OΪ,ôijλ0ʱӦ͵ƽ,ijλ1ʱӦߵƽ + + +typedef VOID ( CALLBACK * mPCH347_INT_ROUTINE ) ( // жϷ + PUCHAR iStatus ); // ж״̬,ολ˵ +// 8ֽGPIO0-7״̬.ÿֽλ: +// λ7ǰGPIO00룻1 +// λ6ǰGPIO0ƽ0͵ƽ1ߵƽ +// λ5ǰGPIO0ǷΪжϣ0ѯģʽ1жģʽ +// λ4-3GPIO0жģʽ00½ش01ش;10˫ش11: ; +// λ2-0 + +// 趨GPIOжϷ +BOOL WINAPI CH347SetIntRoutine(ULONG iIndex, // ָ豸 + UCHAR Int0PinN, // ж0 GPIOź,7:ôжԴ; Ϊ0-7Ӧgpio0-7 + UCHAR Int0TripMode, // ж0: 00:½ش; 01:ش; 02:˫ش; 03:; + UCHAR Int1PinN, // ж1 GPIOź,7ôжԴ,Ϊ0-7Ӧgpio0-7 + UCHAR Int1TripMode, // ж1: 00:½ش; 01:ش; 02:˫ش; 03:; + mPCH347_INT_ROUTINE iIntRoutine );// ָжϷ,ΪNULLȡжϷ,жʱøó + +// ȡж +BOOL WINAPI CH347ReadInter(ULONG iIndex, // ָ豸 + PUCHAR iStatus ); // ָ8ֽڵԪ,ֱΪGPIO0-7״̬,ÿֽλ˵οжϷiStatusλ˵ + +// жݶ +BOOL WINAPI CH347AbortInter(ULONG iIndex ); // ָ豸 + +//IAP̼ģʽ +BOOL WINAPI CH347StartIapFwUpate(ULONG iIndex, + ULONG FwSize); // ̼ + +/**************HID/VCP**********************/ +//򿪴 +HANDLE WINAPI CH347Uart_Open(ULONG iIndex); + +//رմ +BOOL WINAPI CH347Uart_Close(ULONG iIndex); + +BOOL WINAPI CH347Uart_SetDeviceNotify( // 趨豸¼֪ͨ + ULONG iIndex, // ָ豸,0Ӧһ豸 + PCHAR iDeviceID, // ѡ,ַָ,ָص豸ID,ַ\0ֹ + mPCH347_NOTIFY_ROUTINE iNotifyRoutine ); // ָ豸¼ص,ΪNULLȡ¼֪ͨ,ڼ⵽¼ʱøó + +//ȡUARTӲ +BOOL WINAPI CH347Uart_GetCfg(ULONG iIndex, // ָ豸 + PULONG BaudRate, // + PUCHAR ByteSize, // λ(5,6,7,8,16) + PUCHAR Parity, // Уλ(0None; 1Odd; 2Even; 3Mark; 4Space) + PUCHAR StopBits, // ֹͣλ(01ֹͣλ; 11.5ֹͣλ; 22ֹͣλ) + PUCHAR ByteTimeout); // ֽڳʱ + +//UART +BOOL WINAPI CH347Uart_Init(ULONG iIndex, // ָ豸 + DWORD BaudRate, // + UCHAR ByteSize, // λ(5,6,7,8,16) + UCHAR Parity, // Уλ(0None; 1Odd; 2Even; 3Mark; 4Space) + UCHAR StopBits, // ֹͣλ(01ֹͣλ; 11.5ֹͣλ; 22ֹͣλ) + UCHAR ByteTimeout);// ֽڳʱʱ,λ100uS + +// USBݶдijʱ +BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // ָ豸 + ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + +// ȡݿ +BOOL WINAPI CH347Uart_Read(ULONG iIndex, // ָ豸 + PVOID oBuffer, // ָһ㹻Ļ,ڱȡ + PULONG ioLength );// ָ򳤶ȵԪ,ʱΪ׼ȡij,غΪʵʶȡij +// дݿ +BOOL WINAPI CH347Uart_Write(ULONG iIndex, // ָ豸 + PVOID iBuffer, // ָһ,׼д + PULONG ioLength );// ָ򳤶ȵԪ,ʱΪ׼дij,غΪʵдij + +//ѯжֽδȡ +BOOL WINAPI CH347Uart_QueryBufUpload(ULONG iIndex, // ָ豸 + LONGLONG *RemainBytes); // δȡֽ + +//ȡ豸Ϣ +BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation); + +// USBݶдijʱ +BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // ָ豸 + ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ) + +/********IIC***********/ +// I2C +BOOL WINAPI CH347I2C_Set(ULONG iIndex, // ָ豸 + ULONG iMode ); // ָģʽ, +// λ1-λ0: I2Cӿٶ/SCLƵ, 00=/20KHz,01=׼/100KHz(Ĭֵ),10=/400KHz,11=/750KHz +// ,Ϊ0 + +//Set I2C Clock Stretch +BOOL WINAPI CH347I2C_SetStretch(ULONG iIndex, // ָ豸 + BOOL iEnable); // I2C Clock Stretch enable, 1:enable,0:disable + +// Ӳ첽ʱ,úܿ췵,һ֮ǰʱָ +BOOL WINAPI CH347I2C_SetDelaymS(ULONG iIndex, // ָ豸 + ULONG iDelay ) ; // ָʱĺ + + // I2C,2߽ӿ,ʱΪSCL,ΪSDA +BOOL WINAPI CH347StreamI2C( ULONG iIndex, // ָ豸 + ULONG iWriteLength, // ׼дֽ + PVOID iWriteBuffer, // ָһ,׼д,ֽͨI2C豸ַдλ + ULONG iReadLength, // ׼ȡֽ + PVOID oReadBuffer ); // ָһ,غǶ + +BOOL WINAPI CH347StreamI2C_RetACK( // I2C,2߽ӿ,ʱΪSCL,ΪSDA(׼˫I/O),ٶԼ56Kֽ,˻ȡACK + ULONG iIndex, // ָCH341豸 + ULONG iWriteLength, // ׼дֽ + PVOID iWriteBuffer, // ָһ,׼д,ֽͨI2C豸ַдλ + ULONG iReadLength, // ׼ȡֽ + PVOID oReadBuffer, // ָһ,غǶ + PULONG rAckCount); // ָдصACKֵ + +#ifndef _CH341_DLL_H +typedef enum _EEPROM_TYPE {// EEPROMͺ + ID_24C01, + ID_24C02, + ID_24C04, + ID_24C08, + ID_24C16, + ID_24C32, + ID_24C64, + ID_24C128, + ID_24C256, + ID_24C512, + ID_24C1024, + ID_24C2048, + ID_24C4096 +} EEPROM_TYPE; +#endif + +// EEPROMжȡݿ,ٶԼ56Kֽ +BOOL WINAPI CH347ReadEEPROM(ULONG iIndex, // ָ豸 + EEPROM_TYPE iEepromID, // ָEEPROMͺ + ULONG iAddr, // ָݵԪĵַ + ULONG iLength, // ׼ȡֽ + PUCHAR oBuffer ); // ָһ,غǶ +// EEPROMдݿ +BOOL WINAPI CH347WriteEEPROM(ULONG iIndex, // ָ豸 + EEPROM_TYPE iEepromID, // ָEEPROMͺ + ULONG iAddr, // ָݵԪĵַ + ULONG iLength, // ׼дֽ + PUCHAR iBuffer ); // ָһ,׼д + +#ifdef __cplusplus +} +#endif + +#endif // _CH347_DLL_H diff --git a/FlashGBX/bacon/include/CH347DLL_EN.H b/FlashGBX/bacon/include/CH347DLL_EN.H new file mode 100644 index 0000000..a41c333 --- /dev/null +++ b/FlashGBX/bacon/include/CH347DLL_EN.H @@ -0,0 +1,610 @@ +/***************************************************************************** +** Copyright (C) WCH 2001-2023 ** +** Web: http://wch.cn ** +*****************************************************************************/ + +//USB bus interface chip CH341/7 parallel port application layer interface library, CH347 based on 480mbps high-speed USB bus, extends UART/SPI/I2C/JTAG +//CH347-DLL V1.2 +//Environment: Windows 98/ME, Windows 2000/XP, WIN7/8/10/11,and later. +//support USB chip: CH341, CH341A,CH347 +//USB => Parallel, I2C, SPI, JTAG, SWD, UART ... +//Notes: +//Copyright (c) 2023 Nanjing Qinheng Microelectronics Co., Ltd. + + +#ifndef _CH347_DLL_H +#define _CH347_DLL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN64 +#define mOFFSET(s, m) ((ULONG_PTR) & (((s *)0)->m)) // Define macro, get relative offset address of structure member +#else +#define mOFFSET(s, m) ((ULONG) & (((s *)0)->m)) // Define macro, get relative offset address of structure member +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) // Determine maximum value +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) // Determine minimum value +#endif + +#ifdef ExAllocatePool +#undef ExAllocatePool // Delete memory allocation with TAG +#endif + +#ifndef NTSTATUS +typedef LONG NTSTATUS; // Return status +#endif + +// Sharing CH341WDM driver with CH341DLL +#ifndef _CH341_DLL_H +typedef struct _USB_SETUP_PKT { // USB control transfer structure + UCHAR mUspReqType; // 00H request type + UCHAR mUspRequest; // 01H request code + union { + struct { + UCHAR mUspValueLow; // 02H Value parameter low byte + UCHAR mUspValueHigh; // 03H Value parameter high byte + }; + USHORT mUspValue; // 02H-03H value parameters + }; + union { + struct { + UCHAR mUspIndexLow; // 04H index parameter low byte + UCHAR mUspIndexHigh; // 05H index parameter high byte + }; + USHORT mUspIndex; // 04H-05H index parameter + }; + USHORT mLength; // 06H-07H data length +} mUSB_SETUP_PKT, *mPUSB_SETUP_PKT; + +typedef struct _WIN32_COMMAND { // Define WIN32 command interface structure + union { + ULONG mFunction; // Specify function code or pipe number when input + NTSTATUS mStatus; // Return operation status when output + }; + ULONG mLength; // Access length, return the length of subsequent data + union { + mUSB_SETUP_PKT mSetupPkt; // Data request in the setup phase of USB control transfer + UCHAR mBuffer[512]; // Data buffer, the length is 0 to 255B + }; +} mWIN32_COMMAND, *mPWIN32_COMMAND; +// WIN32 application layer interface command +#define IOCTL_CH341_COMMAND (FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED) // Private interface + +#define mWIN32_COMMAND_HEAD mOFFSET(mWIN32_COMMAND, mBuffer) // Header length of WIN32 command interface + +#define mCH341_MAX_NUMBER 32 // Maximum number of CH341/7 connected at the same time + +#define mMAX_BUFFER_LENGTH 0x1000 // Maximum length of the data buffer is 4096 + +#define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // Maximum data length plus command structure header length + +#define mDEFAULT_BUFFER_LEN 0x0400 // Data buffer default length is 1024 + +#define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // default data length plus command structure header length + +// CH341 endpoint address +#define mCH347_ENDP_DATA_UP 0x86 // Data upload endpoint of CH347 +#define mCH347_ENDP_DATA_DOWN 0x06 // Data download endpoint of CH347 + +// Pipeline operation command provided by equipment layer interface +#define mPipeDeviceCtrl 0x00000004 // CH347 integrated control pipeline +#define mPipeDataUp 0x00000006 // CH347 data block upload pipeline +#define mPipeDataDown 0x00000007 // CH347 data block download pipeline + +// Function code of application layer interface +#define mFuncNoOperation 0x00000000 // No operation +#define mFuncGetVersion 0x00000001 // Get driver version number +#define mFuncGetConfig 0x00000002 // Get USB device configuration descriptor +#define mFuncSetTimeout 0x00000009 // Set USB communication timeout +#define mFuncSetExclusive 0x0000000b // Set exclusive use +#define mFuncResetDevice 0x0000000c // Reset USB device +#define mFuncResetPipe 0x0000000d // Reset USB pipe +#define mFuncAbortPipe 0x0000000e // Cancel data request of USB pipe +#define mFuncBufferMode 0x00000020 // Set buffer upload mode and query data length in the buffer +#define mFuncBufferModeDn 0x00000021 // Set buffer download mode and query data length in the buffer +#define mFuncGetVersionEx 0x00000022 // Get driver version number and chip model +// USB device standard request code +#define mUSB_CLR_FEATURE 0x01 +#define mUSB_SET_FEATURE 0x03 +#define mUSB_GET_STATUS 0x00 +#define mUSB_SET_ADDRESS 0x05 +#define mUSB_GET_DESCR 0x06 +#define mUSB_SET_DESCR 0x07 +#define mUSB_GET_CONFIG 0x08 +#define mUSB_SET_CONFIG 0x09 +#define mUSB_GET_INTERF 0x0a +#define mUSB_SET_INTERF 0x0b +#define mUSB_SYNC_FRAME 0x0c + +// Vendor specific request type of CH341 control transfer +#define mCH341_VENDOR_READ 0xC0 // CH341 vendor-specific read operation via control transfer +#define mCH341_VENDOR_WRITE 0x40 // CH341 vendor-specific write operation via control transfer + +#define mCH341A_CMD_I2C_STREAM 0xAA // Command package of I2C, starting with the second byte is I2C command stream +#define mCH341A_CMD_UIO_STREAM 0xAB // Command package of UIO, starting with the second byte is command stream +#define mCH341A_CMD_PIO_STREAM 0xAE // Command package of PIO, starting with the second byte is data stream +// Vendor specific request code of CH341A control transfer +#define mCH341A_BUF_CLEAR 0xB2 // Clear incomplete data +#define mCH341A_I2C_CMD_X 0x54 // Issue command of I2C interface and executes it immediately +#define mCH341A_DELAY_MS 0x5E // Delay specified time, in millisecond +#define mCH341A_GET_VER 0x5F // Get chip version + +#define mCH341A_CMD_I2C_STM_STA 0x74 // I2C interface command stream: generate start bit +#define mCH341A_CMD_I2C_STM_STO 0x75 // I2C interface command stream: generate stop bit +#define mCH341A_CMD_I2C_STM_OUT 0x80 // I2C interface command stream: output data, bit5 - bit0 is length, subsequent bytes are data, length 0 only sends one byte and returns an response +#define mCH341A_CMD_I2C_STM_IN 0xC0 // I2C interface command stream: input data, bit 5-bit 0 is length, length 0 only receives one byte and sends no response +#define mCH341A_CMD_I2C_STM_MAX (min(0x3F, mCH341_PACKET_LENGTH)) // Maximum length of a single command input/output data of I2C interface command stream +#define mCH341A_CMD_I2C_STM_SET 0x60 // I2C interface command stream: set parameters, bit2=SPI I/O number (0= single input single output, 1= double input double output), bit1 bit0=I2C speed (00= low speed, 01= standard, 10= fast, 11= high speed) +#define mCH341A_CMD_I2C_STM_US 0x40 // I2C interface command stream: delay in microseconds, bit3 - bit0 is delay value +#define mCH341A_CMD_I2C_STM_MS 0x50 // I2C interface command stream: delay in milliseconds, bit3 - bit0 is delay value +#define mCH341A_CMD_I2C_STM_DLY 0x0F // Maximum value of a single command delay of I2C interface command stream +#define mCH341A_CMD_I2C_STM_END 0x00 // I2C interface command stream: command package ends in advance + +#define mCH341A_CMD_UIO_STM_IN 0x00 // UIO interface command stream: input data D7-D0 +#define mCH341A_CMD_UIO_STM_DIR 0x40 // UIO interface command stream: set I/O direction D5-D0, bit5 - bit0 is direction data +#define mCH341A_CMD_UIO_STM_OUT 0x80 // UIO interface command stream: output data D5-D0, bit5 - bit0 is data +#define mCH341A_CMD_UIO_STM_US 0xC0 // UIO interface command stream: delay in microseconds, bit5 - bit0 is delay value +#define mCH341A_CMD_UIO_STM_END 0x20 // UIO interface command stream: command package ends in advance + +#define MAX_DEVICE_PATH_SIZE 128 // Maximum number of characters for device name +#define MAX_DEVICE_ID_SIZE 64 // Maximum number of characters for device ID +#endif _CH341_DLL_H + +// Driver interface +#define CH347_USB_VENDOR 0 +#define CH347_USB_HID 2 +#define CH347_USB_VCP 3 + +// CH347_USB_VENDOR support CH341/7 +#define CHIP_TYPE_CH341 0 +#define CHIP_TYPE_CH347 1 +#define CHIP_TYPE_CH347F 2 +#define CHIP_TYPE_CH347T CHIP_TYPE_CH347 + +// Chip function interface type +#define CH347_FUNC_UART 0 +#define CH347_FUNC_SPI_IIC 1 +#define CH347_FUNC_JTAG_IIC 2 +#define CH347_FUNC_JTAG_IIC_SPI 3 // CH347F + +#define DEFAULT_READ_TIMEOUT 500 // Default read timeout milliseconds +#define DEFAULT_WRITE_TIMEOUT 500 // Default write timeout milliseconds + +#define mCH347_PACKET_LENGTH 512 // Length of data packet supported by CH347 +#pragma pack(1) +// SPI controller configuration +typedef struct _SPI_CONFIG { + UCHAR iMode; // 0-3:SPI Mode0/1/2/3 + UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz + UCHAR iByteOrder; // 0=LSB, 1=MSB + USHORT iSpiWriteReadInterval; // SPI regular read/write data command, in uS + UCHAR iSpiOutDefaultData; // Default output data when SPI reads data + ULONG iChipSelect; // Chip select control, Bit7=0: ignore chip select control; bit7=1: parameter is valid, bit1/bit0 are 00/01 to select the CS1/CS2 pins as low-level active chip select, respectively. + UCHAR CS1Polarity; // Bit0: CS1 polarity control: 0: active low; 1: active high; + UCHAR CS2Polarity; // Bit0: CS2 polarity control: 0: active low; 1: active high; + USHORT iIsAutoDeativeCS; // Auto undo chip selection or not after operation is completed + USHORT iActiveDelay; // Setting delay time for performing read/write operations after chip selection, in uS + ULONG iDelayDeactive; // Delay time for read/write operations after de-selecting chip selection, in uS +} mSpiCfgS, *mPSpiCfgS; + +// Device information +typedef struct _DEV_INFOR { + UCHAR iIndex; // Currently open index + UCHAR DevicePath[MAX_PATH]; // Device link name, used for CreateFile + UCHAR UsbClass; // Driver Type 0:CH347_USB_CH341, 2:CH347_USB_HID, 3:CH347_USB_VCP + UCHAR FuncType; // Functional Type 0:CH347_FUNC_UART, 1:CH347_FUNC_SPI_I2C, 2:CH347_FUNC_JTAG_I2C + CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx + UCHAR ChipMode; // Chip Mode, 0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+I2C) 4:CH347F(Uart*2+Jtag/SPI/I2C) + HANDLE DevHandle; // Device handle + USHORT BulkOutEndpMaxSize; // Upload endpoint size + USHORT BulkInEndpMaxSize; // Downstream endpoint size + UCHAR UsbSpeedType; // USB speed, 0:FS,1:HS,2:SS + UCHAR CH347IfNum; // USB interface number: CH347T: IF0:UART; IF1:SPI/IIC/JTAG/GPIO + // CH347F: IF0:UART0; IF1:UART1; IF2:SPI/IIC/JTAG/GPIO + UCHAR DataUpEndp; // Bulk upload endpoint address + UCHAR DataDnEndp; // Bulk download endpoint address + CHAR ProductString[64]; // USB product string + CHAR ManufacturerString[64]; // USB manufacturer string + ULONG WriteTimeout; // USB write timeout + ULONG ReadTimeout; // USB read timeout + CHAR FuncDescStr[64]; // Interface function descriptor + UCHAR FirewareVer; // Firmware version, hex value + +} mDeviceInforS, *mPDeviceInforS; +#pragma pack() + +// CH347 each mode public function, support CH347 all modes of open, close, USB read, USB write, including HID +// Open USB device +HANDLE WINAPI CH347OpenDevice(ULONG DevI); + +// Close USB device +BOOL WINAPI CH347CloseDevice(ULONG iIndex); + +// Get device information +BOOL WINAPI CH347GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation); + +// Get CH347 chip type:CHIP_TYPE_CH347T/CHIP_TYPE_CH347F +UCHAR WINAPI CH347GetChipType(ULONG iIndex); // Specify device serial number + +// Obtain driver version, library version, device version and chip type(CH341(FS)/CH347HS) +BOOL WINAPI CH347GetVersion(ULONG iIndex, + PUCHAR iDriverVer, + PUCHAR iDLLVer, + PUCHAR ibcdDevice, + PUCHAR iChipType); // CHIP_TYPE_CH341/7 + +typedef VOID(CALLBACK *mPCH347_NOTIFY_ROUTINE)( // Device plug/unplug notification event callback routine + ULONG iEventStatus); // Device plug/unplug event and current status (define below): 0=Device unplug event, 3=Device insertion event + +#define CH347_DEVICE_ARRIVAL 3 // Device insertion event, has been inserted +#define CH347_DEVICE_REMOVE_PEND 1 // Device wil be unplugged +#define CH347_DEVICE_REMOVE 0 // Device unplug event, has been pulled out + +// Set the device event notification routine +BOOL WINAPI CH347SetDeviceNotify(ULONG iIndex, // Specify device serial number, 0 corresponds to the first device + PCHAR iDeviceID, // Optional parameter, points to a string, specifying the monitored device ID, ending with \0. + mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies the port device event callback program. If it is NULL, tehn cancel the event notification, otherwise call the routine when an event is detected. + +// Read USB data block +BOOL WINAPI CH347ReadData(ULONG iIndex, // Specify device serial number + PVOID oBuffer, // Points to a buffer large enough to save the read data + PULONG ioLength); // Points to length unit; When input, it is the length to be read, and after return, it is the actual length to be read. + +// Write USB data block +BOOL WINAPI CH347WriteData(ULONG iIndex, // Specify device serial number + PVOID iBuffer, // Points to a buffer large enough to save the written data + PULONG ioLength); // Points to length unit; When input, it is the length to be written, and after return, it is the actual length to be written. + +// Set the timeout of USB data read/write +BOOL WINAPI CH347SetTimeout(ULONG iIndex, // Specify device serial number + ULONG iWriteTimeout, // Specifies the timeout for USB to write data blocks, in mS, 0xFFFFFFFF specifies no timeout (default) + ULONG iReadTimeout); // Specifies the timeout for USB to read data blocks, in mS, 0xFFFFFFFF specifies no timeout (default) + +/***************SPI********************/ +// SPI controller initialization +BOOL WINAPI CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg); + +//CH347F set SPI clock frequency. iSpiSpeedHz= 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz$17 7=468.75KHz +BOOL WINAPI CH347SPI_SetFrequency(ULONG iIndex, ULONG iSpiSpeedHz); + +// CH347F set the SPI databit +BOOL WINAPI CH347SPI_SetDataBits(ULONG iIndex, + UCHAR iDataBits); // iDataBits= 0:8bit, 1:16bit + +// Get SPI controller configuration information +BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg); + +// Sets the chip select state, call CH347SPI_Init to set up the CS before use +BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // Specify device serial number + UCHAR iStatus); // 0= Undo chip select, 1=Set chip select + +// Set SPI chip select +BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // Specify device serial number + USHORT iEnableSelect, // Lower 8-bit is CS1, higher 8-bit is CS2; A byte value of 1= sets CS, 0= ignores this CS setting + USHORT iChipSelect, // Lower 8-bit is CS1, higher 8-bit is CS2; A byte value of 1= sets CS, 0= ignores this CS setting + ULONG iIsAutoDeativeCS, // Lower 16-bit is CS1, higher 16-bit is CS2; auto undo chip selection or not after operation is completed + ULONG iActiveDelay, // Lower 16-bit is CS1, higher 16-bit is CS2; set the delay time of performing read/write operations after chip selection, in us + ULONG iDelayDeactive); // Lower 116-bit is CS1, higher 16-bit is CS2; do not set the delay time of performing read/write operations after chip selection, in us + +// SPI4 write data +BOOL WINAPI CH347SPI_Write(ULONG iIndex, // Specify device serial number + ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select + ULONG iLength, // Number of data bytes ready to be transferred + ULONG iWriteStep, // Length of a single block to be read + PVOID ioBuffer); // Point to a buffer, place the data to be written out from MOSI + +// SPI4 read data, no need to write data first, much more efficient than CH347SPI_WriteRead +BOOL WINAPI CH347SPI_Read(ULONG iIndex, // Specify device serial number + ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select + ULONG oLength, // Number of data bytes ready to be transferred + PULONG iLength, // Number of data bytes of data to be read + PVOID ioBuffer); // Points to a buffer, place the data to be written out from DOUT, returned data is read from DIN + +// Handle SPI data stream, 4-wire interface +BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // Specify device serial number + ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select + ULONG iLength, // Number of data bytes ready to be transferred + PVOID ioBuffer); // Points to a buffer that place the data to be written out from DOUT, returned data is read from DIN + +// Handle SPI data stream, 4-wire interface +BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // Specify device serial number + ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: parameter is valid + ULONG iLength, // Number of data bytes ready to be transferred + PVOID ioBuffer); // Points to a buffer, places data to be written out from DOUT, returned data is read from DIN + +/***************JTAG********************/ +// JTAG interface initialization, set mode and speed +BOOL WINAPI CH347Jtag_INIT(ULONG iIndex, + UCHAR iClockRate); // Communication speed; valid values are 0-5, larger values indicating faster speeds + +// Gets Jtag speed configuration +BOOL WINAPI CH347Jtag_GetCfg(ULONG iIndex, // Specify device serial number + UCHAR *ClockRate); // Communication speed; valid values are 0-5, larger values indicating faster speeds + +// Changing the TMS value for state switching +BOOL WINAPI CH347Jtag_TmsChange(ULONG iIndex, // Specify device serial number + PUCHAR tmsValue, // TMS bit value for switching, in bytes + ULONG Step, // Number of valid TMS bits stored in tmsValue + ULONG Skip); // Valid start bit + +// Read/write data in the shift-dr/ir state, switch to Exit DR/IR state after execution. +// State machine: Shift-DR/ ir.rw.->Exit DR/IR +BOOL WINAPI CH347Jtag_IoScan(ULONG iIndex, + PUCHAR DataBits, // Data bits to be transmitted + ULONG DataBitsNb, // Number of bits of data to be transmitted + BOOL IsRead); // Read data or not + +// Switch to shift-dr/ir state for read/write, after execution is complete, If it is the last packet, then shift to Exit DR/IR; if not, then in Shift-DR/IR +// State machine :Shift-DR/IR.RW.. ->[Exit DR/IR] +BOOL WINAPI CH347Jtag_IoScanT(ULONG iIndex, // Specify the device number + PUCHAR DataBits, // Data bits to be transmitted + ULONG DataBitsNb, // Number of bits of data to be transmitted + BOOL IsRead, // Read data or not + BOOL IsLastPkt); // Is it the last packet or not + +// CH347 reset Tap state function. six or more consecutive TCKs & TMS are high��will set the state machine to Test-Logic Reset +ULONG WINAPI CH347Jtag_Reset(ULONG iIndex); + +// CH347 operates TRST to complete a hardware reset +BOOL WINAPI CH347Jtag_ResetTrst(ULONG iIndex, BOOL TRSTLevel); + +// Bit band mode JTAG IR/DR data read/write, suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_WriteRead_Fast +// Command packages are read/write in batches of 4096 bytes +// State machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead(ULONG iIndex, // Specify device serial number + BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write + ULONG iWriteBitLength, // Write length, the length to be written + PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out + PULONG oReadBitLength, // Points to the length unit, returned length is the actual length read + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +// JTAG IR/DR data batch read/write, for multi-byte sequential read/write. Such as JTAG download firmware. Because the hardware has a 4K buffer, if you write first and read later, the length will not exceed 4096 bytes. The buffer size can be adjusted by yourself +// State machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead_Fast(ULONG iIndex, // Specify device serial number + BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write + ULONG iWriteBitLength, // Write length, length to be written + PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out + PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read. + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +// Bitband mode JTAG IR/DR data read/write, suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_WriteRead_Fast +// Command packages are read/write in batches of 4096 bytes +// State machine:Run-Test-> Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteReadEx(ULONG iIndex, // Specify device serial number + BOOL IsInDrOrIr, // =TRUE: in SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(perform data interaction).->Exit IR/DR -> Run-Test + BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write + ULONG iWriteBitLength, // Write length, The length to be written + PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out + PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +// JTAG IR/DR data batch read/write, for multi-byte sequential read/write. Such as JTAG download firmware. Because the hardware has a 4K buffer, if you write first and read later, the length will not exceed 4096 bytes. The buffer size can be adjusted by yourself +// State machine:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test +BOOL WINAPI CH347Jtag_WriteRead_FastEx(ULONG iIndex, // Specify the device number + BOOL IsInDrOrIr, // =TRUE: In SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(perform data interaction).->Exit IR/DR -> Run-Test + BOOL IsDR, // =TRUE: DR read/write, =FALSE:IR data read/write + ULONG iWriteBitLength, // Write length. The length to be written + PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out + PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +// Switch the JTAG state machine +BOOL WINAPI CH347Jtag_SwitchTapState(UCHAR TapState); + +// JTAG DR Write, in bytes, for multi-byte sequential read/write. such as JTAG firmware download operations. +// State machine: Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_ByteWriteDR(ULONG iIndex, // Specify the device serial number + ULONG iWriteLength, // Write length, length of bytes to be written + PVOID iWriteBuffer); // Points to a buffer, place the data ready to be written out + +// JTAG DR read, in bytes, for multi-byte sequential read. +// State machine: Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_ByteReadDR(ULONG iIndex, // Specify the device serial number + PULONG oReadLength, // Points to the length unit, returned length is the actual length read + PVOID oReadBuffer); // Points to a buffer large enough to place the read data + +// JTAG IR write, in bytes, for multi-byte sequential write. +// State machine: Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_ByteWriteIR(ULONG iIndex, // Specify device serial number + ULONG iWriteLength, // Write length, the length of bytes to be written + PVOID iWriteBuffer); // Points to a buffer, place the data ready to be written out + +// JTAG IR read, in bytes, for multi-byte sequential read/write. +// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_ByteReadIR(ULONG iIndex, // Specify device serial number + PULONG oReadLength, // Points to the length unit, returned data is the length of actual bytes read. + PVOID oReadBuffer); // Points to a buffer large enough to place the read data + +// Bitband mode JTAG DR data write. suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_ByteWriteDR +// The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_BitWriteDR(ULONG iIndex, // Specify device serial number + ULONG iWriteBitLength, // Points to the length unit, returned data is the length of actual bytes read. + PVOID iWriteBitBuffer); // Points to a buffer large enough to place the read data + +// Bit band mode JTAG IR data write. Suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_ByteWriteIR +// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_BitWriteIR(ULONG iIndex, // Specify device serial number + ULONG iWriteBitLength, // Points to the length unit, returned data is the length of actual bytes read. + PVOID iWriteBitBuffer); // Points to a buffer large enough to place the read data + +// Bit band mode JTAG IR data read. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching, etc. For batch data transfer, CH347Jtag_ByteReadIR is recommended. +// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test +BOOL WINAPI CH347Jtag_BitReadIR(ULONG iIndex, // Specify device serial number + PULONG oReadBitLength, // Points to the length unit, returned data is the length of actual bytes read. + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +// Bit band mode JTAG DR data read. Suitable for reading and writing small amounts of data. For batch and high-speed data transfer, CH347Jtag_ByteReadDR is recommended +// The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test +BOOL WINAPI CH347Jtag_BitReadDR(ULONG iIndex, // Specify device serial number + PULONG oReadBitLength, // Points to the length unit, returned data is the length of actual bytes read. + PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data + +/***************GPIO********************/ +// Get the GPIO direction and pin level of CH347 +BOOL WINAPI CH347GPIO_Get(ULONG iIndex, + UCHAR *iDir, // Pin direction: GPIO0-7 corresponding bit0-7, 0: input; 1: output + UCHAR *iData); // GPIO0 level: GPIO0-7 corresponding bit0-7, 0: low level; 1: high level + +// Set the GPIO direction and pin level of CH347 +BOOL WINAPI CH347GPIO_Set(ULONG iIndex, // Specify device serial number + UCHAR iEnable, // Data valid flag: corresponding bit0-7, correspond to GPIO0-7. + UCHAR iSetDirOut, // Sets I/O direction, A bit is 0 means corresponding pin is input, a bit is 1 means corresponding pin is output. GPIO0-7 corresponds to bit0-7. + UCHAR iSetDataOut); // Output data. If I/O direction is output, then a pin outputs low level when a bit cleared to 0, a pin outputs high level when a bit set to 1 + +typedef VOID(CALLBACK *mPCH347_INT_ROUTINE)( // Interrupt service routine + PUCHAR iStatus); // For interrupt status data, refer to the bit description below +// 8 byte GPIO0-7 pin status, the bits per byte are defined as follows. +// Bit7:Current GPIO0 direction, 0:Input; 1:Output +// Bit6:Current GPIO0 level, 0:Low level;1:High level +// Bit5:Whether the current GPIO0 is set to interrupt; 0:Query mode; 1:Interrupt mode +// Bit4-3:Set the GPIO0 interrupt mode, 00:Falling edge trigger; 01:Rising edge trigger; 10:Double edge trigger;11: reserved; +// Bit2-0:reserved; + +// Set GPIO interrupt service routine +BOOL WINAPI CH347SetIntRoutine(ULONG iIndex, // Specify the device serial number + UCHAR Int0PinN, // INT0 GPIO pin number greater than 7: disable this interrupt source; 0-7 corresponds to GPIO0-7 + UCHAR Int0TripMode, // INT0 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve; + UCHAR Int1PinN, // INT1 GPIO pin number. If it is greater than 7, disable this interrupt source; 0-7 corresponds to GPIO0-7 + UCHAR Int1TripMode, // INT1 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve; + mPCH347_INT_ROUTINE iIntRoutine); // Specify the interrupt service routine, if it is NULL, the interrupt service is canceled, otherwise, the program is called when interrupted. + +// Read interrupt data +BOOL WINAPI CH347ReadInter(ULONG iIndex, // Specify the device serial number + PUCHAR iStatus); // Point to the 8-byte unit, used to save read GPIO pin status data, refer to the following bit description + +// Abandon interrupt data read operation +BOOL WINAPI CH347AbortInter(ULONG iIndex); // Specify the device serial number + +// Enter IAP firmware upgrade mode +BOOL WINAPI CH347StartIapFwUpate(ULONG iIndex, + ULONG FwSize); // Firmware length + +/**************HID/VCP Serial Port*********************/ +// Open serial port +HANDLE WINAPI CH347Uart_Open(ULONG iIndex); + +// Close serial port +BOOL WINAPI CH347Uart_Close(ULONG iIndex); + +// Set device event notification program +BOOL WINAPI CH347Uart_SetDeviceNotify( + ULONG iIndex, // Specify the device serial number, 0 corresponds to the first device + PCHAR iDeviceID, // Optional parameter, points to a string, specify the monitored device ID, end with \0 + mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies device event callback program; if it is NULL then cancel event notification, otherwise the program is called when an event is detected. + +// Obtain UART hardware configuration +BOOL WINAPI CH347Uart_GetCfg(ULONG iIndex, // Specify the device serial number + PULONG BaudRate, // Baud rate + PUCHAR ByteSize, // Data bits (5,6,7,8,16) + PUCHAR Parity, // Parity bits(0:None; 1:Odd; 2:Even; 3:Mark; 4:Space) + PUCHAR StopBits, // Stop bits (0: 1 stop bits; 1: 1.5 stop bit; 2: 2 stop bit); + PUCHAR ByteTimeout); // Byte timeout + +// Set UART configuration +BOOL WINAPI CH347Uart_Init(ULONG iIndex, // Specify the device serial number + DWORD BaudRate, // Baud rate + UCHAR ByteSize, // Data bits (5,6,7,8,16) + UCHAR Parity, // Parity bits (0:None; 1:Odd; 2:Even; 3:Mark; 4:Space) + UCHAR StopBits, // Stop bits (0: 1 Stop bit; 1: 1.5 stop bit; 2: 2 stop bit); + UCHAR ByteTimeout); // Byte timeout, in unit of 100uS + +// Set USB data read/write timeout +BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // Specify the device serial number + ULONG iWriteTimeout, // Specify the timeout for USB to write data blocks, in mS,0xFFFFFFFF specifies no timeout (default) + ULONG iReadTimeout); // Specify the timeout for USB to read data blocks, in mS,0xFFFFFFFF specifies no timeout (default) + +// Read data block +BOOL WINAPI CH347Uart_Read(ULONG iIndex, // Specify the device serial number + PVOID oBuffer, // Points to a buffer large enough to place the read data + PULONG ioLength); // Refers to the length unit, the input is the length to be read, the return is the actual length to be read + +// Write data blocks +BOOL WINAPI CH347Uart_Write(ULONG iIndex, // Specify the device serial number + PVOID iBuffer, // Points to a buffer to place data ready to be written out + PULONG ioLength); // Point to the length unit, the input is the intended length, the return is the actual length + +// Query how many bytes are unfetched in read buffer +BOOL WINAPI CH347Uart_QueryBufUpload(ULONG iIndex, // Specify the device serial number + LONGLONG *RemainBytes); // Read buffer unfetched bytes + +// Obtain device information +BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation); + +// Set USB data read/write timeout +BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // Specify device serial number + ULONG iWriteTimeout, // Specify timeout for USB to write data block, in mS, 0xFFFFFFFF specifies no timeout (default) + ULONG iReadTimeout); // Specify timeout for USB to read data block, in mS, 0xFFFFFFFF specifies no timeout (default) +/********I2C***********/ +// I2C settings +BOOL WINAPI CH347I2C_Set(ULONG iIndex, // Specify device serial number + ULONG iMode); // Specify mode, see next line +// bit1 - bit 0: I2C interface speed/SCL frequency, 00=low speed/20KHz, 01=standard/100KHz(default), 10= fast/400KHz, 11= high speed/750KHz +// Others are reserved, must be 0 + +// Set I2C clock stretch +BOOL WINAPI CH347I2C_SetStretch(ULONG iIndex, // Specify device serial number + BOOL iEnable); // I2C Clock Stretch enable, 1:enable,0:disable + +// Set hardware asynchronous delay, returns shortly after being called, and then delays for specified milliseconds before the next stream operation. +BOOL WINAPI CH347I2C_SetDelaymS(ULONG iIndex, // Specify device serial number + ULONG iDelay); // Specify the delay in milliseconds + +// Process I2C data stream, 2-wire interface, clock wire is SCL pin, data wire is SDA pin +BOOL WINAPI CH347StreamI2C(ULONG iIndex, // Specify device serial number + ULONG iWriteLength, // Number of data bytes to be written + PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, first byte is usually the I2C device address and read/write direction bit + ULONG iReadLength, // Number of bytes of data to be read + PVOID oReadBuffer); // Points to a buffer, returned data is the read-in data + +// Process I2C data stream, 2-wire interface, clock wire is SCL pin, data wire is SDA pin (standard bidirectional I/O), speed about 56K bytes, and return the number of ACKs obtained by the host side +BOOL WINAPI CH347StreamI2C_RetACK(ULONG iIndex, // Specify device serial number + ULONG iWriteLength, // Number of data bytes to be written + PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, first byte is usually the I2C device address and read/write direction bit + ULONG iReadLength, // Number of bytes of data to be read + PVOID oReadBuffer, // Points to a buffer, returned data is the read-in data + PULONG rAckCount); // Points to the ACK value returned by read/write + +#ifndef _CH341_DLL_H +typedef enum _EEPROM_TYPE { // EEPROM type + ID_24C01, + ID_24C02, + ID_24C04, + ID_24C08, + ID_24C16, + ID_24C32, + ID_24C64, + ID_24C128, + ID_24C256, + ID_24C512, + ID_24C1024, + ID_24C2048, + ID_24C4096 +} EEPROM_TYPE; +#endif + +// Read data blocks from EEPROM, speed about 56KB +BOOL WINAPI CH347ReadEEPROM(ULONG iIndex, // Specify device serial number + EEPROM_TYPE iEepromID, // Specify EEPROM model + ULONG iAddr, // Specify data unit address + ULONG iLength, // Number of data bytes to be read + PUCHAR oBuffer); // Points to a buffer, returned data is the read-in data + +// Write data block to the EEPROM +BOOL WINAPI CH347WriteEEPROM(ULONG iIndex, // Specify device serial number + EEPROM_TYPE iEepromID, // Specify EEPROM model + ULONG iAddr, // Specify data unit address + ULONG iLength, // Number of data bytes to be written out + PUCHAR iBuffer); // Points to a buffer to place data ready to be written out + +#ifdef __cplusplus +} +#endif + +#endif // _CH347_DLL_H diff --git a/FlashGBX/bacon/lib/CH347DLL.DLL b/FlashGBX/bacon/lib/CH347DLL.DLL new file mode 100644 index 0000000..7cd5b1e Binary files /dev/null and b/FlashGBX/bacon/lib/CH347DLL.DLL differ diff --git a/FlashGBX/bacon/lib/CH347DLLA64.DLL b/FlashGBX/bacon/lib/CH347DLLA64.DLL new file mode 100644 index 0000000..3ba5295 Binary files /dev/null and b/FlashGBX/bacon/lib/CH347DLLA64.DLL differ diff --git a/FlashGBX/bacon/serial.py b/FlashGBX/bacon/serial.py new file mode 100644 index 0000000..9b7b893 --- /dev/null +++ b/FlashGBX/bacon/serial.py @@ -0,0 +1,438 @@ +# -*- coding: utf-8 -*- +# bacon +# Author: ChisBread (github.com/ChisBread) +import zlib +import time +from .bacon import BaconDevice +DEBUG = False +DEVICE_CMD = {} +CMD_TO_NAME = {} +DEVICE_VAR = {} +VARKEY_TO_NAME = {} + +FLASH_TYPES = { + "AMD": 0x01, + "INTEL": 0x02, + "SHARP": 0x02, + "OTHERS":0x00, +} + +FLASH_MODS = { + "FLASH_METHOD_AGB_FLASH2ADVANCE": 0x05, + "FLASH_METHOD_DMG_MMSA": 0x03, + "FLASH_METHOD_DMG_DATEL_ORBITV2": 0x09, + "FLASH_METHOD_DMG_E201264": 0x0A, + "FLASH_METHOD_AGB_GBAMP": 0x0B, + "FLASH_METHOD_DMG_BUNG_16M": 0x0C, + "FLASH_METHOD_BUFFERED": 0x02, + "FLASH_METHOD_PAGED": 0x08, + "FLASH_METHOD_UNBUFFERED": 0x01, +} + +def SetDebug(debug: bool): + global DEBUG + DEBUG = debug + +def SetDeviceCMD(device_cmd: dict, device_var: dict): + DEVICE_CMD = device_cmd + for key in device_cmd.keys(): + CMD_TO_NAME[device_cmd[key]] = key + for key in device_var.keys(): + VARKEY_TO_NAME[tuple(device_var[key])] = key + +def ParseCommand(cmd: bytes): + if len(cmd) == 0: + return None + return CMD_TO_NAME.get(cmd[0], "UNKNOWN") + +def dprint(*args, **kwargs): + if DEBUG: + print(*args, **kwargs) + +# | 地址范围 | 功能 | 大小 | 说明 | +# |------------|-----------|---------|------| +# | 08000000-09FFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态0) | +# | 0A000000-0BFFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态1) | +# | 0C000000-0DFFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态2) | +# | 0E000000-0E00FFFF | SRAM | 64KB | SRAM的地址空间(8位总线) | + +def MappingAddressToReal(addr): + if addr >= 0x08000000 and addr <= 0x09FFFFFF: + return addr - 0x08000000 + if addr >= 0x0A000000 and addr <= 0x0BFFFFFF: + return addr - 0x0A000000 + if addr >= 0x0C000000 and addr <= 0x0DFFFFFF: + return addr - 0x0C000000 + if addr >= 0x0E000000 and addr <= 0x0E00FFFF: + return addr - 0x0E000000 + return addr + +# 模拟串口设备,兼容GBX协议 +class BaconFakeSerialDevice: + def __init__(self): + self.timeout = 1 + + self.in_buff = b"" * 0x4000 + self.in_buff_size = 0x4000 + self.out_buff = b"\x00" * 0x4000 + self.out_buff_offset = 0 + self.out_buff_size = 0x4000 + + self.in_waiting = 0 + + self.bacon_dev = BaconDevice() + self.is_open = True + + self.FLASH_CMD_TYPE = 0x00 + self.FLASH_CMD_MOD = 0x00 + self.FLASH_CMD_WE = 0x00 + self.FLASH_CUSTOM_CMDS = [0x00]*6 + + self.BAK_FLASH_TYPE = 0x00 + + self.FW_VARS = {} + self.MODE = "AGB" # or DMG + self.POWER = 0 + self.AGB_SRAM_WRITING = False + self.AGB_BAK_FLASH_WRITING = False + self.CALC_CRC32_WAITING = False + self.FLASH_PROGRAMMING = False + self.SET_FLASH_CMD_WAITING = 0 + + # ROM cache 32MB + self.ROM_CACHE = [0x00]*0x2000000 + self.ROM_CACHED = [False]*0x2000000 + + def cache_rom(self, addr, data): + self.ROM_CACHE[addr:addr+len(data)] = data + self.ROM_CACHED[addr:addr+len(data)] = [True]*len(data) + def cache_rom_reset(self, addr=0, size=0x2000000): + self.ROM_CACHED[addr:addr+size] = [False]*size + def read_rom(self, addr, size): + if self.ROM_CACHED[addr:addr+size].count(False) > 0: + dprint("[BaconFakeSerialDevice] ReadROM:0x%08X Size:%s" % (addr, size)) + self.cache_rom(addr, self.bacon_dev.AGBReadROM(addr, size)) + else: + dprint("[BaconFakeSerialDevice] ReadROMCached:0x%08X Size:%s" % (addr, size)) + return bytes(self.ROM_CACHE[addr:addr+size]) + + def isOpen(self): + return self.bacon_dev is not None + + def close(self): + if self.bacon_dev is not None: + self.bacon_dev.Close() + self.bacon_dev = None + self.reset_input_buffer() + self.reset_output_buffer() + self.is_open = False + + def open(self): + self.bacon_dev = BaconDevice() + self.is_open = True + + def reset_input_buffer(self): + self.in_buff = b"" + self.in_waiting = 0 + + def reset_output_buffer(self): + self.out_buff_offset = 0 + + def push_to_input_buffer(self, data): + dprint("[BaconFakeSerialDevice] PushData:%s" % data.hex()) + self.in_buff = self.in_buff + data + self.in_waiting = len(data) + + def _push_ack(self): + self.push_to_input_buffer(b"\x01") + + def write(self, data): + self._cmd_parse(data) + return len(data) + + def _make_flash_cmds(self, addr, buffer_size): + flash_prepare = [] + flash_commit = [] + for j in range(6): + tcmd = (self.FLASH_CUSTOM_CMDS[j][0]<<1, self.FLASH_CUSTOM_CMDS[j][1]) + flash_prepare.append(tcmd) + + if flash_prepare[-1] == (0x00, 0x00): # write buffer size? + flash_prepare[-1] = (addr, buffer_size//2-1) + for k in range(j+1, 6): + # commit + if not (self.FLASH_CUSTOM_CMDS[k] == (0x00, 0x00)): + tcmd = (self.FLASH_CUSTOM_CMDS[k][0]<<1, self.FLASH_CUSTOM_CMDS[k][1]) + flash_commit.append(tcmd) + if flash_commit[-1][0] == 0x00: + flash_commit[-1] = (addr, flash_commit[-1][1]) + break + elif flash_prepare[-1][0] == 0x00: + flash_prepare[-1] = (addr, flash_prepare[-1][1]) + return flash_prepare, flash_commit + + + def _cmd_parse(self, cmd): + if self.AGB_SRAM_WRITING: + dprint("[BaconFakeSerialDevice] AGB_CART_WRITE_SRAM:0x%08X Value:%s" % (self.FW_VARS["ADDRESS"], cmd.hex())) + addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]) + self.bacon_dev.AGBWriteRAM(addr, cmd) + self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"] + self.AGB_SRAM_WRITING = False + self._push_ack() + return + if self.AGB_BAK_FLASH_WRITING: + dprint("[BaconFakeSerialDevice] AGB_CART_WRITE_FLASH_DATA:0x%08X" % (self.FW_VARS["ADDRESS"], )) + addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]) + # prepare unlock cmds and write + # TODO: other flash type + flash_cmds = [ + (0x5555, 0xAA), + (0x2AAA, 0x55), + (0x5555, 0xA0), + ] + flash_exit = (0, 0xF0) + if self.BAK_FLASH_TYPE != FLASH_TYPES["AMD"]: + raise Exception("Unsupported Backup Flash Type") + for i, data in enumerate(cmd): + self.bacon_dev.AGBWriteRAMWithAddress(commands = flash_cmds+[(addr+i, data), flash_exit], reset=False).Flush() + self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"] + self.AGB_BAK_FLASH_WRITING = False + self._push_ack() + return + if self.CALC_CRC32_WAITING: + chunk_size = int.from_bytes(cmd, byteorder='big') + addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1) + dprint("[BaconFakeSerialDevice] CALC_CRC32:0x%08X Size:0x%08X" % (self.FW_VARS["ADDRESS"], chunk_size)) + ret = self.read_rom(addr, chunk_size) + crc32 = zlib.crc32(ret) + # push crc32 4byte big-endian + self.push_to_input_buffer(crc32.to_bytes(4, byteorder='big')) + self.CALC_CRC32_WAITING = False + return + if self.FLASH_PROGRAMMING: + #TODO: More AGB Flash Type, And DMG Flash + raw_addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1) + addr = raw_addr + size = self.FW_VARS["TRANSFER_SIZE"] + buffer_size = self.FW_VARS["BUFFER_SIZE"] + dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING:0x%08X ValueSize:%s TransferSize:%s BufferSize:%s" % (self.FW_VARS["ADDRESS"], len(cmd), size, buffer_size)) + if self.FLASH_CMD_MOD == FLASH_MODS["FLASH_METHOD_BUFFERED"] and buffer_size > 0: + # per buffer Seq + start_time = time.time() + self.cache_rom_reset(raw_addr, size) + for i in range(0, size, buffer_size): + # make flash cmds + flash_prepare, flash_commit = self._make_flash_cmds(addr, buffer_size) + #dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Prepare:%s Commit:%s" % ([(hex(i[0]), hex(i[1])) for i in flash_prepare], [(hex(i[0]), hex(i[1])) for i in flash_commit])) + self.bacon_dev.AGBWriteROMWithAddress(commands=flash_prepare) + self.bacon_dev.AGBWriteROMSequential(addr=addr, data=cmd[i:i+buffer_size], reset=False) + self.bacon_dev.AGBWriteROMWithAddress(commands=flash_commit).Flush() + # S29GL256: + # 2-byte[33] 125 750 µs + # 32-byte[33] 160 750 + # 64-byte[33] 175 750 + # 128-byte[33] 198 750 + # 256-byte[33] 239 750 + # 512-byte 340 750 + # Don't need verify if time is enough + addr += buffer_size + # retry + ret = self.read_rom(raw_addr, size) + dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING PerBufferTime:%s(us)" % ((time.time()-start_time)/(size//buffer_size)*1000000)) + addr = raw_addr + for i in range(0, size, buffer_size): + j = 1 + while ret[i:i+buffer_size] != cmd[i:i+buffer_size]: + if j > 10: + dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Retry Failed!") + break + flash_prepare, flash_commit = self._make_flash_cmds(addr, buffer_size) + self.bacon_dev.AGBWriteROMWithAddress(commands=flash_prepare) + self.bacon_dev.AGBWriteROMSequential(addr=addr, data=cmd[i:i+buffer_size], reset=False) + self.bacon_dev.AGBWriteROMWithAddress(commands=flash_commit).Flush() + # 等待Nms + time.sleep(0.001*j) + dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Retry:%s" % j) + ret = ret[:i] + self.read_rom(addr, buffer_size) + ret[i+buffer_size:] + j += 1 + addr += buffer_size + + else: + #TODO write with cmd + pass + self._push_ack() + self.FLASH_PROGRAMMING = False + self.FW_VARS["ADDRESS"] += size // 2 + return + if self.SET_FLASH_CMD_WAITING > 0: + if self.SET_FLASH_CMD_WAITING == 9: + self.FLASH_CMD_TYPE = int.from_bytes(cmd, byteorder='big') + elif self.SET_FLASH_CMD_WAITING == 8: + self.FLASH_CMD_MOD = int.from_bytes(cmd, byteorder='big') + elif self.SET_FLASH_CMD_WAITING == 7: + self.FLASH_CMD_WE = int.from_bytes(cmd, byteorder='big') + elif self.SET_FLASH_CMD_WAITING <= 6: + # 0~3: addr + # 4~5: cmd + self.FLASH_CUSTOM_CMDS[6-self.SET_FLASH_CMD_WAITING] = (int.from_bytes(cmd[:4], byteorder='big'), int.from_bytes(cmd[4:], byteorder='big')) + dprint("[BaconFakeSerialDevice] SET_FLASH_CMD: Type:%s Mod:%s WE:%s Cmds:%s" % (self.FLASH_CMD_TYPE, self.FLASH_CMD_MOD, self.FLASH_CMD_WE, self.FLASH_CUSTOM_CMDS)) + self.SET_FLASH_CMD_WAITING = self.SET_FLASH_CMD_WAITING - 1 + if self.SET_FLASH_CMD_WAITING == 0: + self._push_ack() + return + + cmdname = ParseCommand(cmd) + + if cmdname == 'SET_VARIABLE': + # 0: cmd + # 1: size + # 2-5: key + # 6-: value + size = int(cmd[1])*8 + key = int.from_bytes(cmd[2:6], byteorder='big') + value = int.from_bytes(cmd[6:], byteorder='big') + # save + self.FW_VARS[VARKEY_TO_NAME.get((size, key), "UNKNOWN")] = value + self._push_ack() + dprint("[BaconFakeSerialDevice] SetVariable:", VARKEY_TO_NAME.get((size, key), "UNKNOWN"), value) + elif cmdname == "GET_VARIABLE": + # 0: cmd + # 1: size + # 2-5: key + size = int(cmd[1]) + key = int.from_bytes(cmd[2:6], byteorder='big') + # get + value = self.FW_VARS.get(VARKEY_TO_NAME.get((size, key), "UNKNOWN"), 0) + self.push_to_input_buffer(bytes([(value >> (i*8)) & 0xFF for i in range(4)])) + elif cmdname == "SET_MODE_AGB": + self.MODE = "AGB" + self._push_ack() + elif cmdname == "SET_MODE_DMG": + self.MODE = "DMG" + self._push_ack() + elif cmdname == "SET_VOLTAGE_3_3V": + self.POWER = 3 + self._push_ack() + elif cmdname == "SET_VOLTAGE_5V": + self.POWER = 5 + self._push_ack() + elif cmdname == "CART_PWR_ON": + if self.POWER == 3: + self.bacon_dev.PowerControl(v3_3v=True, v5v=False).Flush() + elif self.POWER == 5: + self.bacon_dev.PowerControl(v3_3v=False, v5v=True).Flush() + self._push_ack() + elif cmdname == "CART_PWR_OFF": + self.bacon_dev.PowerControl(v3_3v=False, v5v=False).Flush() + self._push_ack() + elif cmdname == "QUERY_CART_PWR": + self.push_to_input_buffer(bytes([self.bacon_dev.power])) + dprint("[BaconFakeSerialDevice] QueryCartPower:", self.bacon_dev.power) + elif cmdname == "AGB_CART_READ_SRAM": + addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]) + dprint("[BaconFakeSerialDevice] AGB_CART_READ_SRAM:0x%08X(0x%08X) Size:%d" % (self.FW_VARS["ADDRESS"], addr, self.FW_VARS["TRANSFER_SIZE"])) + ret = self.bacon_dev.AGBReadRAM(addr, self.FW_VARS["TRANSFER_SIZE"]) + self.push_to_input_buffer(ret) + self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"] + elif cmdname == "AGB_CART_WRITE_SRAM": + self.AGB_SRAM_WRITING = True + elif cmdname == "AGB_CART_WRITE_FLASH_DATA": + self.AGB_BAK_FLASH_WRITING = True + self.BAK_FLASH_TYPE = int(cmd[1]) + elif cmdname == "CALC_CRC32": # 读取一段数据,计算CRC32 + # 0: cmd + # 1~4: chunk_size + self.CALC_CRC32_WAITING = True + elif cmdname == "AGB_CART_READ": + addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1) + dprint("[BaconFakeSerialDevice] AGB_CART_READ:0x%08X(0x%08X) Size:%d" % (self.FW_VARS["ADDRESS"], addr, self.FW_VARS["TRANSFER_SIZE"])) + ret = self.bacon_dev.AGBReadROM(addr, self.FW_VARS["TRANSFER_SIZE"], reset=False) + if ret is not False: + self.push_to_input_buffer(ret) + if self.MODE == "AGB": + self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]//2 + else: + self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"] + elif cmdname == "AGB_FLASH_WRITE_SHORT": + # 0: cmd + # 1~4: addr + # 5~6: short + dprint("[BaconFakeSerialDevice] AGB_FLASH_WRITE_SHORT:0x%08X Value:%s" % (int.from_bytes(cmd[1:5], byteorder='big'), hex(int.from_bytes(cmd[5:7], byteorder='big')))) + addr = int.from_bytes(cmd[1:5], byteorder='big') + addr = MappingAddressToReal(addr<<1) + self.bacon_dev.AGBWriteROMWithAddress(commands=[(addr, int.from_bytes(cmd[5:7], byteorder='big'))]).Flush() + self._push_ack() + elif cmdname == "CART_WRITE_FLASH_CMD": + # 0: cmd + # 1: flashcart + # 2: num + # 6byte * num: cmds(4byte addr, 2byte cmd) + flashcart = int(cmd[1]) + num = int(cmd[2]) + cmds = [] + for i in range(num): + addr = int.from_bytes(cmd[3+i*6:7+i*6], byteorder='big') + if self.MODE == "AGB" and flashcart: + addr = MappingAddressToReal(addr<<1) + data = int.from_bytes(cmd[7+i*6:9+i*6], byteorder='big') + dprint("[BaconFakeSerialDevice] CART_WRITE_FLASH_CMD:0x%08X Value:%s" % (addr, hex(data))) + cmds.append((addr, data)) + if self.MODE == "AGB" and flashcart: + self.bacon_dev.AGBWriteROMWithAddress(commands=cmds).Flush() + else: + self.bacon_dev.AGBWriteRAMWithAddress(commands=cmds).Flush() + self._push_ack() + elif cmdname == "FLASH_PROGRAM": + self.FLASH_PROGRAMMING = True + # self._push_ack() 0x03? + elif cmdname == "SET_FLASH_CMD": + self.SET_FLASH_CMD_WAITING = 9 + elif cmdname == "AGB_CART_WRITE": + # 0: cmd + # 1~4: addr + # 5~6: short + addr = int.from_bytes(cmd[1:5], byteorder='big') + addr = MappingAddressToReal(addr<<1) + dprint("[BaconFakeSerialDevice] AGB_CART_WRITE:0x%08X Value:%s" % (addr, hex(int.from_bytes(cmd[5:7], byteorder='big')))) + self.bacon_dev.AGBWriteROMWithAddress(commands=[(addr, int.from_bytes(cmd[5:7], byteorder='big'))]).Flush() + self._push_ack() + elif cmdname == "AGB_READ_GPIO_RTC": + dprint("[BaconFakeSerialDevice] !!!! AGB_READ_GPIO_RTC is not implemented !!!!") + self.push_to_input_buffer(b"\x00"*8) + elif cmdname == "ENABLE_PULLUPS": + # TODO + dprint("[BaconFakeSerialDevice] !!!! DISABLE_PULLUPS is not implemented !!!!") + self._push_ack() + elif cmdname == "DISABLE_PULLUPS": + # TODO + dprint("[BaconFakeSerialDevice] !!!! DISABLE_PULLUPS is not implemented !!!!") + self._push_ack() + elif cmdname == "AGB_BOOTUP_SEQUENCE": + # TODO + dprint("[BaconFakeSerialDevice] !!!! AGB_BOOTUP_SEQUENCE is not implemented !!!!") + self._push_ack() + ###### DMG CMDS ###### + elif cmdname == "DMG_MBC_RESET": + self._push_ack() + elif cmd[0] == 0: + self._push_ack() + else: + dprint("[BaconFakeSerialDevice] UnsupportedCommand:%s Value:%s" % (cmdname, cmd.hex())) + + + def read(self, size: int): + dprint("[BaconFakeSerialDevice] ReadSize:%s, Left:%s" % (size, self.in_waiting)) + if size == 0: + return b"" + if size > self.in_waiting: + size = self.in_waiting + data = self.in_buff[:size] + self.in_buff = self.in_buff[size:] + self.in_waiting = len(self.in_buff) + dprint("[BaconFakeSerialDevice] ReadData:%s size:%s" % ([ hex(i) for i in data[:64] ], size)) + return data + + def flush(self): + # TODO: Parse the command + self.reset_output_buffer() diff --git a/FlashGBX/bacon/test.py b/FlashGBX/bacon/test.py new file mode 100644 index 0000000..519919c --- /dev/null +++ b/FlashGBX/bacon/test.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# bacon +# Author: ChisBread (github.com/ChisBread) + +import time +import os +import sys +# append path +from .bacon import BaconDevice +from .command import * + +bacon_dev = BaconDevice() + +def open_current_dif(filename, mode): + return open(os.path.join(os.path.dirname(__file__), filename), mode) + +def bacon_write_test(): + # 11: 5v + # 00: 3.3v + print("[SPI] write power control command") + powercmd = make_power_control_command(not_v3_3v=False, v5v=False) + print("[SPI] write bytes %s" % bytes2command(powercmd)) + ret = bacon_dev.Write(powercmd) + #True if successful, False otherwise. + print("[SPI] write result %s" % ret) + print("[SPI] write cart 30bit write command") + pintestcmd = make_cart_30bit_write_command( + phi=False, req=False, + wr=False, rd=True, + cs1=False, cs2=True, + v16bit=b"\xFF\xFF", v8bit=b"\xFF" + ) + print("[SPI] write bytes %s" % bytes2command(pintestcmd)) + bacon_dev.Write(pintestcmd) + print("[SPI] write test done") + +def bacon_read_test_30bit(): + power_read_cmd = make_power_read_command() + cart_read_cmd = make_cart_30bit_read_command() + # 测试3.3v + print("[SPI] write power control command") + bacon_dev.Write(make_power_control_command(not_v3_3v=False, v5v=False)) + ret = bacon_dev.WriteRead(power_read_cmd) + print("[SPI] read bytes %s, expected %s" % (bytes2command(ret)[::-1], "00")) + ###### 测试读取卡带数据 ###### + bacon_dev.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00" + )) + ## CS1上升沿,写入低位地址0x0000 + bacon_dev.Write(make_gba_rom_cs_write(cs=False)) + ret = bacon_dev.WriteRead(cart_read_cmd) + print(extract_cart_30bit_read_data(ret)) + readbytes = [] + avg_start = time.time() + start = time.time() + # 每0x10000,需要对v8bit+1 + # 所以0x10000需要能被BULK_SIZE整除 + high_addr = 0x00 + BULK_SIZE = 512 + TOTAL_SIZE = 0x800000 # 16MB + if TOTAL_SIZE % BULK_SIZE != 0: + raise ValueError("TOTAL_SIZE must be a multiple of BULK_SIZE") + if 0x10000 % BULK_SIZE != 0: + raise ValueError("BULK_SIZE must be a factor of 0x10000") + readcyclecmd = make_rom_read_cycle_command(times=BULK_SIZE) + for i in range(TOTAL_SIZE//BULK_SIZE): + ret = bacon_dev.WriteRead(readcyclecmd) + exteds = extract_read_cycle_data(ret, times=BULK_SIZE) + for idx, exted in enumerate(exteds): + num = i * BULK_SIZE + idx + readbytes.append(exted["v16bit"] >> 8) + readbytes.append(exted["v16bit"] & 0xFF) + if num < 2: + print(f"header bytes: {hex(readbytes[-2])[2:].rjust(2, '0')}, {hex(readbytes[-1])[2:].rjust(2, '0')}") + if num % 0x10000 == 0x10000 - 1: + high_addr += 1 + bacon_dev.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=False, cs2=True, + v16bit=b"\x00\x00", v8bit=bytes([high_addr]) + )) + if num % 0x1000 == 0x1000 - 1: + # print(f"[SPI] high address: 0x{hex(high_addr)[2:].rjust(2, '0')}") + # print(f"[SPI] read 0x2000 bytes in {time.time() - start}s speed(KB/s): {0x2000 / (time.time() - start) / 1024}") + print(f"\rAddress:0x{num+1:06X} Speed(KB/s):{(0x2000 / (time.time() - start) / 1024):.2f}", end="") + start = time.time() + print(f"\n[SPI] read {hex(TOTAL_SIZE*2)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_SIZE*2 / (time.time() - avg_start) / 1024}") + ch347.spi_change_cs(0x00) + with open_current_dif("gba_rom.bin", "wb") as f: + f.write(bytes(readbytes)) + +def bacon_read_test_16bit(): + power_read_cmd = make_power_read_command() + cart_read_cmd = make_cart_30bit_read_command() + # 测试3.3v + print("[SPI] write power control command") + bacon_dev.Write(make_power_control_command(not_v3_3v=False, v5v=False)) + ret = bacon_dev.WriteRead(power_read_cmd) + print("[SPI] read bytes %s, expected %s" % (bytes2command(ret)[::-1], "00")) + ###### 测试读取卡带数据 ###### + bacon_dev.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=True, cs2=True, + v16bit=b"\x00\x00", v8bit=b"\x00" + )) + ## CS1上升沿,写入低位地址0x0000 + bacon_dev.Write(make_gba_rom_cs_write(cs=False)) + ret = bacon_dev.WriteRead(cart_read_cmd) + print(extract_cart_30bit_read_data(ret)) + readbytes = [] + avg_start = time.time() + start = time.time() + # 每0x10000,需要对v8bit+1 + # 所以0x10000需要能被BULK_SIZE整除 + high_addr = 0x00 + MAX_BULK_SIZE = 0x1000//(len(make_rom_read_cycle_command(times=1))) # buffer不能超过0x1000,但尽量大一点 + bulk_list = [MAX_BULK_SIZE]*(0x10000//MAX_BULK_SIZE) + [0x10000%MAX_BULK_SIZE] + # print(f"[SPI] bulk list: {bulk_list}") + TOTAL_SIZE = 0x800000 # 16MB + for i in range(TOTAL_SIZE//0x10000): + # readcyclecmd = make_rom_read_cycle_command(times=MAX_BULK_SIZE) + # ret = bacon_dev.WriteRead(readcyclecmd) + # exteds = extract_read_cycle_data(ret, times=dynamic_bulk_size) + for j in range(len(bulk_list)): + readcyclecmd = make_rom_read_cycle_command(times=bulk_list[j]) + ret = bacon_dev.WriteRead(readcyclecmd) + exteds = extract_read_cycle_data(ret, times=bulk_list[j]) + for idx, exted in enumerate(exteds): + readbytes.append(exted >> 8) + readbytes.append(exted & 0xFF) + if num < 2: + print(f"header bytes: {hex(readbytes[-2])[2:].rjust(2, '0')}, {hex(readbytes[-1])[2:].rjust(2, '0')}") + if num % 0x10000 == 0x10000 - 1: + high_addr += 1 + bacon_dev.Write(make_cart_30bit_write_command( + phi=False, req=False, + wr=True, rd=True, + cs1=False, cs2=True, + v16bit=b"\x00\x00", v8bit=bytes([high_addr]) + )) + if num % 0x1000 == 0x1000 - 1: + # print(f"[SPI] high address: 0x{hex(high_addr)[2:].rjust(2, '0')}") + # print(f"[SPI] read 0x2000 bytes in {time.time() - start}s speed(KB/s): {0x2000 / (time.time() - start) / 1024}") + print(f"\rAddress:0x{num+1:06X} Speed(KB/s):{(0x2000 / (time.time() - start) / 1024):.2f}", end="") + start = time.time() + num += 1 + print(f"\n[SPI] read {hex(TOTAL_SIZE*2)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_SIZE*2 / (time.time() - avg_start) / 1024}") + with open_current_dif("gba_rom.bin", "wb") as f: + f.write(bytes(readbytes)) + +def bacon_test_api(): + bacon_dev.PowerControl(v3_3v=True, v5v=False).Flush() + bacon_dev.AGBWriteRAM(addr=0, data=open_current_dif("gba_bak_ram.bin", "rb").read()) + TOTAL_BYTE_SIZE = 0x800000*2 # 16MB + start = time.time() + avg_start = time.time() + first = True + last_addr = 0 + def callback(addr, data): + nonlocal first, start, avg_start, last_addr + if addr == 0 or (addr % 0x1000 != 0 and addr != TOTAL_BYTE_SIZE): + return + if first: + print(f"header bytes: {hex(data[0])[2:].rjust(2, '0')}, {hex(data[1])[2:].rjust(2, '0')}") + first = False + last_addr = 0 + if addr == TOTAL_BYTE_SIZE: + print(f"\n[SPI] read {hex(TOTAL_BYTE_SIZE)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_BYTE_SIZE / (time.time() - avg_start) / 1024}") + else: + delta = addr - last_addr + print(f"\rAddress:0x{addr:06X} Speed(KB/s):{(delta / (time.time() - start) / 1024):.2f}", end="") + start = time.time() + last_addr = addr + ## 读取卡带存档 + TOTAL_BYTE_SIZE = 0x8000 # 32KB + start = time.time() + avg_start = time.time() + first = True + ram = bacon_dev.AGBReadRAM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback) + with open_current_dif("gba_ram.bin", "wb") as f: + f.write(ram) + ## 写入卡带存档 + new_ram = bytes([0xFF]*TOTAL_BYTE_SIZE) + start = time.time() + avg_start = time.time() + first = True + ret = bacon_dev.AGBWriteRAM(addr=0, data=new_ram, callback=callback) + TOTAL_BYTE_SIZE = 0x8000 # 32KB + start = time.time() + avg_start = time.time() + first = True + new_ram_ret = bacon_dev.AGBReadRAM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback) + #### 判断是否写入成功 + if new_ram_ret == new_ram and new_ram_ret != ram: + print("[SPI] write and read ram success") + else: + print("[SPI] write and read ram failed head 10 bytes: %s, %s" % (new_ram[:10], new_ram_ret[:10])) + ## 恢复卡带存档 + bacon_dev.AGBWriteRAM(addr=0, data=ram) + ## 读取卡带ROM + TOTAL_BYTE_SIZE = 0x800000*2 # 16MB + start = time.time() + avg_start = time.time() + first = True + rom = bacon_dev.AGBReadROM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback) + with open_current_dif("gba_rom.bin", "wb") as f: + f.write(rom) +if __name__ == "__main__": + # print(ch347.get_version()) + # bacon_read_test_30bit() + # bacon_read_test_16bit() + bacon_test_api() + bacon_dev.Close() \ No newline at end of file diff --git a/FlashGBX/hw_Bacon.py b/FlashGBX/hw_Bacon.py new file mode 100644 index 0000000..ba4d85d --- /dev/null +++ b/FlashGBX/hw_Bacon.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +# FlashGBX +# Author: Lesserkuma (github.com/lesserkuma) + +# pylint: disable=wildcard-import, unused-wildcard-import +from .LK_Device import * + +from .bacon import BaconFakeSerialDevice, SetDebug, SetDeviceCMD + +class GbxDevice(LK_Device): + DEVICE_NAME = "Bacon" + DEVICE_MIN_FW = 1 + DEVICE_MAX_FW = 12 + DEVICE_LATEST_FW_TS = { 5:1730731680, 10:1730731680, 11:1730731680, 12:1730731680, 13:1730731680 } + PCB_VERSIONS = { 5:'', 12:'v1.2', 13:'v1.3' } + + def __init__(self): + SetDebug(Util.DEBUG) + SetDeviceCMD(LK_Device.DEVICE_CMD, LK_Device.DEVICE_VAR) + pass + + def Initialize(self, flashcarts, port=None, max_baud=2000000): + conn_msg = [] + try: + self.DEVICE = BaconFakeSerialDevice() + self.LoadFirmwareVersion() + except Exception as e: + dprint("Failed to initialize BaconFakeSerialDevice:", e) + return False + dprint(f"Found a {self.DEVICE_NAME}") + dprint("Firmware information:", "?") + + self.MAX_BUFFER_READ = 0x1000 + self.MAX_BUFFER_WRITE = 0x800 + + self.PORT = "Dummy" + self.DEVICE.timeout = 1 + + conn_msg.append([0, "Welcome to use " + self.DEVICE_NAME + "."]) + + # Load Flash Cartridge Handlers + self.UpdateFlashCarts(flashcarts) + return conn_msg + + def LoadFirmwareVersion(self): + dprint("Querying firmware version") + try: + self.DEVICE.timeout = 0.075 + self.DEVICE.reset_input_buffer() + self.DEVICE.reset_output_buffer() + self.DEVICE.timeout = 1 + self.FW = {} + self.FW["fw_ts"] = 1730731680 + self.FW["cfw_id"] = "L" + self.FW["fw_ver"] = 13 + self.FW["fw_dt"] = datetime.datetime.fromtimestamp(self.FW["fw_ts"]).astimezone().replace(microsecond=0).isoformat() + self.FW["ofw_ver"] = None + self.FW["pcb_ver"] = 13 + self.FW["pcb_name"] = "Bacon" + # Cartridge Power Control support + self.FW["cart_power_ctrl"] = True + # Reset to bootloader support + self.FW["bootloader_reset"] = False #True if temp & 1 == 1 else False + self.FW["unregistered"] = False #True if temp >> 7 == 1 else False + return True + except Exception as e: + traceback.print_exc() + dprint("Disconnecting due to an error", e, sep="\n") + try: + if self.DEVICE.isOpen(): + self.DEVICE.reset_input_buffer() + self.DEVICE.reset_output_buffer() + self.DEVICE.close() + self.DEVICE = None + except: + pass + return False + + def ChangeBaudRate(self, _): + dprint("Baudrate change is not supported.") + + def CheckActive(self): + if time.time() < self.LAST_CHECK_ACTIVE + 1: return True + dprint("Checking if device is active") + if self.DEVICE is None: + dprint("Device is None") + return False + if self.LoadFirmwareVersion(): + dprint("Device is active") + self.LAST_CHECK_ACTIVE = time.time() + return True + return False + + def GetFirmwareVersion(self, more=False): + s = "{:s}{:d}".format(self.FW["cfw_id"], self.FW["fw_ver"]) + if self.FW["pcb_name"] == None: + s += " " + if more: + s += " ({:s})".format(self.FW["fw_dt"]) + return s + + def GetFullNameExtended(self, more=False): + if more: + return "{:s} – Firmware {:s} ({:s}) on {:s}".format(self.GetFullName(), self.GetFirmwareVersion(), self.FW["fw_dt"], self.GetPort()) + else: + return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.GetPort()) + + def CanSetVoltageManually(self): + return False + + def CanSetVoltageAutomatically(self): + return True + + def CanPowerCycleCart(self): + return self.FW["cart_power_ctrl"] + + def GetSupprtedModes(self): + return ["DMG", "AGB"] + + def IsSupported3dMemory(self): + return True + + def IsClkConnected(self): + return True + + def SupportsFirmwareUpdates(self): + return True + + def FirmwareUpdateAvailable(self): + return False + + def GetFirmwareUpdaterClass(self): + return None + + def ResetLEDs(self): + pass + + def SupportsBootloaderReset(self): + return self.FW["bootloader_reset"] + + def BootloaderReset(self): + return False + + def SupportsAudioAsWe(self): + return not (self.FW["pcb_ver"] < 13 and self.CanPowerCycleCart()) + + def GetMode(self): + return super().GetMode() + + def SetAutoPowerOff(self, value): + value &= 0xFFFFFFFF + #if value == 0 or value > 5000: value = 1500 + return super().SetAutoPowerOff(value) + + def GetFullName(self): + if self.FW["pcb_ver"] < 13 and self.CanPowerCycleCart(): + s = "{:s} {:s} + PLUGIN 01".format(self.GetName(), self.GetPCBVersion()) + else: + s = "{:s} {:s}".format(self.GetName(), self.GetPCBVersion()) + if self.IsUnregistered(): + s += " (unregistered)" + return s + + def GetRegisterInformation(self): + text = "hahaha, no" + return text diff --git a/setup.py b/setup.py index 87c3d5b..de279c4 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ description="Reads and writes Game Boy and Game Boy Advance cartridge data", url="https://github.com/lesserkuma/FlashGBX", packages=setuptools.find_packages(), - install_requires=['pyserial>=3.5', 'Pillow', 'setuptools', 'requests', 'python-dateutil'], + install_requires=['pyserial>=3.5', 'Pillow', 'setuptools', 'requests', 'python-dateutil', 'bitarray', 'ch347api'], extras_require={ "qt5":["PySide2>=5.14"], "qt6":["PySide6"]