Adding usb everdrive transfer

This commit is contained in:
Saf 2022-03-10 23:47:47 -08:00
parent 709a501071
commit 5aec94a036
1 changed files with 175 additions and 0 deletions

175
everdrive_transfer/everdrive.rb Executable file
View File

@ -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