From 5aec94a036669000c11dee81a00f072a02365cbf Mon Sep 17 00:00:00 2001 From: Saf Date: Thu, 10 Mar 2022 23:47:47 -0800 Subject: [PATCH] Adding usb everdrive transfer --- everdrive_transfer/everdrive.rb | 175 ++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100755 everdrive_transfer/everdrive.rb diff --git a/everdrive_transfer/everdrive.rb b/everdrive_transfer/everdrive.rb new file mode 100755 index 0000000..99c8e51 --- /dev/null +++ b/everdrive_transfer/everdrive.rb @@ -0,0 +1,175 @@ +#!/usr/bin/env ruby + +require 'libusb' +require 'date' + +class EverdriveIO + class DeviceNotFound < StandardError; end + + def initialize(vendor, product) + @device = find_device(vendor, product) + @input, @output = find_bulk_endpoints(device) + @handle = device.open + handle.claim_interface(input.interface) + end + + def inspect + "#<#{self.class.name}: #{device.inspect}>" + end + + def close + handle.close + end + + def read(length) + handle.bulk_transfer(endpoint: input, dataIn: length) + end + + def write(data) + handle.bulk_transfer(endpoint: output, dataOut: data) + end + + def read_u8 + read(1).ord + end + + def read_u16 + read(2).unpack('S').first + end + + def read_u32 + read(4).unpack('L').first + end + + def write_u8(u8) + write( + (u8 & 0xff).chr + ) + end + + def write_u16(u16) + write( + [ + (u16 & 0x00ff), + (u16 & 0xff00) >> 8 + ].pack('cc') + ) + end + + def write_u32(u32) + write( + [ + (u32 & 0x000000ff), + (u32 & 0x0000ff00) >> 8, + (u32 & 0x00ff0000) >> 32, + (u32 & 0xff000000) >> 24, + ].pack('cccc') + ) + end + + def write_string(string) + write_u16(string.length) + write(string) + end + + private + + attr_reader :input, :output, :handle, :device + + def find_device(vendor, product) + LIBUSB::Context.new.devices(idVendor: vendor, idProduct: product).first.tap do |device| + raise(DeviceNotFound) if device.nil? + end + end + + def find_bulk_endpoints(device) + [ + device.endpoints.find { |ep| ep.transfer_type == :bulk && ep.direction == :in }, + device.endpoints.find { |ep| ep.transfer_type == :bulk && ep.direction == :out } + ] + end +end + + +class Everdrive + VENDOR = 0x0483 + PRODUCT = 0x5740 + STATUS_OK = 0xa500 + + FAT_WRITE = 0x02 + FAT_OPEN_ALWAYS = 0x10 + + CMD_F_FCLOSE = 0xCE; + CMD_F_FOPN = 0xC9 + CMD_F_FWR = 0xCC + CMD_RTC_GET = 0x14; + CMD_STATUS = 0x10 + + + def initialize + @everdrive = EverdriveIO.new(VENDOR, PRODUCT) + p status_ok? + end + + def inspect + "#<#{self.class.name}: #{@everdrive.inspect}>" + end + + def status_ok? + everdrive.write(make_command(CMD_STATUS)) + everdrive.read_u16 == STATUS_OK + end + + def get_realtime_clock + everdrive.write(make_command(CMD_RTC_GET)) + rtc = everdrive.read(6) + + ary = rtc.split('').map{|c| ('%x' % c.ord).to_i } + DateTime.new(ary[0] + 2000, ary[1], ary[2], ary[3], ary[4], ary[5]) + end + + def test_write_file + filename = '000-test.nes' + File.open(filename, 'rb') do |fp| + contents = fp.read + p file_open(filename, FAT_OPEN_ALWAYS | FAT_WRITE) + p file_write(contents, 0, contents.length) + p file_close + end + end + + private + + attr_reader :everdrive + + def file_open(filename, mode) + puts 'file_open' + everdrive.write(make_command(CMD_F_FOPN)) + everdrive.write_u8(mode) + everdrive.write_string('000-test.nes') + status_ok? + end + + def file_write(data, notsure, length) + puts 'file_write' + everdrive.write(make_command(CMD_F_FWR)) + everdrive.write_u32(data.length) + everdrive.write(data) + # other stuff txdataack instead, pay attention to blocksizes + status_ok? + end + + def file_close + puts 'file_close' + everdrive.write(make_command(CMD_F_FCLOSE)) + status_ok? + end + + def make_command(command_code) + data = ['+', '+'.ord ^ 0xff, command_code, command_code ^ 0xff].map { |b| (b.ord & 0xff).chr }.join + end +end + +everdrive = Everdrive.new +p everdrive.get_realtime_clock +# p everdrive.test_write_file