pyamlboot icon indicating copy to clipboard operation
pyamlboot copied to clipboard

[FR] Implement `AML_LIBUSB_REQ_WRITE_MEDIA` and `AML_LIBUSB_REQ_READ_MEDIA` commands

Open fonix232 opened this issue 1 year ago • 2 comments

Many have been tinkering with the Spotify CarThing (codename superbird), an Amlogic S905D2 based device. @bishopdynamics has created a cross-platform tool wrapping pyamlboot to simplify a lot of the processes (dumping and restoring devices to specific firmware versions, enabling ADB and custom ROMs, etc.), however this tool, at the moment is somewhat limited in speed due to the indirect approach needed - instead of being able to read the attached storage (media) directly, it needs to instruct the device to read chunks of the partitions into RAM, then transfer those chunks to the host. This has a very limited read and write bandwidth - a few hundred KBps for read, and around 1.5MBps for writing. Simply said, dumping a device can take hours, which is really suboptimal.

However, the semi-official update utility, which is sadly not cross platform, has a much faster option for both writing and reading, the mread and mwrite methods.

Fortunately, there's an older version of the update utility where the source code is available: https://github.com/yangfl/update/

This source code defines both mread and mwrite quite well:

  • The opcodes for these operations: https://github.com/yangfl/update/blob/master/AmlLibusb/AmlLibusb.h#L46C3-L47C36
  • The main mread function: https://github.com/yangfl/update/blob/master/update.cpp#L797-L830
  • The main mwrite function: https://github.com/yangfl/update/blob/master/update.cpp#L199-L316
  • The actual MediaWrite and MediaRead commands
  • Their specific USB stack implementation

By the looks of it, the process is quite straightforward - I'll do mread as I'm more familiar with it, still need to dig through mwrite properly.

  1. After sanitising the input of mread, the parameters (store or read into memory, partition name, file type (always normal), and read size) are assigned
  2. A bulkcmd is sent with the params bulkcmd update [storeOrMemory] [partition] [fileType] [readSize in hex]
  3. The method ReadMediaFile is called, with the optional output file name (empty if reading into memory) and size
  4. ReadMediaFile prepares the output file, then begins calling AmlReadMedia with 0x10000L chunks read into buffer]
  5. AmlReadMedia further sanitises its input, then calls ReadMediaCMD to send the instruction to the device to read the chunk
  6. ReadMediaCMD is a bit convoluted, but it generally sends a USB control message and checks the output (https://github.com/yangfl/update/blob/master/AmlLibusb/AmlLibusb.cpp#L168-L191)
  7. Once ReadMediaCMD is finished, a bulk USB read happens into the provided buffer (https://github.com/yangfl/update/blob/master/AmlLibusb/AmlLibusb.cpp#L282-L293)
  8. That buffer is then written into the output file, or prints the block into console

mwrite is mostly the same, with some intermittent bulkcmd's checking the status and verifying the download (I'm guessing some light checksums of blocks).

Would it be possible to implement this in pyamlboot? I'd gladly do it myself, however neither my C++ nor my Python knowledge is on a level where I'd feel certain my efforts aren't pointless or downright dangerous.

fonix232 avatar Jan 27 '24 18:01 fonix232

Hi I thinks this is already done in #20 see https://github.com/superna9999/pyamlboot/pull/20/commits/d8d772d89527c669d24db16b91c5228dae9e74a4

superna9999 avatar Feb 05 '24 08:02 superna9999

@superna9999 I agree, it looks okay on first blink. However, given how much data is being transferred, to avoid hanging, I think it would be better for both ReadMedia and WriteMedia if they did a batched approach for continuous progress update - basically, replacing epin.read(size, timeout).tobytes() and epout.write(data, 1000) with smaller entries, similarly to how the update utility does:

  • for mread: https://github.com/yangfl/update/blob/master/update.cpp#L1271-L1288
  • for mwrite: https://github.com/yangfl/update/blob/master/update.cpp#L1221-L1245

With the main difference being that we wouldn't need to re-issue the control messages (self.dev.ctrl_transfer calls in the PR you linked), but only the bulk read/write part would need to repeat.

But otherwise this marginal nitpick, the code looks good, and seems to match precisely what's happening in the update tool!

fonix232 avatar Feb 05 '24 10:02 fonix232