mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 21:13:39 +00:00
39e0333663
fix: validate bmRequestType\nfix: mimic tinyusb behavior in mscStateNeedReset\nfix: CLEAR_FEATURE requests to iface addr are against spec
179 lines
5.0 KiB
Go
179 lines
5.0 KiB
Go
package msc
|
|
|
|
import (
|
|
"machine"
|
|
"machine/usb"
|
|
)
|
|
|
|
func setupPacketHandler(setup usb.Setup) bool {
|
|
if MSC != nil {
|
|
return MSC.setupPacketHandler(setup)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (m *msc) setupPacketHandler(setup usb.Setup) bool {
|
|
ok := false
|
|
wValue := (uint16(setup.WValueH) << 8) | uint16(setup.WValueL)
|
|
switch setup.BRequest {
|
|
case usb.CLEAR_FEATURE:
|
|
if setup.BmRequestType == 0x02 { // Host-to-Device | Standard | Endpoint
|
|
ok = m.handleClearFeature(setup, wValue)
|
|
}
|
|
case usb.GET_MAX_LUN:
|
|
if setup.BmRequestType == 0xA1 { // Device-to-Host | Class | Interface
|
|
ok = m.handleGetMaxLun(setup, wValue)
|
|
}
|
|
case usb.MSC_RESET:
|
|
if setup.BmRequestType == 0x21 { // Host-to-Device | Class | Interface
|
|
ok = m.handleReset(setup, wValue)
|
|
}
|
|
}
|
|
return ok
|
|
}
|
|
|
|
// Handles the CLEAR_FEATURE request for clearing ENDPOINT_HALT/stall
|
|
func (m *msc) handleClearFeature(setup usb.Setup, wValue uint16) bool {
|
|
ok := false
|
|
// wValue is the feature selector (0x00 for ENDPOINT_HALT)
|
|
// We aren't handling any other feature selectors
|
|
// https://wiki.osdev.org/Universal_Serial_Bus#CLEAR_FEATURE
|
|
if wValue != 0 {
|
|
return ok
|
|
}
|
|
// Clearing the stall is not enough, continue stalling until a reset is received first
|
|
// 6.6.1 CBW Not Valid
|
|
// If the CBW is not valid, the device shall STALL the Bulk-In pipe. Also, the device
|
|
// shall either STALL the Bulk-Out pipe, or the device shall accept and discard any
|
|
// Bulk-Out data. The device shall maintain this state until a Reset Recovery
|
|
// For Reset Recovery the host shall issue in the following order: :
|
|
// (a) a Bulk-Only Mass Storage Reset (handleReset())
|
|
// (b) a Clear Feature HALT to the Bulk-In endpoint (clear stall IN)
|
|
// (c) a Clear Feature HALT to the Bulk-Out endpoint (clear stall OUT)
|
|
// https://usb.org/sites/default/files/usbmassbulk_10.pdf
|
|
if m.state == mscStateNeedReset {
|
|
wIndex := setup.WIndex & 0x7F // Clear the direction bit from the endpoint address for comparison
|
|
if wIndex == usb.MSC_ENDPOINT_IN {
|
|
m.stallEndpoint(usb.MSC_ENDPOINT_IN)
|
|
} else if wIndex == usb.MSC_ENDPOINT_OUT {
|
|
m.stallEndpoint(usb.MSC_ENDPOINT_OUT)
|
|
}
|
|
machine.SendZlp()
|
|
return true
|
|
}
|
|
|
|
// Clear the direction bit from the endpoint address for comparison
|
|
wIndex := setup.WIndex & 0x7F
|
|
|
|
// Clear the IN/OUT stalls if addressed to the endpoint
|
|
if wIndex == usb.MSC_ENDPOINT_IN {
|
|
m.clearStallEndpoint(usb.MSC_ENDPOINT_IN)
|
|
ok = true
|
|
}
|
|
if wIndex == usb.MSC_ENDPOINT_OUT {
|
|
m.clearStallEndpoint(usb.MSC_ENDPOINT_OUT)
|
|
ok = true
|
|
}
|
|
// Send a CSW if needed to resume after the IN endpoint stall is cleared
|
|
if m.state == mscStateStatus && wIndex == usb.MSC_ENDPOINT_IN {
|
|
m.sendCSW(m.respStatus)
|
|
ok = true
|
|
}
|
|
|
|
if ok {
|
|
machine.SendZlp()
|
|
}
|
|
return ok
|
|
}
|
|
|
|
// 3.2 Get Max LUN
|
|
// https://usb.org/sites/default/files/usbmassbulk_10.pdf
|
|
func (m *msc) handleGetMaxLun(setup usb.Setup, wValue uint16) bool {
|
|
if setup.WIndex != mscInterface || setup.WLength != 1 || wValue != 0 {
|
|
return false
|
|
}
|
|
// Send the maximum LUN ID number (zero-indexed, so n-1) supported by the device
|
|
m.resetBuffer(1) // Shrink buffer to 1 byte
|
|
m.buf[0] = m.maxLUN
|
|
return machine.SendUSBInPacket(usb.CONTROL_ENDPOINT, m.buf)
|
|
}
|
|
|
|
// 3.1 Bulk-Only Mass Storage Reset
|
|
// https://usb.org/sites/default/files/usbmassbulk_10.pdf
|
|
func (m *msc) handleReset(setup usb.Setup, wValue uint16) bool {
|
|
if setup.WIndex != mscInterface || setup.WLength != 0 || wValue != 0 {
|
|
return false
|
|
}
|
|
// Reset to command waiting state
|
|
m.state = mscStateCmd
|
|
|
|
// Reset transfer state
|
|
m.resetBuffer(0)
|
|
m.senseKey = 0
|
|
m.addlSenseCode = 0
|
|
m.addlSenseQualifier = 0
|
|
|
|
// Send a zero-length packet (ZLP) to indicate the reset is complete
|
|
machine.SendZlp()
|
|
|
|
// Return true to indicate successful reset
|
|
return true
|
|
}
|
|
|
|
func (m *msc) stallEndpoint(ep uint8) {
|
|
if ep == usb.MSC_ENDPOINT_IN {
|
|
m.txStalled = true
|
|
machine.USBDev.SetStallEPIn(usb.MSC_ENDPOINT_IN)
|
|
} else if ep == usb.MSC_ENDPOINT_OUT {
|
|
m.rxStalled = true
|
|
machine.USBDev.SetStallEPOut(usb.MSC_ENDPOINT_OUT)
|
|
} else if ep == usb.CONTROL_ENDPOINT {
|
|
machine.USBDev.SetStallEPIn(usb.CONTROL_ENDPOINT)
|
|
}
|
|
}
|
|
|
|
func (m *msc) clearStallEndpoint(ep uint8) {
|
|
if ep == usb.MSC_ENDPOINT_IN {
|
|
machine.USBDev.ClearStallEPIn(usb.MSC_ENDPOINT_IN)
|
|
m.txStalled = false
|
|
} else if ep == usb.MSC_ENDPOINT_OUT {
|
|
machine.USBDev.ClearStallEPOut(usb.MSC_ENDPOINT_OUT)
|
|
m.rxStalled = false
|
|
}
|
|
}
|
|
|
|
func (m *msc) setStringField(field []byte, value string) {
|
|
copy(field, []byte(value))
|
|
for i := len(value); i < len(field); i++ {
|
|
field[i] = 0x20 // Fill remaining bytes with spaces
|
|
}
|
|
}
|
|
|
|
func (m *msc) SetVendorID(vendorID string) {
|
|
m.setStringField(m.vendorID[:], vendorID)
|
|
}
|
|
|
|
func (m *msc) SetProductID(productID string) {
|
|
m.setStringField(m.productID[:], productID)
|
|
}
|
|
|
|
func (m *msc) SetProductRev(productRev string) {
|
|
m.setStringField(m.productRev[:], productRev)
|
|
}
|
|
|
|
func SetVendorID(vendorID string) {
|
|
if MSC != nil {
|
|
MSC.SetVendorID(vendorID)
|
|
}
|
|
}
|
|
func SetProductID(productID string) {
|
|
if MSC != nil {
|
|
MSC.SetProductID(productID)
|
|
}
|
|
}
|
|
func SetProductRev(productRev string) {
|
|
if MSC != nil {
|
|
MSC.SetProductRev(productRev)
|
|
}
|
|
}
|