Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] port usb-sd-mux to macOS #84

Open
danieleftodi opened this issue Oct 30, 2024 · 1 comment
Open

[Feature Request] port usb-sd-mux to macOS #84

danieleftodi opened this issue Oct 30, 2024 · 1 comment

Comments

@danieleftodi
Copy link

danieleftodi commented Oct 30, 2024

Hi,

I recently bought a USB-SD-Mux FAST ... and I love it 😄

... though, I noticed it doesn't have macOS support.

I have done some reading through your code and and the MicroChip USB2642 documentation.

Especially the "USB82642/2642 I2C Over USB User’s Guide - Using USB Mass Storage Class Bulk-Only Transport & SCSI Pass Through"

From what I can gather, correct me if I'm wrong, but it just a matter of being able to send an I2C message over USB - to the MicroChip USB2642.

Ergo, all we would need to create, is a USB driver with DriverKit SDK for the usb-sd-mux.

And I do understand, that maybe you guy's don't have a Mac or interested to do this work. But since this an OSS project. Could you maybe assist with some technical information, when needed?

Cheers,
Daniel

Screenshot 2024-10-29 at 21 57 23
Screenshot 2024-10-29 at 21 56 13
I2C_Over_USB_UserGuilde_50002283A_Page_09
Screenshot 2024-10-29 at 21 16 54

Also found these sources:
Building a Simple USB Driver
USBDriverKit
Creating a Driver Using the DriverKit SDK
Sending SCSI or ATA commands to storage devices
Accessing SCSI Architecture Model Devices
WWDC 2020 - Modernize PCI and SCSI drivers with DriverKit
SCSIControllerDriverKit
SCSIPeripheralsDriverKit
VendorSpecificType00

@hnez
Copy link
Member

hnez commented Oct 30, 2024

Hi,

I recently bought a USB-SD-Mux FAST ... and I love it 😄

it's great to hear that, thanks!

... though, I noticed it doesn't have macOS support.

Also no Windows or BSD support. There are two reasons for that:

  • We at Linux Automation GmbH and Pengutronix (our parent company) tend to be Linux fans.
  • The SCSI Generic driver we use to communicate with the USB-SD-Mux is very Linux specific. Windows does seem to have a similar(-ish. At least as similar as Windows and Linux APIs get) API and various people have said they were interested in adding support for it to the USB-SD-Mux tool and we have also done some investigation ourself but we did not see anything get out of that yet.

While our own focus is very Linux-centric we would be very happy about contributions that add support for other platforms.

I have done some reading through your code and and the MicroChip USB2642 documentation.

Especially the "USB82642/2642 I2C Over USB User’s Guide - Using USB Mass Storage Class Bulk-Only Transport & SCSI Pass Through"

From what I can gather, correct me if I'm wrong, but it just a matter of being able to send an I2C message over USB - to the MicroChip USB2642.

I2C messages over SCSI over USB but yes, there is not much more to how the USB-SD-Mux works.

On the USB-SD-Mux FAST we have a TCA6408 i²c GPIO expander that is connected to the USB2642 via i²c and that controls the muxing and the external GPIOs.

To set the mux to HOST we set output pin 0 of the GPIO expander high.
To set the mux to DUT we set output pin 0 of the GPIO expander low.
To set the pin high/low we set/clear the corresponding bit in the GPIO expander output register via i²c:

def output_values(self, values: int, bitmask: int = 0xFF):
"""
Writes the given values to the GPIO-expander.
Pins configured as Inputs are not affected by this.
Arguments:
values -- Combination of I2cGpio.gpio_*
bitmask -- Only update bits in the register that are '1' in the bitmask
"""
if bitmask == 0xFF:
# trivial case: Let's just write the value
self._write_register(self._register_outputPort, values)
else:
# complex case: Let's do a read-modify-write
val = self._read_register(self._register_outputPort)
val = (val & ~bitmask) & 0xFF # reset masked bits
val = val | (values & bitmask) # set bits set in values and bitmask
self._write_register(self._register_outputPort, val)

The i²c transfer is encapsulated in a SCSI command …

def write_to(self, i2cAddr, data):
"""
Tries to write data to an I2C-Device.
This function will perform am I2C-Transaction like the following:
* I2C-Start
* I2C-Slave address with R/W = W (0)
* data[0]
* data[1]
* ...
* I2C-Stop
Transactions like this can (for example) be used if configuration registers
on a device have to be written.
Arguments:
i2cAddr -- 7-Bit I2C Slave address (as used by Linux). Will be shifted 1 Bit
to the left before adding the R/W-bit.
data -- iterateable of bytes to write."""
scsiCommand, data = self._get_SCSI_cmd_I2C_write(i2cAddr, data)
# TODO: Add length checks
# print("I2C-Command:")
# print(self.to_pretty_hex(scsiCommand))
# print("I2C-Payload:")
# print(self.to_pretty_hex(data))
data, sense, sgio = self._call_IOCTL(scsiCommand, self._SG_DXFER_TO_DEV, data)
if sgio.status != 0:
raise I2cTransactionFailed(
f"SCSI-Transaction ended with status {sgio.status}. I2C-Transaction has probably failed."
)

… the SCSI command is sent to the Linux Kernel via the SCSI generic device using an ioctl …

def _call_IOCTL(self, command, sg_dxfer, databuffer):
"""
Call the ioctl()
This function will create the struct to call the ioctl() and handle return
codes.
Arguments:
command -- SCSI Command payload to send. 16-Byte buffer containing the SCSI
command parameters.
sg_dxfer -- _SG_DXFER_*: Direction of the SCSI transfer
databuffer -- 512 byte long buffer to be written or read
"""
sgio, sense = self._get_SGIO(command, sg_dxfer, databuffer)
# print("SGIO:")
# print(self.to_pretty_hex(sgio))
with open(self.sg, "r+b", buffering=0) as fh:
rc = fcntl.ioctl(fh, self._SG_IO, sgio)
if rc != 0:
raise IoctlFailed(f"SG_IO ioctl() failed with non-zero exit-code {rc}")
return databuffer, sense, sgio

and the kernel wraps it into USB transfers.

Ergo, all we would need to create, is a USB driver with DriverKit SDK for the usb-sd-mux.

I would suggest trying to implement it at the SCSI first, instead of the USB level.
Talking raw USB means having to implement the SCSI layer on top and more potential for confusing the USB and SCSI state machines in the USB2642 and your OS Kernel.

And I do understand, that maybe you guy's don't have a Mac or interested to do this work. But since this an OSS project. Could you maybe assist with some technical information, when needed?

Sure! You can reach us here or in the #lxa channel on libera.chat.
We are usually not too secretive about the inner workings of our devices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants