diff --git a/src/device/nxp/mimxrt1062_mpu.go b/src/device/nxp/mimxrt1062_mpu.go index 9f3b5a20d..fee203f0a 100644 --- a/src/device/nxp/mimxrt1062_mpu.go +++ b/src/device/nxp/mimxrt1062_mpu.go @@ -252,3 +252,84 @@ func enableDcache(enable bool) { } } } + + +// FlushDcache flushes data from cache to memory +// +// Normally FlushDcache is used when metadata written to memory will be used by +// a DMA or a bus-controller peripheral. Any data in the cache is written to +// memory. A copy remains in the cache, so this is typically used with special +// fields you will want to quickly access in the future. For data transmission, +// use FlushDeleteDcache. +//go:inline +func FlushDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCCMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} + +// DeleteDcache deletes data from the cache, without touching memory. +// +// Normally DeleteDcache is used before receiving data via DMA or from +// bus-controller peripherals which write to memory. You want to delete anything +// the cache may have stored, so your next read is certain to access the +// physical memory. +//go:inline +func DeleteDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCIMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} + +// FlushDeleteDcache flushes data from cache to memory, and delete it from the +// cache +// +// Normally FlushDeleteDcache is used when transmitting data via DMA or +// bus-controller peripherals which read from memory. You want any cached data +// written to memory, and then removed from the cache, because you no longer +// need to access the data after transmission. +//go:inline +func FlushDeleteDcache(addr, size uintptr) { + location := addr & 0xFFFFFFE0 + endAddr := addr + size + arm.AsmFull(` + dsb 0xF + `, nil) + for { + SystemControl.DCCIMVAC.Set(uint32(location)) + location += 32 + if location >= endAddr { + break + } + } + arm.AsmFull(` + dsb 0xF + isb 0xF + `, nil) +} diff --git a/src/machine/usb2/dcd.go b/src/machine/usb2/dcd.go new file mode 100644 index 000000000..510a9e95d --- /dev/null +++ b/src/machine/usb2/dcd.go @@ -0,0 +1,81 @@ +package usb2 + +import "unsafe" + +type dcd interface { + init() status + enable(enable bool) status + critical(enter bool) status + interrupt() + receive(endpoint uint8, transfer *dcdTransfer) + transmit(endpoint uint8, transfer *dcdTransfer) + control(setup dcdSetup) +} + +const ( + dcdEndpointSize = 64 // bytes + dcdTransferSize = 32 // + dcdSetupSize = 8 // +) + +type dcdEndpoint struct { + config uint32 // 4 + current *dcdTransfer // 4 *dcdTransfer + transfer dcdTransfer // 32 + setup dcdSetup // 8 + // Endpoints are 48-byte data structures. The remaining data extends this to + // 64-byte, and also makes it simpler for implementations to align endpoints + // allocated contiguously on 64-byte boundaries. + first *dcdTransfer // 4 *dcdTransfer + last *dcdTransfer // 4 *dcdTransfer + // After some discussion, perhaps the simplest change to support 64-bit (or + // future TinyGo versions that don't use 8 bytes to refer to a function) is to + // allocate a separate buffer of callbacks. The device controller would assign + // callbacks to unused elements in that buffer, and only the index of that + // callback would be stored here in the descriptor. + callback dcdTransferCallback // 8 +} + +type dcdTransferCallback func(transfer *dcdTransfer) +type dcdTransfer struct { + next *dcdTransfer // 4 *dcdTransfer + token uint32 // 4 + pointer [5]uintptr // 20 + param uint32 // 4 +} + +type dcdSetup struct { + bmRequestType uint8 + bRequest uint8 + wValue uint16 + wIndex uint16 + wLength uint16 +} + +func (s dcdSetup) pack() uint64 { + return ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8 + ((uint64(s.bRequest) & 0xFF) << 8) | // uint8 + ((uint64(s.wValue) & 0xFFFF) << 16) | // uint16 + ((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16 + ((uint64(s.wLength) & 0xFFFF) << 48) // uint16 +} + +var ( + // dcdPointerNil is a sentinel value used to indicate a pointer to an invalid + // value. Do not attempt to dereference! + dcdPointerNil = uintptr(0) + // dcdTransferEOL is a sentinel value used to indicate the final node in a + // linked list of transfer descriptors. + dcdTransferEOL = (*dcdTransfer)(unsafe.Pointer(dcdTransferPointerEOL)) + // dcdTransferPointerEOL is the notional memory address of dcdTransferEOL. + // The address does not refer to an actual dcdTransfer, so no attempt should + // be made to dereference it. + dcdTransferPointerEOL = uintptr(1) +) + +// nextTransfer returns the next transfer descriptor pointed to by the receiver +// transfer descriptor, and whether or not that next descriptor is the final +// descriptor in the list. +func (t *dcdTransfer) nextTransfer() (*dcdTransfer, bool) { + return t.next, uintptr(unsafe.Pointer(t.next)) == dcdTransferPointerEOL +} diff --git a/src/machine/usb2/dcd_mimxrt1062.go b/src/machine/usb2/dcd_mimxrt1062.go new file mode 100644 index 000000000..372952a47 --- /dev/null +++ b/src/machine/usb2/dcd_mimxrt1062.go @@ -0,0 +1,1242 @@ +// +build mimxrt1062 + +package usb2 + +// Implementation of USB device controller interface (dcd) for NXP iMXRT1062. + +import ( + "device/arm" + "device/nxp" + "math/bits" + "runtime/interrupt" + "runtime/volatile" + "unsafe" +) + +// dcdCount defines the number of USB cores to configure for device mode. It is +// computed as the sum of all declared device configuration descriptors. +const dcdCount = descCDCACMCount + +// dcdInterruptPriority defines the priority for all USB device interrupts. +const dcdInterruptPriority = 3 + +// deviceController implements USB device controller driver (dcd) interface. +type deviceController struct { + core *core // Parent USB core this instance is attached to + port int // USB port index + class class // USB device class + id int // deviceControllerInstance index + + bus *nxp.USB_Type + phy *nxp.USBPHY_Type + irq interrupt.Interrupt + + cri volatile.Register8 // set to 1 if in critical section, else 0 + ivm uintptr // interrupt state when entering critical section + + stat *dcdEndpoint // endpoint 0 Rx ("out" direction) + ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction) + + acm *descCDCACMClass + + timerInterrupt [2]func() + controlNotify uint32 + endpointNotify uint32 + sofUsage uint8 + rebootTimer uint8 + setup dcdSetup + controlReply [8]uint8 + + speed uint8 // bus speed (0=full, 1=low, 2=high, 4=super) +} + +// deviceControllerInstance provides statically-allocated instances of each USB +// device controller configured on this platform. +var deviceControllerInstance [dcdCount]deviceController + +// cycleCount uses the ARM debug cycle counter available on iMXRT1062 (enabled +// in runtime_mimxrt1062_time.go) to return the number of CPU cycles since boot. +//go:inline +func cycleCount() uint32 { + return (*volatile.Register32)(unsafe.Pointer(uintptr(0xe0001004))).Get() +} + +//go:linkname ticks runtime.ticks +func ticks() int64 + +// initDCD initializes and assigns a free device controller instance to the +// given USB port. Returns the initialized device controller or nil if no free +// device controller instances remain. +func initDCD(port int, class class) (dcd, status) { + if 0 == dcdCount { + return nil, statusInvalid // must have defined device controllers + } + switch class.id { + case classDeviceCDCACM: + if 0 == class.config || class.config > descCDCACMCount { + return nil, statusInvalid // must have defined descriptors + } + default: + } + // Return the first instance whose assigned core is currently nil. + for i := range deviceControllerInstance { + if nil == deviceControllerInstance[i].core { + // Initialize device controller. + deviceControllerInstance[i].core = &coreInstance[port] + deviceControllerInstance[i].port = port + deviceControllerInstance[i].class = class + deviceControllerInstance[i].id = i + switch port { + case 0: + deviceControllerInstance[i].bus = nxp.USB1 + deviceControllerInstance[i].phy = nxp.USBPHY1 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG1, + func(interrupt.Interrupt) { + coreInstance[0].dc.interrupt() + }) + + case 1: + deviceControllerInstance[i].bus = nxp.USB2 + deviceControllerInstance[i].phy = nxp.USBPHY2 + deviceControllerInstance[i].irq = + interrupt.New(nxp.IRQ_USB_OTG2, + func(interrupt.Interrupt) { + //coreInstance[1].dc.interrupt() + }) + } + switch class.id { + case classDeviceCDCACM: + deviceControllerInstance[i].acm = &descCDCACM[class.config-1] + default: + } + return &deviceControllerInstance[i], statusOK + } + } + return nil, statusBusy // No free device controller instances available. +} + +func (dc *deviceController) init() status { + // reset the controller + dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) + for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { + } + // clear interrupts + m := arm.DisableInterrupts() + switch dc.port { + case 0: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) + case 1: + arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) + } + dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) + dc.phy.PWD.Set(0) + + // clear the controller mode field and set to device mode: + // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only + dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, + nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) + + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold + dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout + dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness + + dc.stat = dc.endpointQueueHead(rxEndpoint(0)) + dc.ctrl = dc.endpointQueueHead(txEndpoint(0)) + dc.stat.config = (descEndptMaxPktSize << 16) | (1 << 15) + dc.ctrl.config = (descEndptMaxPktSize << 16) + + dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer(dc.stat)))) + + // clear installed timer callbacks + dc.timerInterrupt[0] = nil + dc.timerInterrupt[1] = nil + + // enable interrupts + dc.bus.USBINTR.Set( + nxp.USB_USBINTR_UE_Msk | // bus enable + nxp.USB_USBINTR_UEE_Msk | // bus error + nxp.USB_USBINTR_PCE_Msk | // port change detect + nxp.USB_USBINTR_URE_Msk | // bus reset + nxp.USB_USBINTR_SLE) // sleep enable + + // ensure D+ pulled down long enough for host to detect previous disconnect + udelay(5000) + + return statusOK +} + +func (dc *deviceController) enable(enable bool) status { + if enable { + dc.irq.SetPriority(dcdInterruptPriority) + dc.irq.Enable() + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) + } else { + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) + dc.irq.Disable() + } + return statusOK +} + +func (dc *deviceController) critical(enter bool) status { + if enter { + // check if critical section already locked + if dc.cri.Get() != 0 { + return statusRetry + } + // lock critical section + dc.cri.Set(1) + // disable interrupts, storing state in receiver + dc.ivm = arm.DisableInterrupts() + } else { + // ensure critical section is locked + if dc.cri.Get() != 0 { + // re-enable interrupts, using state stored in receiver + arm.EnableInterrupts(dc.ivm) + // unlock critical section + dc.cri.Set(0) + } + } + return statusOK +} + +func (dc *deviceController) interrupt() { + // read and clear the interrupts that fired + status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() + dc.bus.USBSTS.Set(status) + + // USB Interrupt (USBINT) - R/WC + // This bit is set by the Host/Device Controller when the cause of an + // interrupt is a completion of a USB transaction where the Transfer + // Descriptor (TD) has an interrupt on complete (IOC) bit set. + // This bit is also set by the Host/Device Controller when a short packet is + // detected. A short packet is when the actual number of bytes received was + // less than the expected number of bytes. + if 0 != status&nxp.USB_USBSTS_UI { + + setupStatus := dc.bus.ENDPTSETUPSTAT.Get() + for 0 != setupStatus { + dc.bus.ENDPTSETUPSTAT.Set(setupStatus) + var setup dcdSetup + ready := false + for !ready { + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_SUTW) + setup = dc.stat.setup + ready = dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_SUTW) + } + dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_SUTW) + // flush endpoint 0 (bit 0=Rx, 16=Tx) + dc.bus.ENDPTFLUSH.Set(0x00010001) + for dc.bus.ENDPTFLUSH.HasBits(0x00010001) { + } // wait for flush to complete + dc.controlNotify = 0 + dc.control(setup) + setupStatus = dc.bus.ENDPTSETUPSTAT.Get() + } + + completeStatus := dc.bus.ENDPTCOMPLETE.Get() + if 0 != completeStatus { + dc.bus.ENDPTCOMPLETE.Set(completeStatus) + if 0 != completeStatus&dc.controlNotify { + dc.controlNotify = 0 + dc.controlComplete() + } + completeStatus &= dc.endpointNotify + if 0 != completeStatus { + tx := completeStatus >> 16 + for 0 != tx { + num := uint8(bits.TrailingZeros32(tx)) + dc.endpointComplete(txEndpoint(num)) + tx &^= 1 << num + } + rx := completeStatus & 0xFFFF + for 0 != rx { + num := uint8(bits.TrailingZeros32(rx)) + dc.endpointComplete(rxEndpoint(num)) + rx &^= 1 << num + } + } + } + } + + // USB Reset Received - R/WC + // When the device controller detects a USB Reset and enters the default + // state, this bit will be set to a one. + // Software can write a 1 to this bit to clear the USB Reset Received status + // bit. + // Only used in device operation mode. + if 0 != status&nxp.USB_USBSTS_URI { + // clear all setup tokens + dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get()) + // clear all endpoint complete status + dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get()) + // wait on any endpoint priming + for 0 != dc.bus.ENDPTPRIME.Get() { + } + dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF) + // if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { + // } + switch dc.class.id { + case classDeviceCDCACM: + // TBD: reset CDC-ACM UART? + default: + } + dc.endpointNotify = 0 + } + + // General Purpose Timer Interrupt 0(GPTINT0) - R/WC + // This bit is set when the counter in the GPTIMER0CTRL register transitions + // to zero, writing a one to this bit clears it. + if 0 != status&nxp.USB_USBSTS_TI0 { + if nil != dc.timerInterrupt[0] { + dc.timerInterrupt[0]() + } + } + + // General Purpose Timer Interrupt 1(GPTINT1) - R/WC + // This bit is set when the counter in the GPTIMER1CTRL register transitions + // to zero, writing a one to this bit will clear it. + if 0 != status&nxp.USB_USBSTS_TI1 { + if nil != dc.timerInterrupt[1] { + dc.timerInterrupt[1]() + } + } + + // Port Change Detect - R/WC + // The Host Controller sets this bit to a one when on any port a Connect + // Status occurs, a Port Enable/Disable Change occurs, or the Force Port + // Resume bit is set as the result of a J-K transition on the suspended port. + // The Device Controller sets this bit to a one when the port controller + // enters the full or high-speed operational state. When the port controller + // exits the full or high-speed operation states due to Reset or Suspend + // events, the notification mechanisms are the USB Reset Received bit and the + // DCSuspend bits respectively. + if 0 != status&nxp.USB_USBSTS_PCI { + if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) { + dc.speed = descDeviceSpeedHigh // 480 Mbit/sec + } else { + dc.speed = descDeviceSpeedFull // 12 Mbit/sec + } + } + + // DCSuspend - R/WC + // When a controller enters a suspend state from an active state, this bit + // will be set to a one. The device controller clears the bit upon exiting + // from a suspend state. Only used in device operation mode. + if 0 != status&nxp.USB_USBSTS_SLI { + //println("suspend") + } + + // USB Error Interrupt (USBERRINT) - R/WC + // When completion of a USB transaction results in an error condition, this + // bit is set by the Host/Device Controller. This bit is set along with the + // USBINT bit, if the TD on which the error interrupt occurred also had its + // interrupt on complete (IOC) bit set. + // The device controller detects resume signaling only. + if 0 != status&nxp.USB_USBSTS_UEI { + //println("error") + } + + // SOF Received - R/WC + // When the device controller detects a Start Of (micro) Frame, this bit will + // be set to a one. When a SOF is extremely late, the device controller will + // automatically set this bit to indicate that an SOF was expected. + // Therefore, this bit will be set roughly every 1ms in device FS mode and + // every 125ms in HS mode and will be synchronized to the actual SOF that is + // received. + // Because the device controller is initialized to FS before connect, this bit + // will be set at an interval of 1ms during the prelude to connect and chirp. + // In host mode, this bit will be set every 125us and can be used by host + // controller driver as a time base. Software writes a 1 to this bit to clear + // it. + if dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) && + 0 != status&nxp.USB_USBSTS_SRI { + if 0 != dc.rebootTimer { + dc.rebootTimer -= 1 + if 0 == dc.rebootTimer { + dc.enableSofInterrupts(false, descCDCACMInterfaceCount) + } + } + } +} + +func (dc *deviceController) enableSofInterrupts(enable bool, iface uint8) { + if enable { + ivm := arm.DisableInterrupts() + dc.sofUsage |= 1 << iface + if !dc.bus.USBINTR.HasBits(nxp.USB_USBINTR_SRE) { + dc.bus.USBSTS.Set(nxp.USB_USBSTS_SRI) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_SRE) + } + arm.EnableInterrupts(ivm) + } else { + dc.sofUsage &^= 1 << iface + if 0 == dc.sofUsage { + dc.bus.USBINTR.ClearBits(nxp.USB_USBINTR_SRE) + } + } +} + +// receive schedules a receive (Rx, OUT) transfer on the given endpoint. +func (dc *deviceController) receive(endpoint uint8, transfer *dcdTransfer) { + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(rxEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos + dc.transferSchedule(ep, em, transfer) +} + +// transmit schedules a transmit (Tx, IN) transfer on the given endpoint. +func (dc *deviceController) transmit(endpoint uint8, transfer *dcdTransfer) { + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(txEndpoint(endpoint)) + em := (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos + dc.transferSchedule(ep, em, transfer) +} + +// control handles setup messages on control endpoint 0. +func (dc *deviceController) control(setup dcdSetup) { + + // println(strconv.FormatUint(setup.pack(), 16)) + + // First, switch on the type of request (standard, class, or vendor) + switch setup.bmRequestType & descRequestTypeTypeMsk { + + // === STANDARD REQUEST === + case descRequestTypeTypeStandard: + + // Switch on the recepient and direction of the request + switch setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- DEVICE Rx (OUT) --- + case descRequestTypeRecipientDevice | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // SET ADDRESS (0x05): + case descRequestStandardSetAddress: + dc.controlReceive(dcdPointerNil, 0, false) + dc.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | + ((uint32(setup.wValue) << nxp.USB_DEVICEADDR_USBADR_Pos) & + nxp.USB_DEVICEADDR_USBADR_Msk)) + return + + // SET CONFIGURATION (0x09): + case descRequestStandardSetConfiguration: + dc.class.config = int(setup.wValue) + if 0 == dc.class.config || dc.class.config > dcdCount { + // Use default if invalid index received + dc.class.config = 1 + } + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx + dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx + dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx + dc.serialConfigure() + dc.controlReceive(dcdPointerNil, 0, false) + + default: + // Unhandled device class + } + return + + default: + // Unhandled request + } + + // --- DEVICE Tx (IN) --- + case descRequestTypeRecipientDevice | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + dc.controlReply[0] = 0 + dc.controlReply[1] = 0 + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) + return + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + dc.controlDescriptor(setup) + return + + // GET CONFIGURATION (0x08): + case descRequestStandardGetConfiguration: + dc.controlReply[0] = uint8(dc.class.config) + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false) + return + + default: + // Unhandled request + } + + // --- INTERFACE Tx (IN) --- + case descRequestTypeRecipientInterface | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET DESCRIPTOR (0x06): + case descRequestStandardGetDescriptor: + dc.controlDescriptor(setup) + return + + default: + // Unhandled request + } + + // --- ENDPOINT Rx (OUT) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // CLEAR FEATURE (0x01): + case descRequestStandardClearFeature: + // TODO + + // SET FEATURE (0x03): + case descRequestStandardSetFeature: + // TODO + + default: + // Unhandled request + } + + // --- ENDPOINT Tx (IN) --- + case descRequestTypeRecipientEndpoint | descRequestTypeDirIn: + + // Identify which request was received + switch setup.bRequest { + + // GET STATUS (0x00): + case descRequestStandardGetStatus: + num, dir := unpackEndpoint(uint8(setup.wIndex)) + var reg *volatile.Register32 + switch num { + case 0: + reg = &dc.bus.ENDPTCTRL0 + case 1: + reg = &dc.bus.ENDPTCTRL1 + case 2: + reg = &dc.bus.ENDPTCTRL2 + case 3: + reg = &dc.bus.ENDPTCTRL3 + case 4: + reg = &dc.bus.ENDPTCTRL4 + case 5: + reg = &dc.bus.ENDPTCTRL5 + case 6: + reg = &dc.bus.ENDPTCTRL6 + case 7: + reg = &dc.bus.ENDPTCTRL7 + } + if nil != reg { + dc.controlReply[0] = 0 + dc.controlReply[1] = 0 + if ((0 != dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_TXS)) || + ((0 == dir) && reg.HasBits(nxp.USB_ENDPTCTRL0_RXS)) { + dc.controlReply[0] = 1 + } + dc.controlTransmit( + uintptr(unsafe.Pointer(&dc.controlReply[0])), 2, false) + return + } + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + // line coding must contain exactly 7 bytes + if descCDCACMCodingSize == setup.wLength { + dc.setup = setup + dc.controlReceive( + uintptr(unsafe.Pointer(&descCDCACM[dc.class.config-1].cx[0])), + descCDCACMCodingSize, true) + return + } + + default: + // Unhandled device class + } + + // CDC | SET CONTROL LINE STATE (0x22): + case descCDCRequestSetControlLineState: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + + // Determine interface destination of the notification + switch setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + acm := &descCDCACM[dc.class.config-1] + acm.cticks = ticks() + acm.rtsdtr = uint8(setup.wValue) + dc.controlReceive(dcdPointerNil, 0, false) + return + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + // CDC | SEND BREAK (0x23): + case descCDCRequestSendBreak: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + dc.controlReceive(dcdPointerNil, 0, false) + return + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled request recepient or direction + } + + case descRequestTypeTypeVendor: + default: + // Unhandled request type + } + + dc.bus.ENDPTCTRL0.Set(0x00010001) +} + +// controlTransfers returns the data and ackowledgement transfer descriptors for +// the control endpoint (i.e., endpoint 0). +//go:inline +func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { + // control endpoint is device class-specific + switch dc.class.id { + case classDeviceCDCACM: + return descCDCACM[dc.class.config-1].cd, descCDCACM[dc.class.config-1].ad + default: + return nil, nil + } +} + +func (dc *deviceController) controlDescriptor(setup dcdSetup) { + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + acm := &descCDCACM[dc.class.config-1] + dxn := uint8(0) + + // Determine the type of descriptor being requested + switch setup.wValue >> 8 { + + // Device descriptor + case descTypeDevice: + dxn = descLengthDevice + _ = copy(acm.dx[:], acm.device[:dxn]) + + // Configuration descriptor + case descTypeConfigure: + dxn = uint8(descCDCACMConfigSize) + _ = copy(acm.dx[:], acm.config[:dxn]) + + // String descriptor + case descTypeString: + + var s []uint8 + + // Determine the string index requested + switch uint8(setup.wValue) { + + // Language + case 0: + if int(setup.wIndex) < len(acm.locstr) { + s = acm.locstr[setup.wIndex].index[0][:] + } + + // Manufacturer + case 1: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[1][:] + // copy manufacturer string to uint8 buffer as UTF-16 + for n, c := range descManufacturer { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + + // Product + case 2: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[2][:] + // copy product string to uint8 buffer as UTF-16 + for n, c := range descProduct { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + + // Serial number + case 3: + for i := range acm.locstr { + if acm.locstr[i].language == setup.wIndex { + s = acm.locstr[i].index[3][:] + // copy serial number string to uint8 buffer as UTF-16 + for n, c := range descSerialNumber { + s[2+2*n] = uint8(c) + s[3+2*n] = 0 + } + break + } + } + } + + if nil != s && len(s) > 0 { + dxn = s[0] + _ = copy(acm.dx[:], s[:dxn]) + } + + // Device qualification descriptor + case descTypeQualification: + dxn = descLengthQualification + _ = copy(acm.dx[:], acm.qualif[:dxn]) + + // Alternate configuration descriptor + case descTypeOtherSpeedConfiguration: + + } + + if dxn > 0 { + if dxn > uint8(setup.wLength) { + dxn = uint8(setup.wLength) + } + nxp.FlushDeleteDcache( + uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn)) + dc.controlTransmit( + uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false) + } + + default: + // Unhandled device class + } + +} + +// controlReceive receives (Rx, OUT) data on control endpoint 0. +func (dc *deviceController) controlReceive( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descCDCACMConfigAttrRxPos) + tm = uint32(1 << descCDCACMConfigAttrTxPos) + ) + cd, ad := dc.controlTransfers() + if size > 0 { + cd.next = dcdTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qr := dc.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = cd + qr.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(rm) + for 0 != dc.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dcdTransferEOL + ad.token = 1 << 7 + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qt := dc.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = ad + qt.transfer.token = 0 + dc.bus.ENDPTCOMPLETE.Set(rm | tm) + dc.bus.ENDPTPRIME.SetBits(tm) + if notify { + dc.controlNotify = tm + } +} + +// controlTransmit transmits (Tx, IN) data on control endpoint 0. +func (dc *deviceController) controlTransmit( + data uintptr, size uint32, notify bool) { + const ( + rm = uint32(1 << descCDCACMConfigAttrRxPos) + tm = uint32(1 << descCDCACMConfigAttrTxPos) + ) + cd, ad := dc.controlTransfers() + if size > 0 { + cd.next = dcdTransferEOL + cd.token = (size << 16) | (1 << 7) + for i := range cd.pointer { + cd.pointer[i] = data + uintptr(i)*4096 + } + // linked list is empty + qt := dc.endpointQueueHead(txEndpoint(0)) + qt.transfer.next = cd + qt.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(tm) + for 0 != dc.bus.ENDPTPRIME.Get() { + } // wait for endpoint finish priming + } + ad.next = dcdTransferEOL + ad.token = 1 << 7 + if notify { + ad.token |= 1 << 15 + } + ad.pointer[0] = 0 + qr := dc.endpointQueueHead(rxEndpoint(0)) + qr.transfer.next = ad + qr.transfer.token = 0 + dc.bus.ENDPTCOMPLETE.Set(rm | tm) + dc.bus.ENDPTPRIME.SetBits(rm) + if notify { + dc.controlNotify = rm + } +} + +// controlComplete handles the setup completion of control endpoint 0. +func (dc *deviceController) controlComplete() { + + // First, switch on the type of request (standard, class, or vendor) + switch dc.setup.bmRequestType & descRequestTypeTypeMsk { + + // === CLASS REQUEST === + case descRequestTypeTypeClass: + + // Switch on the recepient and direction of the request + switch dc.setup.bmRequestType & + (descRequestTypeRecipientMsk | descRequestTypeDirMsk) { + + // --- INTERFACE Rx (OUT) --- + case descRequestTypeRecipientInterface | descRequestTypeDirOut: + + // Identify which request was received + switch dc.setup.bRequest { + + // CDC | SET LINE CODING (0x20): + case descCDCRequestSetLineCoding: + + // Respond based on our device class configuration + switch dc.class.id { + + // CDC-ACM (single) + case classDeviceCDCACM: + + // Determine interface destination of the notification + switch dc.setup.wIndex { + + // Control/status interface: + case descCDCACMInterfaceCtrl: + + _ = copy(descCDCACM[dc.class.config-1].coding[:], + // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) + descCDCACM[dc.class.config-1].cx[:]) + var coding descCDCACMLineCoding + if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { + if 134 == coding.baud { + dc.enableSofInterrupts(true, descCDCACMInterfaceCount) + dc.rebootTimer = 80 + } + } + + default: + // Unhandled device interface + } + + default: + // Unhandled device class + } + + default: + // Unhandled request + } + + default: + // Unhandled recepient or direction + } + + default: + // Unhandled request type + } + + // // determine interface destination of the notification + // switch dc.setup.wIndex { + // // communication/control interface: + // case descCDCACMInterfaceCtrl: + // // switch on the type and recepient of the request + // switch dc.setup.bmRequestType & + // (descRequestTypeTypeMsk | descRequestTypeRecipientMsk) { + // // interface class request: + // case descRequestTypeRecipientInterface | descRequestTypeTypeClass: + // // identify which request was received + // switch dc.setup.bRequest { + // // CDC_SET_LINE_CODING: + // case descCDCRequestSetLineCoding: + // // respond according to our device class + // switch dc.class.id { + // // CDC-ACM (single) + // case classDeviceCDCACM: + // _ = copy(descCDCACM[dc.class.config-1].coding[:], + // // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize]) + // descCDCACM[dc.class.config-1].cx[:]) + // var coding descCDCACMLineCoding + // if coding.parse(descCDCACM[dc.class.config-1].coding[:]) { + // if 134 == coding.baud { + // dc.enableSofInterrupts(true, descCDCACMInterfaceCount) + // dc.rebootTimer = 80 + // } + // } + // default: + // // unhandled device class + // } + // default: + // // unhandled request + // } + // default: + // // unhandled request type or recepient + // } + // default: + // // unhandled interface + // } +} + +// endpointQueueHead returns the queue head for the given endpoint address, +// encoded as direction D and endpoint number N with the 8-bit mask DxxxNNNN. +//go:inline +func (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint { + // endpoint queue head is device class-specific + switch dc.class.id { + case classDeviceCDCACM: + return &descCDCACM[dc.class.config-1].qh[endpointIndex(endpoint)] + default: + return nil + } +} + +func (dc *deviceController) endpointConfigure( + ep *dcdEndpoint, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + ep.config = uint32(packetSize) << 16 + if !zlp { + ep.config |= 1 << 29 + } + ep.current = nil + ep.transfer.next = dcdTransferEOL + ep.transfer.token = 0 + for i := range ep.transfer.pointer { + ep.transfer.pointer[i] = 0 + } + ep.transfer.param = 0 + ep.setup.bmRequestType = 0 + ep.setup.bRequest = 0 + ep.setup.wValue = 0 + ep.setup.wIndex = 0 + ep.setup.wLength = 0 + ep.first = nil + ep.last = nil + ep.callback = callback +} + +func (dc *deviceController) endpointConfigureRx( + endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(rxEndpoint(endpoint)) + dc.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrRxPos + } +} + +func (dc *deviceController) endpointConfigureTx( + endpoint uint8, packetSize uint16, zlp bool, callback dcdTransferCallback) { + + if endpoint < descCDCACMEndpointStatus || + endpoint > descCDCACMEndpointCount { + return + } + ep := dc.endpointQueueHead(txEndpoint(endpoint)) + dc.endpointConfigure(ep, packetSize, zlp, callback) + if nil != callback { + dc.endpointNotify |= (uint32(1) << endpoint) << descCDCACMConfigAttrTxPos + } +} + +// endpointComplete handles transfer completion of a data endpoint. +func (dc *deviceController) endpointComplete(endpoint uint8) { + ep := dc.endpointQueueHead(endpoint) + if nil == ep.first { + return + } + count := 0 + first := ep.first + for t, eol := first, false; !eol; t, eol = t.nextTransfer() { + if eol { + // reached end of list, new list empty + ep.first = nil + ep.last = nil + } else { + if 0 != t.token&(1<<7) { + // active transfer, new list begins here + ep.first = t + break + } else { + count += 1 + } + } + } + // invoke all callbacks + for i := 0; i < count; i++ { + next := first.next + ep.callback(first) + first = next + } +} + +func (dc *deviceController) transferPrepare( + transfer *dcdTransfer, data *uint8, size uint16, param uint32) { + transfer.next = dcdTransferEOL + transfer.token = (uint32(size) << 16) | (1 << 7) + addr := uintptr(unsafe.Pointer(data)) + for i := range transfer.pointer { + transfer.pointer[i] = addr + uintptr(i)*4096 + } + transfer.param = param +} + +func (dc *deviceController) transferSchedule( + endpoint *dcdEndpoint, mask uint32, transfer *dcdTransfer) { + + if nil != endpoint.callback { + transfer.token |= 1 << 15 + } + ivm := arm.DisableInterrupts() + last := endpoint.last + if nil != last { + last.next = transfer + if dc.bus.ENDPTPRIME.HasBits(mask) { + goto endTransfer + } + start := cycleCount() + estat := uint32(0) + for !dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) && + (cycleCount()-start < 2400) { + dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW) + estat = dc.bus.ENDPTSTAT.Get() + } + if 0 != estat&mask { + goto endTransfer + } + } + endpoint.transfer.next = transfer + endpoint.transfer.token = 0 + dc.bus.ENDPTPRIME.SetBits(mask) + endpoint.first = transfer +endTransfer: + endpoint.last = transfer + arm.EnableInterrupts(ivm) +} + +func (dc *deviceController) timerConfigure(timer int, usec uint32, fn func()) { + if timer < 0 || timer >= len(dc.timerInterrupt) { + return + } + dc.timerInterrupt[timer] = fn + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set(0) + dc.bus.GPTIMER0LD.Set(usec - 1) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE0) + case 1: + dc.bus.GPTIMER1CTRL.Set(0) + dc.bus.GPTIMER1LD.Set(usec - 1) + dc.bus.USBINTR.SetBits(nxp.USB_USBINTR_TIE1) + } +} + +func (dc *deviceController) timerOneShot(timer int) { + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set( + nxp.USB_GPTIMER0CTRL_GPTRUN | nxp.USB_GPTIMER0CTRL_GPTRST) + case 1: + dc.bus.GPTIMER1CTRL.Set( + nxp.USB_GPTIMER1CTRL_GPTRUN | nxp.USB_GPTIMER1CTRL_GPTRST) + } +} + +func (dc *deviceController) timerStop(timer int) { + switch timer { + case 0: + dc.bus.GPTIMER0CTRL.Set(0) + case 1: + dc.bus.GPTIMER1CTRL.Set(0) + } +} + +func (dc *deviceController) serialConfigure() { + acm := &descCDCACM[dc.class.config-1] + switch dc.speed { + case descDeviceSpeedHigh: + acm.rxSize = descCDCACMDataRxHSPacketSize + acm.txSize = descCDCACMDataTxHSPacketSize + default: + acm.rxSize = descCDCACMDataRxFSPacketSize + acm.txSize = descCDCACMDataTxFSPacketSize + } + acm.txHead = 0 + acm.txFree = 0 + acm.rxHead = 0 + acm.rxTail = 0 + acm.rxFree = 0 + dc.endpointConfigureTx(descCDCACMEndpointStatus, + acm.cxSize, false, nil) + dc.endpointConfigureRx(descCDCACMEndpointDataRx, + acm.rxSize, false, dc.serialNotify) + dc.endpointConfigureTx(descCDCACMEndpointDataTx, + acm.txSize, true, nil) + for i := range acm.rd { + dc.serialReceive(uint8(i)) + } + dc.timerConfigure(0, 75, dc.serialFlush) +} + +func (dc *deviceController) serialNotify(transfer *dcdTransfer) { + acm := &descCDCACM[dc.class.config-1] + len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) + p := transfer.param + if 0 == len { + // zero-length packet (ZLP) + dc.serialReceive(uint8(p)) + } else { + // data packet + h := acm.rxHead + if h != acm.rxTail { + // previous packet is still buffered + q := acm.rxQueue[h] + n := acm.rxCount[q] + if len <= descCDCACMRxSize-n { + // previous buffer has enough free space for this packet's data + _ = copy(acm.rx[q*descCDCACMRxSize+n:], + acm.rx[p*descCDCACMRxSize:uint16(p)*descCDCACMRxSize+len]) + acm.rxCount[q] = n + len + acm.rxFree += len + dc.serialReceive(uint8(p)) + return + } + } + // add this packet to Rx buffer + acm.rxCount[p] = len + acm.rxIndex[p] = 0 + h += 1 + if h > descCDCACMRDCount { // should be >= + h = 0 + } + acm.rxQueue[h] = uint16(p) + acm.rxHead = h + acm.rxFree += len + } +} + +func (dc *deviceController) serialReceive(endpoint uint8) { + ivm := arm.DisableInterrupts() + num := uint16(endpoint) & descEndptAddrNumberMsk + acm := &descCDCACM[dc.class.config-1] + buf := &acm.rx[num*descCDCACMRxSize] + dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) + nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) + dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) + arm.EnableInterrupts(ivm) +} + +func (dc *deviceController) serialFlush() { + const autoFlushTx = true + if !autoFlushTx { + return + } + acm := &descCDCACM[dc.class.config-1] + if 0 == acm.txFree { + return + } + xfer := &acm.td[acm.txHead] + buff := &acm.tx[uint16(acm.txHead)*descCDCACMTxSize] + size := descCDCACMTxSize - acm.txFree + dc.transferPrepare(xfer, buff, size, 0) + nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(buff)), uintptr(size)) + dc.transmit(descCDCACMEndpointDataTx, xfer) + acm.txHead += 1 + if acm.txHead >= descCDCACMTDCount { + acm.txHead = 0 + } + acm.txFree = 0 +} diff --git a/src/machine/usb2/dci_mimxrt1062.go b/src/machine/usb2/dci_mimxrt1062.go deleted file mode 100644 index 2af740f25..000000000 --- a/src/machine/usb2/dci_mimxrt1062.go +++ /dev/null @@ -1,174 +0,0 @@ -// +build mimxrt1062 - -package usb2 - -// Implementation of USB device controller interface (dci) for NXP iMXRT1062. - -import ( - "device/arm" - "device/nxp" - "runtime/interrupt" - "runtime/volatile" - "strconv" -) - -// dciCount defines the number of USB cores to configure for device mode. It is -// computed as the sum of all declared device configuration descriptors. -const dciCount = descCDCACMConfigCount - -// dciInterruptPriority defines the priority for all USB device interrupts. -const dciInterruptPriority = 3 - -// deviceController implements USB device controller interface (dci). -type deviceController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - id int // deviceControllerInstance index - - bus *nxp.USB_Type - phy *nxp.USBPHY_Type - irq interrupt.Interrupt - - cri volatile.Register8 // set to 1 if in critical section, else 0 - ivm uintptr // interrupt state when entering critical section -} - -// deviceControllerInstance provides statically-allocated instances of each USB -// device controller configured on this platform. -var deviceControllerInstance [dciCount]deviceController - -// initDCI initializes and assigns a free device controller instance to the -// given USB port. Returns the initialized device controller or nil if no free -// device controller instances remain. -func initDCI(port int) (dci, status) { - if 0 == dciCount { - return nil, statusInvalidArgument // must have defined device descriptors - } - // Return the first instance whose assigned core is currently nil. - for i := range deviceControllerInstance { - if nil == deviceControllerInstance[i].core { - // Initialize device controller. - deviceControllerInstance[i].core = &coreInstance[port] - deviceControllerInstance[i].port = port - deviceControllerInstance[i].id = i - switch port { - case 0: - deviceControllerInstance[i].bus = nxp.USB1 - deviceControllerInstance[i].phy = nxp.USBPHY1 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG1, - func(interrupt.Interrupt) { - coreInstance[0].dc.interrupt() - }) - - case 1: - deviceControllerInstance[i].bus = nxp.USB2 - deviceControllerInstance[i].phy = nxp.USBPHY2 - deviceControllerInstance[i].irq = - interrupt.New(nxp.IRQ_USB_OTG2, - func(interrupt.Interrupt) { - //coreInstance[1].dc.interrupt() - }) - } - return &deviceControllerInstance[i], statusOK - } - } - return nil, statusBusy // No free device controller instances available. -} - -func (dc *deviceController) init() status { - - // reset the controller - dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST) - for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) { - } - // clear interrupts - m := arm.DisableInterrupts() - switch dc.port { - case 0: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1)) - case 1: - arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2)) - } - dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST) - dc.phy.PWD.Set(0) - - // clear the controller mode field and set to device mode: - // controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only - dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2, - nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos) - - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold - dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout - dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness - - // TODO: configure ENDPOINTLISTADDR - - // enable interrupts - dc.bus.USBINTR.Set( - nxp.USB_USBINTR_UE_Msk | // bus enable - nxp.USB_USBINTR_UEE_Msk | // bus error - nxp.USB_USBINTR_PCE_Msk | // port change detect - nxp.USB_USBINTR_URE_Msk | // bus reset - nxp.USB_USBINTR_SLE) // sleep enable - - // ensure D+ pulled down long enough for host to detect previous disconnect - dc.udelay(5000) - - return statusOK -} - -func (dc *deviceController) enable(enable bool) status { - - if enable { - dc.irq.SetPriority(dciInterruptPriority) - dc.irq.Enable() - dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS) - } else { - dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS) - dc.irq.Disable() - } - return statusOK -} - -func (dc *deviceController) critical(enter bool) status { - if enter { - // check if critical section already locked - if dc.cri.Get() != 0 { - return statusRetry - } - // lock critical section - dc.cri.Set(1) - // disable interrupts, storing state in receiver - dc.ivm = arm.DisableInterrupts() - } else { - // ensure critical section is locked - if dc.cri.Get() != 0 { - // re-enable interrupts, using state stored in receiver - arm.EnableInterrupts(dc.ivm) - // unlock critical section - dc.cri.Set(0) - } - } - return statusOK -} - -func (dc *deviceController) interrupt() { - // read and clear the interrupts that fired - status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get() - dc.bus.USBSTS.Set(status) - - println(strconv.FormatUint(uint64(status), 16)) -} - -// udelay waits for the given number of microseconds before returning. -// We cannot use the sleep timer from this context (import cycle), but we need -// an approximate method to spin CPU cycles for short periods of time. -//go:inline -func (dc *deviceController) udelay(microsec uint32) { - n := cycles(microsec, descCPUFrequencyHz) - for i := uint32(0); i < n; i++ { - arm.Asm(`nop`) - } -} diff --git a/src/machine/usb2/desc.go b/src/machine/usb2/desc.go index 9a5b0e890..4642a76f2 100644 --- a/src/machine/usb2/desc.go +++ b/src/machine/usb2/desc.go @@ -1,21 +1,29 @@ package usb2 -const descUSBSpecVersion = 0x0200 // USB 2.0 +const descUSBSpecVersion = uint16(0x0200) // USB 2.0 + +const descLanguageEnglish = uint16(0x0409) + +type descIndexStrings [4][64]uint8 +type descLocalStrings struct { + language uint16 + index descIndexStrings // UTF-16, 32-character maximum length +} // USB constants defined per specification. const ( // Descriptor length - descLengthDevice = 18 - descLengthConfigure = 9 - descLengthInterface = 9 - descLengthEndpoint = 7 - descLengthDeviceQualitier = 10 - descLengthOTG = 5 - descLengthBOS = 5 - descLengthEndpointCompanion = 6 - descLengthDevCapTypeUSB20Extension = 7 - descLengthDevCapTypeSuperspeed = 10 + descLengthDevice = 18 + descLengthConfigure = 9 + descLengthInterface = 9 + descLengthEndpoint = 7 + descLengthQualification = 10 + descLengthOTG = 5 + descLengthBOS = 5 + descLengthEndpointCompanion = 6 + descLengthUSB20Extension = 7 + descLengthSuperspeed = 10 // Descriptor type descTypeDevice = 0x01 @@ -23,7 +31,7 @@ const ( descTypeString = 0x03 descTypeInterface = 0x04 descTypeEndpoint = 0x05 - descTypeDeviceQualitier = 0x06 + descTypeQualification = 0x06 descTypeOtherSpeedConfiguration = 0x07 descTypeInterfacePower = 0x08 descTypeOTG = 0x09 @@ -37,6 +45,36 @@ const ( descTypeCDCEndpoint = 0x25 descTypeEndpointCompanion = 0x30 + // Standard request type + descRequestTypeDirMsk = 0x80 + descRequestTypeDirPos = 7 + descRequestTypeDirOut = 0x00 + descRequestTypeDirIn = 0x80 + descRequestTypeTypeMsk = 0x60 + descRequestTypeTypePos = 5 + descRequestTypeTypeStandard = 0 + descRequestTypeTypeClass = 0x20 + descRequestTypeTypeVendor = 0x40 + descRequestTypeRecipientMsk = 0x1F + descRequestTypeRecipientPos = 0 + descRequestTypeRecipientDevice = 0x00 + descRequestTypeRecipientInterface = 0x01 + descRequestTypeRecipientEndpoint = 0x02 + descRequestTypeRecipientOther = 0x03 + + // Standard request + descRequestStandardGetStatus = 0x00 + descRequestStandardClearFeature = 0x01 + descRequestStandardSetFeature = 0x03 + descRequestStandardSetAddress = 0x05 + descRequestStandardGetDescriptor = 0x06 + descRequestStandardSetDescriptor = 0x07 + descRequestStandardGetConfiguration = 0x08 + descRequestStandardSetConfiguration = 0x09 + descRequestStandardGetInterface = 0x0A + descRequestStandardSetInterface = 0x0B + descRequestStandardSynchFrame = 0x0C + // Configuration attributes descConfigAttrD7Msk = 0x80 descConfigAttrD7Pos = 7 @@ -60,49 +98,55 @@ const ( descEndptAddrDirectionIn = 0x80 // Endpoint attributes - descEndptAttrTypeMsk = 0x03 - descEndptAttrNumberPos = 0 - descEndptAttrSyncTypeMsk = 0x0C - descEndptAttrSyncTypePos = 2 - descEndptAttrSyncTypeNoSync = 0x00 - descEndptAttrSyncTypeAsync = 0x04 - descEndptAttrSyncTypeAdaptive = 0x08 - descEndptAttrSyncTypeSync = 0x0C - descEndptAttrUsageTypeMsk = 0x30 - descEndptAttrUsageTypePos = 4 - descEndptAttrUsageTypeDataEndpoint = 0x00 - descEndptAttrUsageTypeFeedEndpoint = 0x10 - descEndptAttrUsageTypeFeedDataEndpoint = 0x20 + descEndptAttrTypeMsk = 0x03 + descEndptAttrNumberPos = 0 + descEndptAttrSyncTypeMsk = 0x0C + descEndptAttrSyncTypePos = 2 + descEndptAttrSyncTypeNoSync = 0x00 + descEndptAttrSyncTypeAsync = 0x04 + descEndptAttrSyncTypeAdaptive = 0x08 + descEndptAttrSyncTypeSync = 0x0C + descEndptAttrUsageTypeMsk = 0x30 + descEndptAttrUsageTypePos = 4 + descEndptAttrUsageTypeData = 0x00 + descEndptAttrUsageTypeFeed = 0x10 + descEndptAttrUsageTypeFeedData = 0x20 // Endpoint max packet size - descEndptMaxPktSizeSizeMsk = 0x07FF - descEndptMaxPktSizeMultTransMsk = 0x1800 - descEndptMaxPktSizeMultTransPos = 11 - descEndptMaxPktSizeMaximum = 64 + descEndptMaxPktSizeMsk = 0x07FF + descEndptMaxPktSize = 64 + descEndptMaxPktSizeMultMsk = 0x1800 + descEndptMaxPktSizeMultPos = 11 // OTG attributes descOTGAttrSRPMsk = 0x01 descOTGAttrHNPMsk = 0x02 descOTGAttrADPMsk = 0x04 + // Device bus speed + descDeviceSpeedFull = 0x00 + descDeviceSpeedLow = 0x01 + descDeviceSpeedHigh = 0x02 + descDeviceSpeedSuper = 0x04 + // Device capability type - descDevCapTypeWireless = 0x01 - descDevCapTypeUSB20Extension = 0x02 - descDevCapTypeSuperspeed = 0x03 + descDeviceCapTypeWireless = 0x01 + descDeviceCapTypeUSB20Extension = 0x02 + descDeviceCapTypeSuperspeed = 0x03 // Device capability attributes (USB 2.0 extension) - descDevCapExtAttrLPMMsk = 0x02 - descDevCapExtAttrLPMPos = 1 - descDevCapExtAttrBESLMsk = 0x04 - descDevCapExtAttrBESLPos = 2 + descDeviceCapExtAttrLPMMsk = 0x02 + descDeviceCapExtAttrLPMPos = 1 + descDeviceCapExtAttrBESLMsk = 0x04 + descDeviceCapExtAttrBESLPos = 2 ) // USB CDC constants defined per specification. const ( // Device class - descCDCComm = 0x02 // communication/control - descCDCData = 0x0A // data + descCDCTypeComm = 0x02 // communication/control + descCDCTypeData = 0x0A // data // Communication/control subclass descCDCSubNone = 0x00 @@ -146,60 +190,133 @@ const ( descCDCProtoUnitFunctional = 0xFE // Functional descriptor length - descCDCLengthFuncHeader = 5 - descCDCLengthFuncCallManagement = 5 - descCDCLengthFuncAbstractControl = 4 - descCDCLengthFuncUnion = 5 + descCDCFuncLengthHeader = 5 + descCDCFuncLengthCallManagement = 5 + descCDCFuncLengthAbstractControl = 4 + descCDCFuncLengthUnion = 5 // Functional descriptor type - descCDCTypeFuncHeader = 0x00 - descCDCTypeFuncCallManagement = 0x01 - descCDCTypeFuncAbstractControl = 0x02 - descCDCTypeFuncDirectLine = 0x03 - descCDCTypeFuncTelephoneRinger = 0x04 - descCDCTypeFuncTelephoneReport = 0x05 - descCDCTypeFuncUnion = 0x06 - descCDCTypeFuncCountrySelect = 0x07 - descCDCTypeFuncTelephoneModes = 0x08 - descCDCTypeFuncTerminal = 0x09 - descCDCTypeFuncNetworkChannel = 0x0A - descCDCTypeFuncProtocolUnit = 0x0B - descCDCTypeFuncExtensionUnit = 0x0C - descCDCTypeFuncMultiChannel = 0x0D - descCDCTypeFuncCAPIControl = 0x0E - descCDCTypeFuncEthernetNetworking = 0x0F - descCDCTypeFuncATMNetworking = 0x10 - descCDCTypeFuncWirelessControl = 0x11 - descCDCTypeFuncMobileDirectLine = 0x12 - descCDCTypeFuncMDLMDetail = 0x13 - descCDCTypeFuncDeviceManagement = 0x14 - descCDCTypeFuncOBEX = 0x15 - descCDCTypeFuncCommandSet = 0x16 - descCDCTypeFuncCommandSetDetail = 0x17 - descCDCTypeFuncTelephoneControl = 0x18 - descCDCTypeFuncOBEXServiceID = 0x19 + descCDCFuncTypeHeader = 0x00 + descCDCFuncTypeCallManagement = 0x01 + descCDCFuncTypeAbstractControl = 0x02 + descCDCFuncTypeDirectLine = 0x03 + descCDCFuncTypeTelephoneRinger = 0x04 + descCDCFuncTypeTelephoneReport = 0x05 + descCDCFuncTypeUnion = 0x06 + descCDCFuncTypeCountrySelect = 0x07 + descCDCFuncTypeTelephoneModes = 0x08 + descCDCFuncTypeTerminal = 0x09 + descCDCFuncTypeNetworkChannel = 0x0A + descCDCFuncTypeProtocolUnit = 0x0B + descCDCFuncTypeExtensionUnit = 0x0C + descCDCFuncTypeMultiChannel = 0x0D + descCDCFuncTypeCAPIControl = 0x0E + descCDCFuncTypeEthernetNetworking = 0x0F + descCDCFuncTypeATMNetworking = 0x10 + descCDCFuncTypeWirelessControl = 0x11 + descCDCFuncTypeMobileDirectLine = 0x12 + descCDCFuncTypeMDLMDetail = 0x13 + descCDCFuncTypeDeviceManagement = 0x14 + descCDCFuncTypeOBEX = 0x15 + descCDCFuncTypeCommandSet = 0x16 + descCDCFuncTypeCommandSetDetail = 0x17 + descCDCFuncTypeTelephoneControl = 0x18 + descCDCFuncTypeOBEXServiceID = 0x19 + + // Standard request + descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND + descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE + descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE + descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE + descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE + descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE + descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE + descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP + descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE + descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME + descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK + descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING + descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING + descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE + descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK + descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS + descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS + descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM + descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM + descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS + descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS + descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS + descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER + descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER + descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER + descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS + descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER + descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER + descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER + descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC + descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT + descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS + descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC + descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS + descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK + + // Notification type + descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION + descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL + descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE + descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT + descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE + descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE + descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE + descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE + + // Feature select + descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE + descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING + + // Control signal + descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION + descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE + + // UART emulated state + descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER + descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER + descCDCUARTStateBreak = 0x04 // UART state BREAK + descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL + descCDCUARTStateFraming = 0x10 // UART state FRAMING + descCDCUARTStateParity = 0x20 // UART state PARITY + descCDCUARTStateOverrun = 0x40 // UART state OVERRUN ) // Common configuration constants for the USB CDC-ACM (single) device class. const ( - // Interfaces for the first CDC-ACM device (index 1). - descCDCACM0InterfaceCount = 2 - descCDCACM0InterfaceCtrl = 0 - descCDCACM0InterfaceData = 1 - // Endpoints for the first CDC-ACM device (index 1). - descCDCACM0EndpointCount = 4 - descCDCACM0EndpointStatus = 2 // Communication/control interrupt input - descCDCACM0EndpointDataRx = 3 // Bulk data output - descCDCACM0EndpointDataTx = 4 // Bulk data input + // String descriptor languages + descCDCACMLanguageCount = 1 + // Interfaces for all CDC-ACM configurations. + descCDCACMInterfaceCount = 2 + descCDCACMInterfaceCtrl = 0 + descCDCACMInterfaceData = 1 + // Endpoints for all CDC-ACM configurations. + descCDCACMEndpointCount = 4 + descCDCACMEndpointStatus = 2 // Communication/control interrupt input + descCDCACMEndpointDataRx = 3 // Bulk data output + descCDCACMEndpointDataTx = 4 // Bulk data input + // Endpoint configuration attributes for all CDC-ACM configurations. + descCDCACMConfigAttrStatus = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrInterrupt << descCDCACMConfigAttrTxPos) + descCDCACMConfigAttrDataRx = (descCDCACMConfigAttrBulk << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrUnused << descCDCACMConfigAttrTxPos) + descCDCACMConfigAttrDataTx = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) | + (descCDCACMConfigAttrBulk << descCDCACMConfigAttrTxPos) // Size of all CDC-ACM configuration descriptors. descCDCACMConfigSize = uint16( descLengthConfigure + // configuration descLengthInterface + // communication/control interface - descCDCLengthFuncHeader + // CDC header - descCDCLengthFuncCallManagement + // CDC call management - descCDCLengthFuncAbstractControl + // CDC abstract control - descCDCLengthFuncUnion + // CDC union + descCDCFuncLengthHeader + // CDC header + descCDCFuncLengthCallManagement + // CDC call management + descCDCFuncLengthAbstractControl + // CDC abstract control + descCDCFuncLengthUnion + // CDC union descLengthEndpoint + // communication/control input endpoint descLengthInterface + // data interface descLengthEndpoint + // data input endpoint @@ -209,126 +326,200 @@ const ( (1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup 0 // Bits 0-4: reserved (0) + + descCDCACMConfigAttrRxPos = 0 + descCDCACMConfigAttrTxPos = 16 + descCDCACMConfigAttrUnused = 0x02 // TBD: what is this? + descCDCACMConfigAttrIsochronous = descCDCACMConfigAttr | descEndptAttrSyncTypeAsync + descCDCACMConfigAttrBulk = descCDCACMConfigAttr | descEndptAttrSyncTypeAdaptive + descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync ) -// Common descriptors for the USB CDC-ACM (single) device class. -var ( - // descDeviceCDCACM holds the default device descriptors for CDC-ACM devices. - descDeviceCDCACM = [descCDCACMConfigCount][descLengthDevice]uint8{ - { - descLengthDevice, // Size of this descriptor in bytes - descTypeDevice, // DEVICE Descriptor Type - lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) - msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) - descCDCComm, // Class code (assigned by the USB-IF). - descCDCSubNone, // Subclass code (assigned by the USB-IF). - descCDCProtoNone, // Protocol code (assigned by the USB-IF). - descEndptMaxPktSizeMaximum, // Maximum packet size for endpoint zero (8, 16, 32, or 64) - lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) - msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) - lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) - msU8(descProductID), // Product ID (high) (assigned by the manufacturer) - lsU8(descReleaseID), // Device release number in BCD (low) - msU8(descReleaseID), // Device release number in BCD (high) - 1, // Index of string descriptor describing manufacturer - 2, // Index of string descriptor describing product - 0, // Index of string descriptor describing the device's serial number - descCDCACMConfigCount, // Number of possible configurations +// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0String = [descCDCACMLanguageCount]descLocalStrings{ + { + language: descLanguageEnglish, + index: descIndexStrings{ + { // 0: language string + 4, + descTypeString, + lsU8(descLanguageEnglish), + msU8(descLanguageEnglish), + }, + { // 1: manufacturer + uint8(2 + 2*len(descManufacturer)), + descTypeString, + }, + { // 2: product + uint8(2 + 2*len(descProduct)), + descTypeString, + }, + { // 3: serial number + uint8(2 + 2*len(descSerialNumber)), + descTypeString, + }, }, + }, +} + +// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0Device = [descLengthDevice]uint8{ + descLengthDevice, // Size of this descriptor in bytes + descTypeDevice, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCTypeComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) + msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) + lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) + msU8(descProductID), // Product ID (high) (assigned by the manufacturer) + lsU8(descReleaseID), // Device release number in BCD (low) + msU8(descReleaseID), // Device release number in BCD (high) + 1, // Index of string descriptor describing manufacturer + 2, // Index of string descriptor describing product + 3, // Index of string descriptor describing the device's serial number + descCDCACMCount, // Number of possible configurations +} + +// descCDCACM0Qualif holds the default device qualification descriptor for +// CDC-ACM[0], i.e., configuration index 1. +var descCDCACM0Qualif = [descLengthQualification]uint8{ + descLengthQualification, // Size of this descriptor in bytes + descTypeQualification, // Descriptor Type + lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) + msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) + descCDCTypeComm, // Class code (assigned by the USB-IF). + descCDCSubNone, // Subclass code (assigned by the USB-IF). + descCDCProtoNone, // Protocol code (assigned by the USB-IF). + descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) + descCDCACMCount, // Number of possible configurations + 0, // Reserved +} + +// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0], +// i.e., configuration index 1. +var descCDCACM0Config = [descCDCACMConfigSize]uint8{ + descLengthConfigure, // Size of this descriptor in bytes + descTypeConfigure, // Descriptor Type + lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) + msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) + descCDCACMInterfaceCount, // Number of interfaces supported by this configuration + 1, // Value to use to select this configuration (1 = CDC-ACM[0]) + 0, // Index of string descriptor describing this configuration + descCDCACMConfigAttr, // Configuration attributes + descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) + + // Communication/Control Interface Descriptor + descLengthInterface, // Descriptor length + descTypeInterface, // Descriptor type + descCDCACMInterfaceCtrl, // Interface index + 0, // Alternate setting + 1, // Number of endpoints + descCDCTypeComm, // Class code + descCDCSubAbstractControl, // Subclass code + descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) + 0, // Interface Description String Index + + // CDC Header Functional Descriptor + descCDCFuncLengthHeader, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeHeader, // Descriptor Subtype + 0x10, // USB CDC specification version 1.10 (low) + 0x01, // USB CDC specification version 1.10 (high) + + // CDC Call Management Functional Descriptor + descCDCFuncLengthCallManagement, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeCallManagement, // Descriptor Subtype + 0x01, // Capabilities + descCDCACMInterfaceData, // Data Interface + + // CDC Abstract Control Management Functional Descriptor + descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeAbstractControl, // Descriptor Subtype + 0x06, // Capabilities + + // CDC Union Functional Descriptor + descCDCFuncLengthUnion, // Size of this descriptor in bytes + descTypeCDCInterface, // Descriptor Type + descCDCFuncTypeUnion, // Descriptor Subtype + descCDCACMInterfaceCtrl, // Controlling interface index + descCDCACMInterfaceData, // Controlled interface index + + // Communication/Control Notification Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointStatus | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeInterrupt, // Attributes + lsU8(descCDCACMStatusPacketSize), // Max packet size (low) + msU8(descCDCACMStatusPacketSize), // Max packet size (high) + 16, // Polling Interval + + // Data Interface Descriptor + descLengthInterface, // Interface length + descTypeInterface, // Interface type + descCDCACMInterfaceData, // Interface index + 0, // Alternate setting + 2, // Number of endpoints + descCDCTypeData, // Class code + descCDCSubNone, // Subclass code + descCDCProtoNone, // Protocol code + 0, // Interface Description String Index + + // Data Bulk Rx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataRx | // Endpoint address + descEndptAddrDirectionOut, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) + msU8(descCDCACMDataRxPacketSize), // Max packet size (high) + 0, // Polling Interval + + // Data Bulk Tx Endpoint descriptor + descLengthEndpoint, // Size of this descriptor in bytes + descTypeEndpoint, // Descriptor Type + descCDCACMEndpointDataTx | // Endpoint address + descEndptAddrDirectionIn, + descEndptTypeBulk, // Attributes + lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) + msU8(descCDCACMDataTxPacketSize), // Max packet size (high) + 0, // Polling Interval +} + +// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. +const descCDCACMCodingSize = 7 + +// descCDCACM0Coding holds the default UART line coding for CDC-ACM[0], i.e., +// configuration index 1. +var descCDCACM0Coding [descCDCACMCodingSize]uint8 + +type descCDCACMLineCoding struct { + baud uint32 + stopBits uint8 + parity uint8 + numBits uint8 + dtr bool + rts bool +} + +func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { + if len(buffer) < descCDCACMCodingSize { + return false } - - // descConfigCDCACM holds the default configuration descriptors for CDC-ACM - // devices. - descConfigCDCACM = [descCDCACMConfigCount][descCDCACMConfigSize]uint8{ - { - descLengthConfigure, // Size of this descriptor in bytes - descTypeConfigure, // Descriptor Type - lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low) - msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high) - descCDCACM0InterfaceCount, // Number of interfaces supported by this configuration - 1, // Value to use to select this configuration (1 = CDC-ACM[0]) - 0, // Index of string descriptor describing this configuration - descCDCACMConfigAttr, // Configuration attributes - descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units) - - // Communication/Control Interface Descriptor - descLengthInterface, // Descriptor length - descTypeInterface, // Descriptor type - descCDCACM0InterfaceCtrl, // Interface index - 0, // Alternate setting - 1, // Number of endpoints - descCDCComm, // Class code - descCDCSubAbstractControl, // Subclass code - descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250]) - 0, // Interface Description String Index - - // CDC Header Functional Descriptor - descCDCLengthFuncHeader, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncHeader, // Descriptor Subtype - 0x10, // USB CDC specification version 1.10 (low) - 0x01, // USB CDC specification version 1.10 (high) - - // CDC Call Management Functional Descriptor - descCDCLengthFuncCallManagement, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncCallManagement, // Descriptor Subtype - 0x01, // Capabilities - descCDCACM0InterfaceData, // Data Interface - - // CDC Abstract Control Management Functional Descriptor - descCDCLengthFuncAbstractControl, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncAbstractControl, // Descriptor Subtype - 0x06, // Capabilities - - // CDC Union Functional Descriptor - descCDCLengthFuncUnion, // Size of this descriptor in bytes - descTypeCDCInterface, // Descriptor Type - descCDCTypeFuncUnion, // Descriptor Subtype - descCDCACM0InterfaceCtrl, // Controlling interface index - descCDCACM0InterfaceData, // Controlled interface index - - // Communication/Control Notification Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACM0EndpointStatus | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeInterrupt, // Attributes - lsU8(descCDCACMStatusPacketSize), // Max packet size (low) - msU8(descCDCACMStatusPacketSize), // Max packet size (high) - 8, // Polling Interval - - // Data Interface Descriptor - descLengthInterface, // Interface length - descTypeInterface, // Interface type - descCDCACM0InterfaceData, // Interface index - 0, // Alternate setting - 2, // Number of endpoints - descCDCData, // Class code - descCDCSubNone, // Subclass code - descCDCProtoNone, // Protocol code - 0, // Interface Description String Index - - // Data Bulk Rx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACM0EndpointDataRx | // Endpoint address - descEndptAddrDirectionOut, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataRxPacketSize), // Max packet size (low) - msU8(descCDCACMDataRxPacketSize), // Max packet size (high) - 0, // Polling Interval - - // Data Bulk Tx Endpoint descriptor - descLengthEndpoint, // Size of this descriptor in bytes - descTypeEndpoint, // Descriptor Type - descCDCACM0EndpointDataTx | // Endpoint address - descEndptAddrDirectionIn, - descEndptTypeBulk, // Attributes - lsU8(descCDCACMDataTxPacketSize), // Max packet size (low) - msU8(descCDCACMDataTxPacketSize), // Max packet size (high) - 0, // Polling Interval - }, + lc.baud = packU32(buffer) + lc.stopBits = buffer[4] + if 0 == lc.stopBits { + lc.stopBits = 1 } -) + lc.parity = buffer[5] + lc.numBits = buffer[6] + return true +} diff --git a/src/machine/usb2/desc_mimxrt1062.go b/src/machine/usb2/desc_mimxrt1062.go index ea4a40c99..985cc68ba 100644 --- a/src/machine/usb2/desc_mimxrt1062.go +++ b/src/machine/usb2/desc_mimxrt1062.go @@ -1,3 +1,5 @@ +// +build mimxrt1062 + package usb2 // descCPUFrequencyHz defines the target CPU frequency (Hz). @@ -5,28 +7,183 @@ const descCPUFrequencyHz = 600000000 // General USB device identification constants. const ( - descVendorID = 0xABCD - descProductID = 0x1234 + descVendorID = 0x16C0 + descProductID = 0x0483 descReleaseID = 0x0101 - descVendor = "NXP Semiconductors" - descProduct = "TinyGo USB" + descManufacturer = "NXP Semiconductors" + descProduct = "TinyGo USB" + descSerialNumber = "0000000000" ) // Constants for USB CDC-ACM device classes. const ( - // descCDCACMConfigCount defines the number of USB cores that will be - // configured as CDC-ACM (single) devices. - descCDCACMConfigCount = 1 + // descCDCACMCount defines the number of USB cores that will be configured as + // CDC-ACM (single) devices. + descCDCACMCount = 1 + + descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1) + descCDCACMRDCount = 2 * descCDCACMEndpointCount + descCDCACMTDCount = descCDCACMEndpointCount + descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount + descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount + descCDCACMCxCount = 8 descCDCACMMaxPower = 50 // 100 mA descCDCACMStatusPacketSize = 16 - descCDCACMDataRxPacketSize = descCDCACMDataRxFSPacketSize // full-speed - descCDCACMDataTxPacketSize = descCDCACMDataTxFSPacketSize // full-speed + descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed + descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed + descCDCACMRxSize = descCDCACMDataRxPacketSize + descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize descCDCACMDataRxFSPacketSize = 64 // full-speed descCDCACMDataTxFSPacketSize = 64 // full-speed descCDCACMDataRxHSPacketSize = 512 // high-speed descCDCACMDataTxHSPacketSize = 512 // high-speed ) + +// descCDCACM0QH is an array of endpoint queue heads, which is where all +// transfers for a given endpoint are managed, for the default CDC-ACM (single) +// device class configuration (index 1). +// +// From the iMXRT1062 Reference Manual: +// +// Software must ensure that no interface data structure reachable +// by the Device Controller spans a 4K-page boundary. +// +// The [queue head] is a 48-byte data structure, but must be aligned on +// 64-byte boundaries. +// +// Endpoint queue heads are arranged in an array in a continuous area of +// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered +// device queue heads in the list support receive endpoints (OUT/SETUP) and +// the odd-numbered queue heads in the list are used for transmit endpoints +// (IN/INTERRUPT). The device controller will index into this array based upon +// the endpoint number received from the USB bus. All information necessary to +// respond to transactions for all primed transfers is contained in this list +// so the Device Controller can readily respond to incoming requests without +// having to traverse a linked list. +//go:align 4096 +var descCDCACM0QH [descCDCACMQHCount]dcdEndpoint + +// descCDCACM0CD is the transfer descriptor for data messages transmitted or +// received on the status/control endpoint 0 for the default CDC-ACM (single) +// device class configuration (index 1). +//go:align 32 +var descCDCACM0CD dcdTransfer + +// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages +// transmitted or received on the status/control endpoint 0 for the default +// CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0AD dcdTransfer + +// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers, +// which describe to the device controller the location and quantity of data +// being received for a given transfer, for the default CDC-ACM (single) device +// class configuration (index 1). +//go:align 32 +var descCDCACM0RD [descCDCACMRDCount]dcdTransfer + +// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers, +// which describe to the device controller the location and quantity of data +// being transmitted for a given transfer, for the default CDC-ACM (single) +// device class configuration (index 1). +//go:align 32 +var descCDCACM0TD [descCDCACMTDCount]dcdTransfer + +// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of +// the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Cx [descCDCACMCxCount]uint8 + +// descCDCACM0Rx is the receive (Rx) buffer of data endpoints for the default +// CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Rx [descCDCACMRxCount]uint8 + +// descCDCACM0Tx is the transmit (Tx) buffer of data endpoints for the default +// CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Tx [descCDCACMTxCount]uint8 + +// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data for the default +// CDC-ACM (single) device class configuration (index 1). +var descCDCACM0Dx [descCDCACMConfigSize]uint8 + +var descCDCACM0RDNum [descCDCACMRDCount]uint16 +var descCDCACM0RDIdx [descCDCACMRDCount]uint16 +var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 + +type descCDCACMClass struct { + locstr *[descCDCACMLanguageCount]descLocalStrings // string descriptors + device *[descLengthDevice]uint8 // device descriptor + qualif *[descLengthQualification]uint8 // device qualification descriptor + config *[descCDCACMConfigSize]uint8 // configuration descriptor + + coding *[descCDCACMCodingSize]uint8 // UART line coding + cticks int64 + rtsdtr uint8 + + qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads + + cd *dcdTransfer // control endpoint 0 Rx/Tx data transfer descriptor + ad *dcdTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor + rd *[descCDCACMRDCount]dcdTransfer // bulk data endpoint Rx (OUT) transfer descriptors + td *[descCDCACMTDCount]dcdTransfer // bulk data endpoint Tx (IN) transfer descriptors + + cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer + rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer + tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer + dx *[descCDCACMConfigSize]uint8 // descriptor data Tx (IN) transfer buffer + + cxSize uint16 + rxSize uint16 + txSize uint16 + + txHead uint8 + txFree uint16 + + rxHead uint8 + rxTail uint8 + rxFree uint16 + + rxCount *[descCDCACMRDCount]uint16 + rxIndex *[descCDCACMRDCount]uint16 + rxQueue *[descCDCACMRDCount + 1]uint16 +} + +// descCDCACM holds the configuration, endpoint, and transfer descriptors, along +// with the buffers and control states, for all of the CDC-ACM (single) device +// class configurations, ordered by configuration index (offset by -1). +var descCDCACM = [descCDCACMCount]descCDCACMClass{ + { + locstr: &descCDCACM0String, + device: &descCDCACM0Device, + qualif: &descCDCACM0Qualif, + config: &descCDCACM0Config, + + coding: &descCDCACM0Coding, + + qh: &descCDCACM0QH, + + cd: &descCDCACM0CD, + ad: &descCDCACM0AD, + rd: &descCDCACM0RD, + td: &descCDCACM0TD, + + cx: &descCDCACM0Cx, + rx: &descCDCACM0Rx, + tx: &descCDCACM0Tx, + dx: &descCDCACM0Dx, + + cxSize: descCDCACMStatusPacketSize, + rxSize: descCDCACMDataRxPacketSize, + txSize: descCDCACMDataTxPacketSize, + + rxCount: &descCDCACM0RDNum, + rxIndex: &descCDCACM0RDIdx, + rxQueue: &descCDCACM0RDQue, + }, +} diff --git a/src/machine/usb2/dci.go b/src/machine/usb2/hcd.go similarity index 85% rename from src/machine/usb2/dci.go rename to src/machine/usb2/hcd.go index 4ee63081d..dbbc43d06 100644 --- a/src/machine/usb2/dci.go +++ b/src/machine/usb2/hcd.go @@ -1,6 +1,6 @@ package usb2 -type dci interface { +type hcd interface { init() status enable(enable bool) status critical(enter bool) status diff --git a/src/machine/usb2/hci_mimxrt1062.go b/src/machine/usb2/hcd_mimxrt1062.go similarity index 78% rename from src/machine/usb2/hci_mimxrt1062.go rename to src/machine/usb2/hcd_mimxrt1062.go index 85f66ecc2..caf1cb9ab 100644 --- a/src/machine/usb2/hci_mimxrt1062.go +++ b/src/machine/usb2/hcd_mimxrt1062.go @@ -2,7 +2,7 @@ package usb2 -// Implementation of USB host controller interface (hci) for NXP iMXRT1062. +// Implementation of USB host controller driver (hcd) for NXP iMXRT1062. import ( "device/arm" @@ -11,18 +11,19 @@ import ( "runtime/volatile" ) -// hciCount defines the number of USB cores to configure for host mode. It is +// hcdCount defines the number of USB cores to configure for host mode. It is // computed as the sum of all declared host configuration descriptors. -const hciCount = 0 +const hcdCount = 0 -// hciInterruptPriority defines the priority for all USB host interrupts. -const hciInterruptPriority = 3 +// hcdInterruptPriority defines the priority for all USB host interrupts. +const hcdInterruptPriority = 3 -// hostController implements USB host controller interface (hci). +// hostController implements USB host controller driver (hcd) interface. type hostController struct { - core *core // Parent USB core this instance is attached to - port int // USB port index - id int // hostControllerInstance index + core *core // Parent USB core this instance is attached to + port int // USB port index + class class // USB host class + id int // hostControllerInstance index bus *nxp.USB_Type phy *nxp.USBPHY_Type @@ -34,14 +35,14 @@ type hostController struct { // hostControllerInstance provides statically-allocated instances of each USB // host controller configured on this platform. -var hostControllerInstance [hciCount]hostController +var hostControllerInstance [hcdCount]hostController -// initHCI initializes and assigns a free host controller instance to the given +// initHCD initializes and assigns a free host controller instance to the given // USB port. Returns the initialized host controller or nil if no free host // controller instances remain. -func initHCI(port int) (hci, status) { - if 0 == hciCount { - return nil, statusInvalidArgument // must have defined host descriptors +func initHCD(port int, class class) (hcd, status) { + if 0 == hcdCount { + return nil, statusInvalid // must have defined host controllers } // Return the first instance whose assigned core is currently nil. for i := range hostControllerInstance { @@ -82,7 +83,7 @@ func (hc *hostController) init() status { func (hc *hostController) enable(enable bool) status { - hc.irq.SetPriority(hciInterruptPriority) + hc.irq.SetPriority(hcdInterruptPriority) hc.irq.Enable() return statusOK diff --git a/src/machine/usb2/hci.go b/src/machine/usb2/hci.go deleted file mode 100644 index 7997c2351..000000000 --- a/src/machine/usb2/hci.go +++ /dev/null @@ -1,9 +0,0 @@ -package usb2 - -type hci interface { - init() status - enable(enable bool) status - critical(enter bool) status - interrupt() - udelay(micros uint32) -} diff --git a/src/machine/usb2/uart.go b/src/machine/usb2/uart.go index 83bd9ffe0..2ea1b222b 100644 --- a/src/machine/usb2/uart.go +++ b/src/machine/usb2/uart.go @@ -23,29 +23,13 @@ type ( func (uart *UART) Configure(config UARTConfig) error { - if uart.port >= CoreCount || uart.port >= dciCount || - uart.port >= descCDCACMConfigCount { + if uart.port >= CoreCount || uart.port >= dcdCount { return ErrInvalidPort } - // use default configuration index (1-based index; 0=invalid) - // uart.config = configDeviceCDCACMConfigurationIndex - - // modify the global basic configuration struct configDeviceCDCACM for our USB - // port and configuration index. - // - // these settings are copied into the real CDC-ACM object, using interface - // deviceClassDriver, via initialization method (*deviceCDCACM).init(). - - // change baud rate from default, if provided - //if config.BaudRate != 0 { - // configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate = - // config.BaudRate - //} - // verify we have a free USB port and take ownership of it var st status - uart.core, st = initCore(uart.port, modeDevice) + uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) if !st.ok() { return ErrInvalidPort } diff --git a/src/machine/usb2/usb.go b/src/machine/usb2/usb.go index c450f86f8..c5e88b10b 100644 --- a/src/machine/usb2/usb.go +++ b/src/machine/usb2/usb.go @@ -2,12 +2,20 @@ package usb2 // Hardware abstraction for USB ports configured as either host or device. +import "unsafe" + +func init() { + if unsafe.Sizeof(uintptr(0)) > 4 { + panic("USB is only supported on 32-bit systems") + } +} + // core represents the core of a USB port configured as either host or device. type core struct { port int mode int - dc dci - hc hci + dc dcd + hc hcd } // Constant definitions for USB core operating modes. @@ -17,9 +25,32 @@ const ( modeHost = 2 ) +// class represents the type of a host/device and its class configuration index. +// The first valid configuration index is 1. Index 0 is reserved and invalid. +type class struct { + id int + config int +} + +// Constant definitions for all host/device classes. +const ( + classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM) +) + +// mode returns the USB core operating mode of the receiver class cl. +//go:inline +func (cl class) mode() int { + switch cl.id { + case classDeviceCDCACM: + return modeDevice + default: + return modeIdle + } +} + // CoreCount defines the total number of USB cores to configure in device or // host mode. -const CoreCount = dciCount + hciCount +const CoreCount = dcdCount + hcdCount // coreInstance provides statically-allocated instances of each USB core // configured on this platform. @@ -30,30 +61,32 @@ type status uint8 // Constant definitions for all status codes used within the package. const ( - statusOK status = iota // Success - statusBusy // Busy - statusRetry // Retry - statusInvalidArgument // Invalid argument + statusOK status = iota // Success + statusBusy // Busy + statusRetry // Retry + statusInvalid // Invalid argument ) +// ok returns true if and only if the receiver st equals statusOK. +//go:inline func (st status) ok() bool { return statusOK == st } // initCore initializes a free USB core with given operating mode on the USB // port at given index, if available. Returns a reference to the initialized // core or nil if the core is unavailable. -func initCore(port, mode int) (*core, status) { +func initCore(port int, class class) (*core, status) { - if port < 0 || port >= CoreCount { - return nil, statusInvalidArgument + if port < 0 || port >= CoreCount || 0 == class.config { + return nil, statusInvalid } if modeIdle != coreInstance[port].mode { return nil, statusBusy } - switch mode { + switch class.mode() { case modeDevice: // Allocate a free device controller and install interrupts - dc, st := initDCI(port) + dc, st := initDCD(port, class) if !st.ok() { return nil, st } @@ -62,7 +95,7 @@ func initCore(port, mode int) (*core, status) { return nil, st } coreInstance[port].port = port - coreInstance[port].mode = mode + coreInstance[port].mode = modeDevice coreInstance[port].dc = dc // Enable interrupts and enter runtime if st = dc.enable(true); !st.ok() { @@ -73,7 +106,7 @@ func initCore(port, mode int) (*core, status) { case modeHost: // Allocate a free host controller and install interrupts - hc, st := initHCI(port) + hc, st := initHCD(port, class) if !st.ok() { return nil, st } @@ -82,7 +115,7 @@ func initCore(port, mode int) (*core, status) { return nil, st } coreInstance[port].port = port - coreInstance[port].mode = mode + coreInstance[port].mode = modeHost coreInstance[port].hc = hc // Enable interrupts and enter runtime if st = hc.enable(true); !st.ok() { @@ -92,7 +125,7 @@ func initCore(port, mode int) (*core, status) { } default: - return nil, statusInvalidArgument + return nil, statusInvalid } return &coreInstance[port], statusOK diff --git a/src/machine/usb2/util.go b/src/machine/usb2/util.go index 4b78c9fb5..35a082a7a 100644 --- a/src/machine/usb2/util.go +++ b/src/machine/usb2/util.go @@ -192,3 +192,25 @@ func lsU8(u uint16) uint8 { return uint8(u) } func cycles(microsec, cpuFreqHz uint32) uint32 { return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000) } + +//go:inline +func unpackEndpoint(address uint8) (number, direction uint8) { + return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos, + (address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos +} + +//go:inline +func rxEndpoint(number uint8) uint8 { + return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut +} + +//go:inline +func txEndpoint(number uint8) uint8 { + return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn +} + +//go:inline +func endpointIndex(address uint8) uint8 { + return ((address & descEndptAddrNumberMsk) << 1) | + ((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos) +} diff --git a/src/machine/usb2/util_arm.go b/src/machine/usb2/util_arm.go new file mode 100644 index 000000000..8f7b8a2a9 --- /dev/null +++ b/src/machine/usb2/util_arm.go @@ -0,0 +1,16 @@ +// +build arm + +package usb2 + +import "device/arm" + +// udelay waits for the given number of microseconds before returning. +// We cannot use the sleep timer from this context (import cycle), but we need +// an approximate method to spin CPU cycles for short periods of time. +//go:inline +func udelay(microsec uint32) { + n := cycles(microsec, descCPUFrequencyHz) + for i := uint32(0); i < n; i++ { + arm.Asm(`nop`) + } +}