Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 71 additions & 1 deletion src/machine/machine_nrf52840_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,23 @@ func initEndpoint(ep, config uint32) {
switch config {
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
enableEPIn(ep)
setEPDataPID(ep|usb.EndpointIn, false)

case usb.ENDPOINT_TYPE_BULK | usb.EndpointOut:
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
nrf.USBD.SIZE.EPOUT[ep].Set(0)
enableEPOut(ep)
setEPDataPID(ep, false)

case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
nrf.USBD.SIZE.EPOUT[ep].Set(0)
enableEPOut(ep)
setEPDataPID(ep, false)

case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
enableEPIn(ep)
setEPDataPID(ep|usb.EndpointIn, false)

case usb.ENDPOINT_TYPE_CONTROL:
enableEPIn(0)
Expand All @@ -259,7 +263,7 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0)

// clear transfer complete flag
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << ep)

return true
}
Expand Down Expand Up @@ -379,3 +383,69 @@ func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {

return b, nil
}

// Set the USB endpoint Packet ID to DATA0 or DATA1.
// In endpoints must have bit 7 (0x80) set.
func setEPDataPID(ep uint32, dataOne bool) {
val := ep
if dataOne {
val |= nrf.USBD_DTOGGLE_VALUE_Data1 << nrf.USBD_DTOGGLE_VALUE_Pos
} else {
val |= nrf.USBD_DTOGGLE_VALUE_Data0 << nrf.USBD_DTOGGLE_VALUE_Pos
}
nrf.USBD.DTOGGLE.Set(val)
}

// Set ENDPOINT_HALT/stall status on a USB IN endpoint.
func (dev *USBDevice) SetStallEPIn(ep uint32) {
if ep&0x7F == 0 {
nrf.USBD.TASKS_EP0STALL.Set(1)
} else if ep&0x7F < NumberOfUSBEndpoints {
// Stall In Endpoint
val := 0x100 | 0x80 | ep
nrf.USBD.EPSTALL.Set(val)
}
}

// Set ENDPOINT_HALT/stall status on a USB OUT endpoint.
func (dev *USBDevice) SetStallEPOut(ep uint32) {
if ep == 0 {
nrf.USBD.TASKS_EP0STALL.Set(1)
} else if ep < NumberOfUSBEndpoints {
// Stall Out Endpoint
val := 0x100 | 0x00 | ep
nrf.USBD.EPSTALL.Set(val)
}
}

// Clear the ENDPOINT_HALT/stall on a USB IN endpoint.
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
if ep&0x7F == 0 {
nrf.USBD.TASKS_EP0STALL.Set(0)
} else if ep&0x7F < NumberOfUSBEndpoints {
// Reset the endpoint data PID to DATA0
ep |= 0x80 // Set endpoint direction bit
setEPDataPID(ep, false)

// No-stall In Endpoint
val := 0x000 | 0x80 | ep
nrf.USBD.EPSTALL.Set(val)
}
}

// Clear the ENDPOINT_HALT/stall on a USB OUT endpoint.
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
if ep == 0 {
nrf.USBD.TASKS_EP0STALL.Set(0)
} else if ep < NumberOfUSBEndpoints {
// Reset the endpoint data PID to DATA0
setEPDataPID(ep, false)

// No-stall Out Endpoint
val := 0x000 | 0x00 | ep
nrf.USBD.EPSTALL.Set(val)

// Write a value to the SIZE register to allow nRF to ACK/accept data
nrf.USBD.SIZE.EPOUT[ep].Set(0)
}
}
50 changes: 48 additions & 2 deletions src/machine/usb/msc/msc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package msc

import (
"encoding/binary"
"machine"
"machine/usb"
"machine/usb/descriptor"
Expand Down Expand Up @@ -72,9 +73,7 @@ func newMSC(dev machine.BlockDevice) *msc {
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
m := &msc{
// Some platforms require reads/writes to be aligned to the full underlying hardware block
blockCache: make([]byte, dev.WriteBlockSize()),
blockSizeUSB: 512,
buf: make([]byte, dev.WriteBlockSize()),
cswBuf: make([]byte, csw.MsgLen),
cbw: &CBW{Data: make([]byte, 31)},
maxPacketSize: uint32(maxPacketSize),
Expand Down Expand Up @@ -165,6 +164,7 @@ func (m *msc) sendCSW(status csw.Status) {
}
m.cbw.CSW(status, residue, m.cswBuf)
m.state = mscStateStatusSent
m.queuedBytes = csw.MsgLen
m.sendUSBPacket(m.cswBuf)
}

Expand Down Expand Up @@ -297,3 +297,49 @@ func (m *msc) run(b []byte, isEpOut bool) bool {

return ack
}

// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
m.dev = dev

maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
bufSize := max(dev.WriteBlockSize(), int64(maxPacketSize))

if cap(m.blockCache) != int(bufSize) {
m.blockCache = make([]byte, bufSize)
m.buf = make([]byte, bufSize)
}

m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
// them we assume the provided block device is aligned to the end of the underlying hardware block
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB

// Set VPD UNMAP fields
for i := range vpdPages {
if vpdPages[i].PageCode == 0xb0 {
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
if len(vpdPages[i].Data) >= 28 {
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
}
if len(vpdPages[i].Data) >= 32 {
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
// where n is zero or any positive integer value
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf

// We assume the block device is aligned to the end of the underlying block device
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
blockOffset |= 0x80000000
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
}
break
}
}
}
45 changes: 0 additions & 45 deletions src/machine/usb/msc/disk.go → src/machine/usb/msc/recorder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package msc

import (
"encoding/binary"
"errors"
"fmt"
"machine"
Expand All @@ -12,50 +11,6 @@ var (
errWriteOutOfBounds = errors.New("WriteAt offset out of bounds")
)

// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
m.dev = dev

if cap(m.blockCache) != int(dev.WriteBlockSize()) {
m.blockCache = make([]byte, dev.WriteBlockSize())
m.buf = make([]byte, dev.WriteBlockSize())
}

m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
// them we assume the provided block device is aligned to the end of the underlying hardware block
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB
// FIXME: Figure out what to do if the emulated write block size is larger than the erase block size

// Set VPD UNMAP fields
for i := range vpdPages {
if vpdPages[i].PageCode == 0xb0 {
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
if len(vpdPages[i].Data) >= 28 {
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
}
if len(vpdPages[i].Data) >= 32 {
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
// where n is zero or any positive integer value
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf

// We assume the block device is aligned to the end of the underlying block device
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
blockOffset |= 0x80000000
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
}
break
}
}
}

var _ machine.BlockDevice = (*RecorderDisk)(nil)

// RecorderDisk is a block device that records actions taken on it
Expand Down
2 changes: 1 addition & 1 deletion src/machine/usb/msc/scsi_readwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (m *msc) writeBlock(b []byte, lba, offset uint32) (n int, err error) {
// Convert the emulated block address to the underlying hardware block's start and offset
blockStart, blockOffset := m.usbToRawOffset(lba, offset)

if blockOffset != 0 || len(b) != int(m.blockSizeRaw) {
if blockOffset != 0 || len(b)%int(m.blockSizeRaw) != 0 {
return 0, invalidWriteError
}

Expand Down
Loading