//go:build atsamd51 || atsame5x // +build atsamd51 atsame5x package usb // Implementation of USB device controller hardware abstraction (dhw) for // Microchip SAMx51. import ( "device/arm" "device/sam" "math/bits" "runtime/interrupt" "runtime/volatile" "unsafe" ) // dhwInterruptPriority defines the priority for all USB device interrupts. const dhwInterruptPriority = 3 // dhw implements USB device controller hardware abstraction for iMXRT1062. type dhw struct { *dcd // USB device controller driver bus *sam.USB_DEVICE_Type // USB core registers irqEVT interrupt.Interrupt // USB IRQs irqSOF interrupt.Interrupt irqTC0 interrupt.Interrupt irqTC1 interrupt.Interrupt speed Speed ready bool // has init() been called ep [descMaxEndpoints]dhwEPAddrStatus setup dcdSetup stage dcdStage address uint16 } func deleteCache(addr, size uintptr) {} func flushCache(addr, size uintptr) {} func runBootloader() {} // allocDHW returns a reference to the USB hardware abstraction for the given // device controller driver. Should be called only one time and during device // controller initialization. func allocDHW(port, instance int, speed Speed, dc *dcd) *dhw { switch port { case 0: dhwInstance[instance].dcd = dc dhwInstance[instance].bus = sam.USB_DEVICE dhwInstance[instance].irqEVT = interrupt.New(sam.IRQ_USB_OTHER, func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqSOF = interrupt.New(sam.IRQ_USB_SOF_HSOF, func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqTC0 = interrupt.New(sam.IRQ_USB_TRCPT0, func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqTC1 = interrupt.New(sam.IRQ_USB_TRCPT1, func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) } // SAMx51 has only one USB PHY, which is full-speed if speed == 0 { speed = FullSpeed } dhwInstance[instance].speed = speed dhwInstance[instance].ready = false return &dhwInstance[instance] } // Calibrate DP/DM pads using value from NVM. Based on the following from // Atmel's CMSIS 1.2.2 for SAMD51: // // ... NOTE: These register defines are used to obtain calibration parameters // | // | #define NVMCTRL_SW0 (0x00800080UL) /**< \brief (NVMCTRL) SW0 Base Address * // | // ... // | // | #define USB_FUSES_TRANSN_ADDR (NVMCTRL_SW0 + 4) // | #define USB_FUSES_TRANSN_Pos 0 /**< \brief (NVMCTRL_SW0) USB pad Transn calibration */ // | #define USB_FUSES_TRANSN_Msk (_Ul(0x1F) << USB_FUSES_TRANSN_Pos) // | #define USB_FUSES_TRANSN(value) (USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)) // | // | #define USB_FUSES_TRANSP_ADDR (NVMCTRL_SW0 + 4) // | #define USB_FUSES_TRANSP_Pos 5 /**< \brief (NVMCTRL_SW0) USB pad Transp calibration */ // | #define USB_FUSES_TRANSP_Msk (_Ul(0x1F) << USB_FUSES_TRANSP_Pos) // | #define USB_FUSES_TRANSP(value) (USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)) // | // | #define USB_FUSES_TRIM_ADDR (NVMCTRL_SW0 + 4) // | #define USB_FUSES_TRIM_Pos 10 /**< \brief (NVMCTRL_SW0) USB pad Trim calibration */ // | #define USB_FUSES_TRIM_Msk (_Ul(0x7) << USB_FUSES_TRIM_Pos) // | #define USB_FUSES_TRIM(value) (USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)) // | // ... // | // | typedef union { // | struct { // | uint16_t TRANSP:5; /*!< bit: 0.. 4 USB Pad Transp calibration */ // | uint16_t :1; /*!< bit: 5 Reserved */ // | uint16_t TRANSN:5; /*!< bit: 6..10 USB Pad Transn calibration */ // | uint16_t :1; /*!< bit: 11 Reserved */ // | uint16_t TRIM:3; /*!< bit: 12..14 USB Pad Trim calibration */ // | uint16_t :1; /*!< bit: 15 Reserved */ // | } bit; /*!< Structure used for bit access */ // | uint16_t reg; /*!< Type used for register access */ // | } USB_PADCAL_Type; // | // ... NOTE: The following is where USB pad calibration actually occurrs: // | // | USB->DEVICE.PADCAL.bit.TRANSP = (*((uint32_t*) USB_FUSES_TRANSP_ADDR) & USB_FUSES_TRANSP_Msk) >> USB_FUSES_TRANSP_Pos; // | USB->DEVICE.PADCAL.bit.TRANSN = (*((uint32_t*) USB_FUSES_TRANSN_ADDR) & USB_FUSES_TRANSN_Msk) >> USB_FUSES_TRANSN_Pos; // | USB->DEVICE.PADCAL.bit.TRIM = (*((uint32_t*) USB_FUSES_TRIM_ADDR) & USB_FUSES_TRIM_Msk) >> USB_FUSES_TRIM_Pos; // | // ... // func (d *dhw) calibrate() { const reg = 0x00800080 + 4 // NVMCTRL_SW0 + 4 cal := *(*uint16)(unsafe.Pointer(uintptr(reg))) msk := uint16(sam.USB_DEVICE_PADCAL_TRANSP_Msk | sam.USB_DEVICE_PADCAL_TRANSN_Msk | sam.USB_DEVICE_PADCAL_TRIM_Msk) d.bus.PADCAL.ReplaceBits(cal, msk, 0) } // init configures the USB port for device mode operation by initializing all // endpoint and transfer descriptor data structures, initializing core registers // and interrupts, resetting the USB PHY, and enabling power on the bus. func (d *dhw) init() status { // Enable USB clocks // const clockGenerator = sam.GCLK_PCHCTRL_GEN_GCLK10 const clockGenerator = sam.PCHCTRL_GCLK_USB sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_) sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_) sam.GCLK.PCHCTRL[clockGenerator].Set( (sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN) // Reset USB peripheral for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { } d.bus.CTRLA.Set(sam.USB_DEVICE_CTRLA_SWRST) for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { } d.calibrate() // Initialize USB interrupt priorities d.irqEVT.SetPriority(dhwInterruptPriority) d.irqSOF.SetPriority(dhwInterruptPriority) d.irqTC0.SetPriority(dhwInterruptPriority) d.irqTC1.SetPriority(dhwInterruptPriority) // Clear interrupts m := arm.DisableInterrupts() & ^uintptr(sam.IRQ_USB_OTHER|sam.IRQ_USB_SOF_HSOF| sam.IRQ_USB_TRCPT0|sam.IRQ_USB_TRCPT1) arm.EnableInterrupts(m) // USB Quality of Service: High Quality (3) d.bus.QOSCTRL.Set((3 << sam.USB_DEVICE_QOSCTRL_CQOS_Pos) | (3 << sam.USB_DEVICE_QOSCTRL_DQOS_Pos)) // Install USB endpoint descriptor table (USB_DEVICE.DESCADD) d.bus.DESCADD.Set(uint32(d.descriptorTable())) // Configure bus speed (always full-speed (FS)), device mode, enable PHY, and // put finite-state machine (FSM) in standby. d.bus.CTRLB.Set(sam.USB_DEVICE_CTRLB_SPDCONF_FS) d.bus.CTRLA.Set(sam.USB_DEVICE_CTRLA_MODE_DEVICE | sam.USB_DEVICE_CTRLA_ENABLE | sam.USB_DEVICE_CTRLA_RUNSTDBY) for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) { } // Clear and enable interrupts in USB core d.bus.INTFLAG.Set(d.bus.INTFLAG.Get()) d.bus.INTENSET.Set( /*sam.USB_DEVICE_INTENSET_SOF |*/ sam.USB_DEVICE_INTENSET_EORST) // Ensure D+ pulled down long enough for host to detect a previous disconnect udelay(5000) d.ready = true return statusOK } // enable enables the USB interrupts, connects the device to the bus via // internal D+/D- pullup resistors, and enters the normal runtime. func (d *dhw) enable(enable bool) { if d.ready { // ensure init() has been called d.enableInterrupts(enable) d.connect(enable) } } // connect attaches the USB device by enabling/disabling the internal pullup // resistor on D+/D-. func (d *dhw) connect(connect bool) { if d.ready { // ensure init() has been called if connect { d.bus.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) } else { d.bus.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) } } } // enableInterrupts enables/disables all interrupts on the receiver's USB port. func (d *dhw) enableInterrupts(enable bool) { if d.ready { // ensure init() has been called if enable { d.irqEVT.Enable() // Enable USB interrupts d.irqSOF.Enable() d.irqTC0.Enable() d.irqTC1.Enable() } else { d.irqEVT.Disable() // Disable USB interrupts d.irqSOF.Disable() d.irqTC0.Disable() d.irqTC1.Disable() } } } // enableSOF enables or disables start-of-frame (SOF) interrupts on the given // USB device interface. func (d *dhw) enableSOF(enable bool, iface uint8) { // if changing enabled state, clear interrupt if enable != d.bus.INTENSET.HasBits(sam.USB_DEVICE_INTENSET_SOF) { d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SOF) } if enable { d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_SOF) } else { d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_SOF) } } // interrupt handles the USB hardware interrupt events on all four IRQ lines and // notifies the device controller driver using a common "virtual interrupt" // code. func (d *dhw) interrupt() { status := d.bus.INTFLAG.Get() & d.bus.INTENSET.Get() if status&sam.USB_DEVICE_INTFLAG_SOF != 0 { d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SOF) // TBD: handle SOF? } // SAMD doesn't distinguish between SUSPEND and DISCONNECT states. // Both conditions will trigger the SUSPEND interrupt. // To prevent it triggering when D+/D- are not stable, the SUSPEND interrupt is // only enabled after receiving SET_ADDRESS request and is cleared on RESET. if status&sam.USB_DEVICE_INTFLAG_SUSPEND != 0 { d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SUSPEND) d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_WAKEUP) d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_WAKEUP) d.event(dcdEvent{id: dcdEventStatusSuspend}) } if status&sam.USB_DEVICE_INTFLAG_WAKEUP != 0 { d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_WAKEUP) d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_WAKEUP) d.event(dcdEvent{id: dcdEventStatusResume}) } if status&sam.USB_DEVICE_INTFLAG_EORST != 0 { d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_WAKEUP | sam.USB_DEVICE_INTENCLR_SUSPEND) d.event(dcdEvent{id: dcdEventDeviceReady}) } num := endpointNumber(d.controlEndpoint()) if d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.HasBits( sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP) { d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.Set( sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP | sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) // Parse the SETUP packet immediately, clearing room in the (one and only) // control buffer for the next SETUP packet received. sup := setupFrom(d.controlSetupBuffer()) dir := sup.direction() // We've copied the SETUP packet elsewhere and are ready to receive another. d.prepareSetup() // Although there is only one control buffer, EP0 has two transfer queues: // 1×Rx(OUT) and 1×Tx(IN). First we decode the SETUP packet via setupFrom, // and based on its contained request's direction (IN vs OUT), we attempt // to enqueue a new transfer request in EP0's corresponding transfer queue. if ready, _ := d.ep[num][dir].scheduleSetup(sup); ready { // Begin processing the control packet immediately since there were no // pending transfers in the control EP0's IN/OUT transfer queue. d.controlTransferStart(packEndpoint(num, dir)) } else { // The EP0 IN/OUT transfer queue is busy servicing a previous request. // Stall the endpoint. d.controlStall(true, dir) } } epints := d.bus.EPINTSMRY.Get() & ((1 << descMaxEndpoints) - 1) for epints != 0 { ep := uint8(bits.TrailingZeros16(epints)) epints &^= 1 << ep intFlag := d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Get() out, in := d.endpointDescriptors(ep) // handle Tx (IN) endpoint complete if intFlag&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1 != 0 { pcksize := in.packetSize.Get() // number of bytes to be sent on next IN transaction count := (pcksize >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk // total number of bytes sent total := (pcksize >> USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) & USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk // maximum packet size size, _ := endpointSizeDecode((pcksize >> USB_DEVICE_PCKSIZE_SIZE_Pos) & USB_DEVICE_PCKSIZE_SIZE_Msk) d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set( sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) if ep == d.controlEndpoint() { d.controlStall(false, descDirTx) // check if there is more data to transfer or if we need to notify the // upper-layer device driver of a control transfer completion event. if count == 0 || count < size { d.controlTransferComplete(txEndpoint(ep), count, total) } else { d.controlTransferContinue(txEndpoint(ep), count, total) } } else if nil != d.ep[ep][descDirTx].callback { // call our device class-specific callback, if defined, on endpoint // data transfer complete events. d.ep[ep][descDirTx].callback(txEndpoint(ep), count) } } // handle Rx (OUT) endpoint complete if intFlag&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0 != 0 { pcksize := out.packetSize.Get() // number of bytes received on last OUT/SETUP transaction count := (pcksize >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk // total data size for the complete transfer total := (pcksize >> USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) & USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk // maximum packet size size, _ := endpointSizeDecode((pcksize >> USB_DEVICE_PCKSIZE_SIZE_Pos) & USB_DEVICE_PCKSIZE_SIZE_Msk) d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set( sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) if ep == d.controlEndpoint() { d.controlStall(false, descDirRx) // check if there is more data to transfer or if we need to notify the // upper-layer device driver of a control transfer completion event. if count == 0 || count < size { d.controlTransferComplete(rxEndpoint(ep), count, total) } else { d.controlTransferContinue(rxEndpoint(ep), count, total) } } else if nil != d.ep[ep][descDirRx].callback { // call our device class-specific callback, if defined, on endpoint // data transfer complete events. d.ep[ep][descDirRx].callback(rxEndpoint(ep), count) } } } } // prepareSetup configures the buffer for setup packets received on control // endpoint 0 Rx (OUT). func (d *dhw) prepareSetup() { desc := d.endpointDescriptor(rxEndpoint(d.controlEndpoint())) // control buffer address is device class-specific desc.address.Set(uint32(d.controlSetupBuffer())) // overwrite the BYTE_COUNT and MULTI_PACKET_SIZE bitfields only (with 0 and // sizeof(dcdSetup), respectively). var mask uint32 mask |= USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk << USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos mask |= USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk << USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos desc.packetSize.ReplaceBits(pcksize(0, uint32(dcdSetupSize), 0, false), mask, 0) } func (d *dhw) setDeviceAddress(addr uint16) { // SAMx51 can only set address after status for this request is complete, // which is checked in (*dhw).controlStatusComplete(dcdSetup). // Save the device address to the receiver, because the SETUP packet // containing SET_ADDRESS request is not populating wValue correctly. d.address = addr // Enable SUSPEND interrupt since the bus signal D+/D- are stable now. d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SUSPEND) d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_SUSPEND) } func (d *dhw) remoteWakeup() { d.bus.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_UPRSM) } // ============================================================================= // Control Endpoint 0 // ============================================================================= // controlStall stalls a transfer on control endpoint 0. To stall a transfer on // any other endpoint, use method endpointStall(). func (d *dhw) controlStall(stall bool, dir uint8) { // Argument dir will be either 0 = Rx (OUT), or 1 = Tx (IN). // We need to translate this to the USB standard, which is encoded as // direction D and endpoint number N with the 8-bit mask DxxxNNNN. // The value of direction D bit is the same as argument dir described above. endpoint := ((dir << descEndptAddrDirectionPos) & descEndptAddrDirectionMsk) | ((d.controlEndpoint() << descEndptAddrNumberPos) & descEndptAddrNumberMsk) d.endpointStall(endpoint, stall) } func (d *dhw) controlStatusStart(endpoint uint8) { num, dir := unpackEndpoint(endpoint) // Swap direction of the given endpoint Rx->Tx and Tx->Rx switch dir { case descDirRx: endpoint = txEndpoint(num) case descDirTx: endpoint = rxEndpoint(num) } d.endpointTransfer(endpoint, 0, 0) } func (d *dhw) controlStatusComplete(endpoint uint8) { if (d.setup.bmRequestType&descRequestTypeTypeMsk == descRequestTypeTypeStandard) && (d.setup.bmRequestType&(descRequestTypeRecipientMsk|descRequestTypeDirMsk) == descRequestTypeRecipientDevice|descRequestTypeDirOut) && (d.setup.bRequest == descRequestStandardSetAddress) { d.bus.DADD.SetBits((uint8(d.address) << sam.USB_DEVICE_DADD_DADD_Pos) & sam.USB_DEVICE_DADD_DADD_Msk) d.bus.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN) d.event(dcdEvent{id: dcdEventDeviceAddress}) } d.prepareSetup() } func (d *dhw) controlTransferStart(endpoint uint8) { num, dir := unpackEndpoint(endpoint) // Dequeue the next transfer descriptor available. if xfer, ok := d.ep[num][dir].pendingTransfer(); ok { // Update the active transfer descriptor on the corresponding endpoint. d.ep[num][dir].setActiveTransfer(xfer) // Invoke the DCD event handler for SETUP processing, which will enqueue // any necessary response transactions, which are serviced immediately // because this is the sole active transfer on this endpoint. d.event(dcdEvent{ id: dcdEventControlSetup, setup: xfer.setup, }) } } func (d *dhw) controlTransferContinue(endpoint uint8, count, total uint32) { num, dir := unpackEndpoint(endpoint) if xfer, ok := d.ep[num][dir].activeTransfer(); ok { data, size := xfer.packetComplete(count) d.endpointTransfer(endpoint, data, size) if size == 0 || size < xfer.maxPacketSize { d.controlTransferComplete(endpoint, count, total) } } } func (d *dhw) controlTransferComplete(endpoint uint8, count, total uint32) { num, dir := unpackEndpoint(endpoint) setupDir := d.setup.direction() setupAddress := packEndpoint(num, setupDir) // If endpoint direction is opposite the direction in the original SETUP // packet, then this is the end of the STATUS stage, i.e., end of transfer. if dir != setupDir { // Run any post-processing for this endpoint. d.controlStatusComplete(setupAddress) // Notify the upper-layer device driver. d.event(dcdEvent{id: dcdEventControlComplete}) // Clear the active transfer descriptor on this endpoint. d.ep[num][setupDir].setActiveTransfer(nil) // Start processing any pending control transfers. d.controlTransferStart(setupAddress) } else { // Initiate ZLP transfer in opposite direction. d.controlStatusStart(endpoint) } } // controlReceive receives (Rx, OUT) the first data packet on control endpoint 0. // If the given data pointer and size are both 0, then a zero-length status // packet (ZLP) is transmitted (Tx, IN) on control endpoint 0. func (d *dhw) controlReceive(data uintptr, size uint32, notify bool) { ep := d.controlEndpoint() if size > 0 && data > 0 { if xfer, ok := d.ep[ep][descDirRx].activeTransfer(); ok { next := xfer.packetStart(data, size) d.endpointTransfer(rxEndpoint(ep), data, next) } } else { d.endpointTransfer(txEndpoint(ep), 0, 0) } } // controlTransmit transmits (Tx, IN) the first data packet on control endpoint 0. // If the given data pointer and size are both 0, then a zero-length status // packet (ZLP) is received (Rx, OUT) on control endpoint 0. func (d *dhw) controlTransmit(data uintptr, size uint32, notify bool) { ep := d.controlEndpoint() if size > 0 && data > 0 { if xfer, ok := d.ep[ep][descDirTx].activeTransfer(); ok { next := xfer.packetStart(data, size) d.endpointTransfer(txEndpoint(ep), data, next) } } else { d.endpointTransfer(rxEndpoint(ep), 0, 0) } } // ============================================================================= // Endpoint Transfer Descriptor // ============================================================================= type dhwTransfer struct { endpoint uint8 maxPacketSize uint32 setup dcdSetup data uintptr size uint32 sent uint32 } // dhwTransferDepth defines the size of the dhwEPStatus.xferQueue buffered channel, // which affects the number of transfers each endpoint can enqueue for processing. const dhwTransferDepth = 8 type dhwTransferLUT [dhwTransferDepth]dhwTransfer func (t *dhwTransfer) init(endpoint uint8, maxPacketSize uint32) { t.endpoint = endpoint t.maxPacketSize = maxPacketSize t.reset() } func (t *dhwTransfer) reset() { // Do not clear the endpoint field, as it is statically-assigned (during // program initialization) and is never intended to change. t.setup.set(0) t.data = 0 t.size = 0 t.sent = 0 } func (t *dhwTransfer) packetStart(data uintptr, size uint32) (next uint32) { t.data = data t.size = size t.sent = 0 if next = size; next > t.maxPacketSize { next = t.maxPacketSize } return next } func (t *dhwTransfer) packetComplete(sent uint32) (data uintptr, size uint32) { t.sent = sent if size = t.size - t.sent; size > t.maxPacketSize { size = t.maxPacketSize } return t.data + uintptr(t.sent), size } func (t *dhwTransfer) hasDataPayload() bool { return t.data != 0 || t.size != 0 } func (t *dhwTransfer) hasSetupPayload() bool { return t.setup.pack() != 0 } func (t *dhwTransfer) hasPayload() bool { return t.hasDataPayload() || t.hasSetupPayload() } // ============================================================================= // Endpoint Configuration and Status // ============================================================================= // dhwEPStatus holds the status, completion callback of the configured class // driver, and the transfer queue for a given endpoint. // // The transfer queue is structured as follows: // // - The xferTable field is a statically-allocated, single-dimensional array // used as a buffer of transfer requests - known as transfer descriptors - // on a single, directional endpoint. // // - The length of xferTable defines the maximum number of pending transfers // in a given direction on a single endpoint. // // - Since the transfer descriptors are statically-allocated, we do not risk // heap allocation when requesting transfers in the USB interrupt handler. // // - The xferQueue field is a buffered channel of uint8 with capacity equal to // the length of the transfer descriptor table xferTable. // // - To enqueue a new transfer request, the xferTable is first scanned to find // the index of an unused transfer descriptor. The descriptor at this table // index is populated with the transfer details, and this table index is // written to the xferQueue channel. // // - If no other transfer descriptors are queued, the transfer is immediatly // sent to the USB. Otherwise, the next descriptor in queue will be read // from the xferQueue channel upon the next transfer complete interrupt // triggered on this endpoint. // // - Once the transfer descriptor's table index is read from the xferQueue // channel, the descriptor is cleared in the xferTable, marking it free for // use with a subsequent transfer request. // type dhwEPStatus struct { device *dhw endpoint uint8 callback func(endpoint uint8, size uint32) flags volatile.Register8 xferActive volatile.Register32 xferFIFO [dhwTransferDepth]uint8 xferQueue Queue xferTable dhwTransferLUT } // dhwEPAddrStatus contains an endpoint number's dhwEPStatus for both IN + OUT // directions. type dhwEPAddrStatus [2]dhwEPStatus // Bitmasks for each bitfield stored in volatile field dhwEPStatus.flags. const ( dhwEPStatusStatusBusy = 0x1 dhwEPStatusStatusStalled = 0x2 dhwEPStatusStatusClaimed = 0x4 ) func (s *dhwEPStatus) init(dhw *dhw, endpoint uint8) { s.device = dhw s.endpoint = endpoint s.callback = nil s.flags.Set(0) fifo := s.xferFIFO[:] s.xferQueue.Init(&fifo, dhwTransferDepth, QueueFullDiscardLast) mps := dhw.endpointMaxPacketSize(endpoint) for i := range s.xferTable { s.xferTable[i].init(endpoint, mps) } } // Accessor methods to return the logical boolean value from the bit value // stored in volatile field dhwEPStatus.flags. func (s *dhwEPStatus) busy() bool { return s.flags.HasBits(dhwEPStatusStatusBusy) } func (s *dhwEPStatus) stalled() bool { return s.flags.HasBits(dhwEPStatusStatusStalled) } func (s *dhwEPStatus) claimed() bool { return s.flags.HasBits(dhwEPStatusStatusClaimed) } // Mutator methods to set the bit value from the logical boolean value stored in // volatile field dhwEPStatus.flags. func (s *dhwEPStatus) setBusy(set bool) { s.setFlags(set, dhwEPStatusStatusBusy) } func (s *dhwEPStatus) setStalled(set bool) { s.setFlags(set, dhwEPStatusStatusStalled) } func (s *dhwEPStatus) setClaimed(set bool) { s.setFlags(set, dhwEPStatusStatusClaimed) } // setFlags consolidates the common logic of each dhwEPStatus mutator method // defined above. func (s *dhwEPStatus) setFlags(set bool, mask uint8) { if set { s.flags.SetBits(mask) } else { s.flags.ClearBits(mask) } } // hasActiveTransfer returns true if and only if the receiver's active transfer // descriptor is not nil. // // Note that the result of this call does not guarantee a subsequent call to // activeTransfer will succeed, as the active transfer may have been cleared // preemptively (from the USB interrupt handler) during the time between these // two calls. Thus, you should always verify an active transfer descriptor was // obtained with the bool value returned from activeTransfer. func (s *dhwEPStatus) hasActiveTransfer() bool { _, ok := s.activeTransfer() return ok } // activeTransfer returns a pointer to the receiver's active transfer descriptor // being processed in one of the transaction stages (SETUP, DATA, or STATUS). // The bool value returned is true if and only if the receiver's active transfer // descriptor is not nil. // // The pointer returned refers to an element in the receiver's xferTable, which // is also used by the receiver's pending transfer queue (FIFO). Thus, you can // (and should) use this object to reset transfer descriptors when processing // has completed (using (*dhwTransfer).reset()). This frees the descriptor and // allows new transfer requests to be scheduled. // You may also use (*dhwEPStatus).setActiveTransfer(nil) to free the descriptor // if the receiver's active transfer descriptor is not nil. func (s *dhwEPStatus) activeTransfer() (*dhwTransfer, bool) { if active := s.xferActive.Get(); active != 0 { return (*dhwTransfer)(unsafe.Pointer(uintptr(active))), true } return nil, false } // setActiveTransfer sets or clears the receiver's active transfer descriptor. // The receiver's active transfer descriptor is cleared if the given transfer // descriptor is nil. // // If the given transfer descriptor is nil, and the receiver's active transfer // descriptor is not nil, then the receiver's active transfer descriptor is // reset, marking it free for use by the receiver's transfer queue (FIFO). // // The given transfer descriptor should be a pointer into the receiver's // transfer table xferTable. This enables interaction with the receiver's // transfer queue, allowing it to detect when a descriptor is busy or available // for scheduling. func (s *dhwEPStatus) setActiveTransfer(xfer *dhwTransfer) { if xfer == nil { // Clearing the active transfer. Check if an active descriptor exists. if actv, ok := s.activeTransfer(); ok { // Reset the descriptor, freeing it for use in the transfer queue (FIFO). actv.reset() } s.xferActive.Set(0) } else { s.xferActive.Set(uint32(uintptr(unsafe.Pointer(xfer)))) } } // hasPendingTransfer returns true if and only if the number of pending // transfers in the receiver's transfer queue is greater than zero. // // Note that the result of this call does not guarantee that calls to either // pendingTransfer/scheduleSetup/scheduleTransfer will succeed, as new requests // may be added/removed preemptively (from the USB interrupt handler) during the // time between these two calls. Thus, you should always verify queue operations // operations by inspecting the final bool value returned by each of these // mentioned functions. func (s *dhwEPStatus) hasPendingTransfer() bool { return s.xferQueue.Len() > 0 } // pendingTransfer dequeues the table index - referring to the next transfer // descriptor to be processed - from the receiver's xferQueue, returning the // transfer descriptor at that index and true to indicate a pending transfer // descriptor was successfully obtained. // // If the receiver's transfer queue is empty, then the returned values are nil // and a false bool value to indicate failure to obtain a pending transfer // descriptor. func (s *dhwEPStatus) pendingTransfer() (*dhwTransfer, bool) { if s.hasPendingTransfer() { s.device.enableInterrupts(false) defer s.device.enableInterrupts(true) if i, ok := s.xferQueue.Deq(); ok { return &s.xferTable[i], true } } return nil, false } // claimSchedule disables interrupts and scans the receiver's transfer table // for an unused transfer descriptor, returning its table index and true. // If all transfer descriptors are already claimed, re-enables interrupts and // returns -1 and false. // // -- ** IMPORTANT ** -- // Note that interrupts are NOT re-enabled when a transfer index is // successfully found and returned. This ensures no race condition exists // between locating a free transfer index and initializing the transfer at that // index. These two events must not be preempted by another scheduling request // from the USB interrupt handler. // The caller must re-enable interrupts once the available transfer at the // vacant index has been processed. // // ( Because of this potentially danerous behavior, claimSchedule should be // restricted to the scheduling methods — scheduleTransfer and scheduleSetup — // so it can be verified easily that interrupts get re-enabled in all cases. ) func (s *dhwEPStatus) claimSchedule() (int, bool) { // Disable interrupts while scanning the xferTable s.device.enableInterrupts(false) for i := range s.xferTable { // Check that transfer has no payloads if !s.xferTable[i].hasPayload() { // Return index into xferTable (leave interrupts disabled!) return i, true } } // All elements of xferTable have a payload, so we cannot schedule a new // transfer. This request will be ignored, and we can re-enable interrupts // immediately. // // Realistically, we should never encounter this condition with a // sufficiently-sized xferTable/xferQueue and a well-behaved USB host. // // If you do reach this point, check that the transfers are being cleaned // up properly (with (*dhwTransfer).reset()) in the respective transfer // completion event handler. s.device.enableInterrupts(true) return -1, false } // scheduleTransfer enqueues a new data transfer descriptor to the receiver's // transfer queue. // // The first bool returned indicates if this transfer request is the the only // request in the queue, no other active transfer exists, and is thus available // for immediate processing. // The second bool returned is true if and only if the transfer request was // added to the queue successfully. // If the receiver's transfer queue is full, the request is ignored and false is // returned for both return values. func (s *dhwEPStatus) scheduleTransfer(data uintptr, size uint32) (ready bool, ok bool) { var i int if i, ok = s.claimSchedule(); ok { defer s.device.enableInterrupts(true) s.xferTable[i].reset() s.xferTable[i].data = data s.xferTable[i].size = size return !s.hasActiveTransfer() && !s.hasPendingTransfer(), s.xferQueue.Enq(uint8(i)) } return false, false } // scheduleSetup enqueues a new control SETUP transfer to the receiver's // transfer queue. // // The first bool returned indicates if this transfer request is the the only // request in the queue, no other active transfer exists, and is thus available // for immediate processing. // The second bool returned is true if and only if the transfer request was // added to the queue successfully. // If the receiver's transfer queue is full, the request is ignored and false is // returned for both return values. func (s *dhwEPStatus) scheduleSetup(setup dcdSetup) (ready bool, ok bool) { var i int if i, ok = s.claimSchedule(); ok { defer s.device.enableInterrupts(true) s.xferTable[i].reset() s.xferTable[i].setup = setup return !s.hasActiveTransfer() && !s.hasPendingTransfer(), s.xferQueue.Enq(uint8(i)) } return false, false } // ============================================================================= // Endpoint Descriptor // ============================================================================= // dhwEPDesc defines a USB endpoint descriptor, used to inform the USB DMA // controller the location of each endpoint transfer buffer. // // Access to these instances is controlled; i.e., you shouldn't need to use // them directly. Instead, use the higher-level API on types dhwEPStatus and // dhwTransfer, through the (*dhw).ep[num][dir] elements, for scheduling and // inspecting endpoint transfers. type dhwEPDesc struct { address volatile.Register32 packetSize volatile.Register32 extToken volatile.Register16 bankStatus volatile.Register8 _ [5]uint8 } // dhwEPAddrDesc defines an endpoint address descriptor, representing both // directions (IN + OUT) of a given endpoint descriptor. type dhwEPAddrDesc [2]dhwEPDesc // Constants defining bitfields in the endpoint descriptor hardware register // PCKSIZE. These were left out of the SVD for some reason. const ( USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk = 0x3FFF USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk = 0x3FFF USB_DEVICE_PCKSIZE_SIZE_Pos = 28 USB_DEVICE_PCKSIZE_SIZE_Msk = 0x7 USB_DEVICE_PCKSIZE_AUTOZLP_Pos = 31 USB_DEVICE_PCKSIZE_AUTOZLP_Msk = 0x1 ) // pcksize is a convenience routine that constructs the bitfields of the PCKSIZE // register of the USB_DEVICE peripheral, whose Pos/Msk definitions were ommitted // from the SVD-generated device file. //go:inline func pcksize(byteCount, multiPacketSize, size uint32, zlp bool) uint32 { var zlpMask uint32 if zlp { zlpMask = USB_DEVICE_PCKSIZE_AUTOZLP_Msk << USB_DEVICE_PCKSIZE_AUTOZLP_Pos } return ((byteCount & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) << USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) | ((multiPacketSize & USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk) << USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) | ((size & USB_DEVICE_PCKSIZE_SIZE_Msk) << USB_DEVICE_PCKSIZE_SIZE_Pos) | (zlpMask) } var ( // endpointSizeEnum is a constant-time lookup table for translating packet // sizes (bytes) to the corresponding register PCKSIZE.SIZE enumerated value. // // These tables are used instead of simple arithmetic (powers of 2) because of // the exceptional case with packet size = 1023. endpointSizeEnum = map[uint32]uint32{ 8: 0, 16: 1, 32: 2, 64: 3, 128: 4, 256: 5, 512: 6, 1023: 7, } // endpointEnumSize is a constant-time lookup table for translating the // register PCKSIZE.SIZE enumerated values to its packet size (bytes). // // These tables are used instead of simple arithmetic (powers of 2) because of // the exceptional case with packet size = 1023. endpointEnumSize = [8]uint32{ /* 0= */ 8, /* 1= */ 16, /* 2= */ 32, /* 3= */ 64, /* 4= */ 128, /* 5= */ 256, /* 6= */ 512, /* 7= */ 1023, } ) // endpointSizeEncode returns the register PCKSIZE.SIZE enumerated value for a // given endpoint descriptor packet size (bytes). // // See documentation on endpoint descriptor bank SRAM register PCKSIZE, bit // field SIZE for details. //go:inline func endpointSizeEncode(size uint32) (enum uint32, ok bool) { enum, ok = endpointSizeEnum[size] return } // endpointSizeDecode returns the endpoint descriptor packet size (bytes) for a // given register PCKSIZE.SIZE enumerated value. // // See documentation on endpoint descriptor bank SRAM register PCKSIZE, bit // field SIZE for details. //go:inline func endpointSizeDecode(enum uint32) (size uint32, ok bool) { if ok = int(enum) < len(endpointEnumSize); ok { size = endpointEnumSize[enum] } return } // endpointDescriptors returns the OUT + IN endpoint descriptors for the given // endpoint number, encoded as direction D and endpoint number N with the 8-bit // mask D000NNNN. The direction bit D is ignored. //go:inline func (d *dhw) endpointDescriptors(endpoint uint8) (out, in *dhwEPDesc) { // endpoint descriptor is device class-specific return d.endpointDescriptor(rxEndpoint(endpoint)), d.endpointDescriptor(txEndpoint(endpoint)) } func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) { if control { // Configure control endpoint 0 Rx (bank 0, OUT) and Tx (bank 1, IN) out, in := d.endpointDescriptors(d.controlEndpoint()) if enum, ok := endpointSizeEncode(descControlPacketSize); ok { num := endpointNumber(d.controlEndpoint()) // Initialize IN and OUT transfer descriptors on control endpoint 0. d.ep[num][descDirRx].init(d, rxEndpoint(num)) d.ep[num][descDirTx].init(d, txEndpoint(num)) // Conigure packet size for control endpoints. out.packetSize.ReplaceBits(enum, USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) in.packetSize.ReplaceBits(enum, USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) // rxType/txType uses the same rationale as epType (defined below in the // else-branch that handles non-control endpoints). // Thus, we add +1 to the value below. // // See the comment above the previously-mentioned epType (below) rxType := uint8(descEndptTypeControl+1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos txType := uint8(descEndptTypeControl+1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos // Configure bank 0 Rx (SETUP/OUT) as CONTROL, bank 1 Tx (IN) as CONTROL. d.bus.DEVICE_ENDPOINT[num].EPCFG.Set(rxType | txType) // Enable transfer complete and SETUP received interrupts d.bus.DEVICE_ENDPOINT[num].EPINTENSET.Set( sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0 | sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1 | sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP) // Prepare to start processing SETUP packets d.prepareSetup() } } else { desc := d.endpointDescriptor(endpoint) if enum, ok := endpointSizeEncode(d.endpointMaxPacketSize(endpoint)); ok { num, dir := unpackEndpoint(endpoint) // Initialize transfer descriptors now that the device class configuration // has been defined, which affects maximum packet size. d.ep[num][dir].init(d, endpoint) desc.packetSize.ReplaceBits(enum, USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) // config contains the bmAttributes field per USB standard EP descriptor, // i.e., ctrl=0, iso=1, bulk=2, int=3, which corresponds to the EPCFG // register's EPTYPE0/1 bitfield+1: ctrl=1, iso=2, bulk=3, int=4, dual=5. // Thus, we add +1 to the value below. switch endpoint { case rxEndpoint(endpoint): epType := ((config >> descEndptConfigAttrRxPos) & descEndptAttrSyncTypeMsk) >> descEndptAttrSyncTypePos d.bus.DEVICE_ENDPOINT[num].EPCFG.ReplaceBits( uint8(epType+1)<> descEndptConfigAttrTxPos) & descEndptAttrSyncTypeMsk) >> descEndptAttrSyncTypePos d.bus.DEVICE_ENDPOINT[num].EPCFG.ReplaceBits( uint8(epType+1)<