Compare commits

..

3 Commits

Author SHA1 Message Date
Nia Waldvogel 80c89809a9 machine: fix usb truncation?
Remove the sendUSBPacket maxLen param because this greatly confused the compiler.
It also fixes a bug where the length provided to the hardware may not match the length of the packet.
sendUSBPacket now panics if the sent packet is too big.

I also fixed some of the string descriptor logic where we could create a packet without fully populating it.

RP2* systems might require some more work since they are implemented very differently?
I don't have any of those to test with yet, so maybe someone can deal with them in a seperate PR?
2025-12-30 15:51:27 -05:00
Nia Waldvogel 266d70816f compiler/runtime: add element layout to sliceAppend
Attach the memory layout of the element type to newly created slices via append when using the precise collector.
2025-12-26 21:27:24 -05:00
Damian Gryski 27cedf09e8 testdata: more corpus repos 2025-12-22 13:15:06 +00:00
13 changed files with 152 additions and 117 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) {
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 3668, 280, 0, 2244}, {"hifive1b", "examples/echo", 3668, 280, 0, 2244},
{"microbit", "examples/serial", 2694, 342, 8, 2248}, {"microbit", "examples/serial", 2694, 342, 8, 2248},
{"wioterminal", "examples/pininterrupt", 7187, 1489, 116, 6888}, {"wioterminal", "examples/pininterrupt", 6833, 1491, 120, 6888},
// TODO: also check wasm. Right now this is difficult, because // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
+2 -1
View File
@@ -1599,7 +1599,8 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new") elemLayout := b.createObjectLayout(elemType, pos)
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize, elemLayout}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr") newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
newLen := b.CreateExtractValue(result, 1, "append.newLen") newLen := b.CreateExtractValue(result, 1, "append.newLen")
newCap := b.CreateExtractValue(result, 2, "append.newCap") newCap := b.CreateExtractValue(result, 2, "append.newCap")
+3 -3
View File
@@ -55,7 +55,7 @@ entry:
store i32 2, ptr %0, align 4 store i32 2, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8 %1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8
store i32 3, ptr %1, align 4 store i32 3, ptr %1, align 4
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #3 %append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 %append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
@@ -66,13 +66,13 @@ entry:
ret { ptr, i32, i32 } %4 ret { ptr, i32, i32 } %4
} }
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #1 declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 { define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #3 %append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 %append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
+10 -5
View File
@@ -1745,7 +1745,7 @@ func memzero(ptr unsafe.Pointer, size uintptr)
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
//go:linkname sliceAppend runtime.sliceAppend //go:linkname sliceAppend runtime.sliceAppend
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr)
//go:linkname sliceCopy runtime.sliceCopy //go:linkname sliceCopy runtime.sliceCopy
func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int
@@ -1810,7 +1810,7 @@ func buflen(v Value) (unsafe.Pointer, uintptr) {
} }
//go:linkname sliceGrow runtime.sliceGrow //go:linkname sliceGrow runtime.sliceGrow
func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr)
// extend slice to hold n new elements // extend slice to hold n new elements
func extendSlice(v Value, n int) sliceHeader { func extendSlice(v Value, n int) sliceHeader {
@@ -1823,7 +1823,10 @@ func extendSlice(v Value, n int) sliceHeader {
old = *(*sliceHeader)(v.value) old = *(*sliceHeader)(v.value)
} }
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size()) elem := v.typecode.elem()
elemSize := elem.Size()
elemLayout := elem.gcLayout()
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), elemSize, elemLayout)
return sliceHeader{ return sliceHeader{
data: nbuf, data: nbuf,
@@ -1862,8 +1865,10 @@ func AppendSlice(s, t Value) Value {
} }
sSlice := (*sliceHeader)(s.value) sSlice := (*sliceHeader)(s.value)
tSlice := (*sliceHeader)(t.value) tSlice := (*sliceHeader)(t.value)
elemSize := s.typecode.elem().Size() elem := s.typecode.elem()
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize) elemSize := elem.Size()
elemLayout := elem.gcLayout()
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize, elemLayout)
result := &sliceHeader{ result := &sliceHeader{
data: ptr, data: ptr,
len: len, len: len,
+17 -16
View File
@@ -337,7 +337,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0) sendUSBPacket(ep, data)
// clear transfer complete flag // clear transfer complete flag
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1) setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
@@ -351,27 +351,28 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { func sendUSBPacket(ep uint32, data []byte) {
l := uint16(len(data)) // Select the corresponding buffer.
if 0 < maxsize && maxsize < l { buffer := udd_ep_control_cache_buffer[:]
l = maxsize if ep != 0 {
buffer = udd_ep_in_cache_buffer[ep][:]
} }
// Set endpoint address for sending data // Copy the packet to the buffer.
if ep == 0 { copy(buffer[:len(data)], data)
copy(udd_ep_control_cache_buffer[:], data[:l])
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer)))) // Select the corresponding endpoint descriptor.
} else { endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) // Set the endpoint address.
} endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
// clear multi-packet size which is total bytes already sent // clear multi-packet size which is total bytes already sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count, which is total number of bytes to be sent // set byte count, which is total number of bytes to be sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
} }
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+17 -16
View File
@@ -340,7 +340,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0) sendUSBPacket(ep, data)
// clear transfer complete flag // clear transfer complete flag
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
@@ -354,27 +354,28 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { func sendUSBPacket(ep uint32, data []byte) {
l := uint16(len(data)) // Select the corresponding buffer.
if 0 < maxsize && maxsize < l { buffer := udd_ep_control_cache_buffer[:]
l = maxsize if ep != 0 {
buffer = udd_ep_in_cache_buffer[ep][:]
} }
// Set endpoint address for sending data // Copy the packet to the buffer.
if ep == 0 { copy(buffer[:len(data)], data)
copy(udd_ep_control_cache_buffer[:], data[:l])
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer)))) // Select the corresponding endpoint descriptor.
} else { endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) // Set the endpoint address.
} endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
// clear multi-packet size which is total bytes already sent // clear multi-packet size which is total bytes already sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count, which is total number of bytes to be sent // set byte count, which is total number of bytes to be sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
} }
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+18 -19
View File
@@ -256,7 +256,7 @@ func initEndpoint(ep, config uint32) {
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in). // SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0) sendUSBPacket(ep, data)
// clear transfer complete flag // clear transfer complete flag
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4) nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
@@ -267,33 +267,32 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { func sendUSBPacket(ep uint32, data []byte) {
// Select the corresponding buffer.
count := len(data) count := len(data)
if 0 < int(maxsize) && int(maxsize) < count { var buffer []byte
count = int(maxsize)
}
if ep == 0 { if ep == 0 {
copy(udd_ep_control_cache_buffer[:], data[:count]) buffer = udd_ep_control_cache_buffer[:]
if count > usb.EndpointPacketSize { if count > usb.EndpointPacketSize {
// The packet must be sent in chunks.
sendOnEP0DATADONE.offset = usb.EndpointPacketSize sendOnEP0DATADONE.offset = usb.EndpointPacketSize
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset] sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[usb.EndpointPacketSize]
sendOnEP0DATADONE.count = count - usb.EndpointPacketSize sendOnEP0DATADONE.count = count - usb.EndpointPacketSize
count = usb.EndpointPacketSize count = usb.EndpointPacketSize
} }
sendViaEPIn(
ep,
&udd_ep_control_cache_buffer[0],
count,
)
} else { } else {
copy(udd_ep_in_cache_buffer[ep][:], data[:count]) buffer = udd_ep_in_cache_buffer[ep][:]
sendViaEPIn(
ep,
&udd_ep_in_cache_buffer[ep][0],
count,
)
} }
// Copy the packet to the buffer.
copy(buffer[:len(data)], data)
// Send the first chunk of the packet.
sendViaEPIn(
ep,
&buffer[0],
count,
)
} }
func handleEndpointRx(ep uint32) []byte { func handleEndpointRx(ep uint32) []byte {
+1 -1
View File
@@ -128,7 +128,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
const ackTimeout = 570 const ackTimeout = 570
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
sendUSBPacket(0, []byte{}, 0) sendUSBPacket(0, []byte{})
// Wait for transfer to complete with a timeout. // Wait for transfer to complete with a timeout.
t := timer.timeElapsed() t := timer.timeElapsed()
+1 -1
View File
@@ -131,7 +131,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
const ackTimeout = 570 const ackTimeout = 570
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC) rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC)
sendUSBPacket(0, []byte{}, 0) sendUSBPacket(0, []byte{})
// Wait for transfer to complete with a timeout. // Wait for transfer to complete with a timeout.
t := timer.timeElapsed() t := timer.timeElapsed()
+3 -7
View File
@@ -72,19 +72,15 @@ func initEndpoint(ep, config uint32) {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data, 0) sendUSBPacket(ep, data)
return true return true
} }
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { func sendUSBPacket(ep uint32, data []byte) {
count := len(data) count := len(data)
if 0 < int(maxsize) && int(maxsize) < count {
count = int(maxsize)
}
if ep == 0 { if ep == 0 {
if count > usb.EndpointPacketSize { if count > usb.EndpointPacketSize {
count = usb.EndpointPacketSize count = usb.EndpointPacketSize
@@ -145,7 +141,7 @@ func setEPDataPID(ep uint32, dataOne bool) {
} }
func SendZlp() { func SendZlp() {
sendUSBPacket(0, []byte{}, 0) sendUSBPacket(0, []byte{})
} }
func sendViaEPIn(ep uint32, data []byte, count int) { func sendViaEPIn(ep uint32, data []byte, count int) {
+72 -37
View File
@@ -81,21 +81,6 @@ func usbSerial() string {
return "" return ""
} }
// strToUTF16LEDescriptor converts a utf8 string into a string descriptor
// note: the following code only converts ascii characters to UTF16LE. In order
// to do a "proper" conversion, we would need to pull in the 'unicode/utf16'
// package, which at the time this was written added 512 bytes to the compiled
// binary.
func strToUTF16LEDescriptor(in string, out []byte) {
out[0] = byte(len(out))
out[1] = descriptor.TypeString
for i, rune := range in {
out[(i<<1)+2] = byte(rune)
out[(i<<1)+3] = 0
}
return
}
const cdcLineInfoSize = 7 const cdcLineInfoSize = 7
var ( var (
@@ -137,51 +122,53 @@ var (
usbStallHandler [NumberOfUSBEndpoints]func(usb.Setup) bool usbStallHandler [NumberOfUSBEndpoints]func(usb.Setup) bool
) )
var usbLangInfo = [4]byte{
// length = 4 bytes
0x04,
// descriptor type = string
0x03,
// language codes
// 0x0409 = English (United States)
0x09, 0x04,
}
// sendDescriptor creates and sends the various USB descriptor types that // sendDescriptor creates and sends the various USB descriptor types that
// can be requested by the host. // can be requested by the host.
func sendDescriptor(setup usb.Setup) { func sendDescriptor(setup usb.Setup) {
switch setup.WValueH { switch setup.WValueH {
case descriptor.TypeConfiguration: case descriptor.TypeConfiguration:
sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength) sendDescriptorData(usbDescriptor.Configuration, setup.WLength)
return return
case descriptor.TypeDevice: case descriptor.TypeDevice:
usbDescriptor.Configure(usbVendorID(), usbProductID()) usbDescriptor.Configure(usbVendorID(), usbProductID())
sendUSBPacket(0, usbDescriptor.Device, setup.WLength) sendDescriptorData(usbDescriptor.Device, setup.WLength)
return return
case descriptor.TypeString: case descriptor.TypeString:
switch setup.WValueL { switch setup.WValueL {
case 0: case 0:
usb_trans_buffer[0] = 0x04 sendDescriptorData(usbLangInfo[:], setup.WLength)
usb_trans_buffer[1] = 0x03
usb_trans_buffer[2] = 0x09
usb_trans_buffer[3] = 0x04
sendUSBPacket(0, usb_trans_buffer[:4], setup.WLength)
case usb.IPRODUCT: case usb.IPRODUCT:
b := usb_trans_buffer[:(len(usbProduct())<<1)+2] sendDescriptorString(usbProduct(), setup.WLength)
strToUTF16LEDescriptor(usbProduct(), b)
sendUSBPacket(0, b, setup.WLength)
case usb.IMANUFACTURER: case usb.IMANUFACTURER:
b := usb_trans_buffer[:(len(usbManufacturer())<<1)+2] sendDescriptorString(usbManufacturer(), setup.WLength)
strToUTF16LEDescriptor(usbManufacturer(), b)
sendUSBPacket(0, b, setup.WLength)
case usb.ISERIAL: case usb.ISERIAL:
sz := len(usbSerial()) serial := usbSerial()
if sz == 0 { if len(serial) == 0 {
SendZlp() SendZlp()
} else { } else {
b := usb_trans_buffer[:(sz<<1)+2] sendDescriptorString(serial, setup.WLength)
strToUTF16LEDescriptor(usbSerial(), b)
sendUSBPacket(0, b, setup.WLength)
} }
} }
// TODO: why do we do this when WValueL is unknown?
return return
case descriptor.TypeHIDReport: case descriptor.TypeHIDReport:
if h, ok := usbDescriptor.HID[setup.WIndex]; ok { if h, ok := usbDescriptor.HID[setup.WIndex]; ok {
sendUSBPacket(0, h, setup.WLength) sendDescriptorData(h, setup.WLength)
return return
} }
case descriptor.TypeDeviceQualifier: case descriptor.TypeDeviceQualifier:
@@ -194,6 +181,39 @@ func sendDescriptor(setup usb.Setup) {
return return
} }
// sendDescriptorString sends a string descriptor, truncating it to fit maxLen or the buffer size.
// note: the following code only converts ascii characters to UTF16LE. In order
// to do a "proper" conversion, we would need to pull in the 'unicode/utf16'
// package, which at the time this was written added 512 bytes to the compiled
// binary.
// TODO: old comment, re-evaluate
func sendDescriptorString(data string, maxLen uint16) {
if maxLen < 2 {
// Something has gone horribly wrong.
SendZlp()
return
}
// Clamp the length.
maxEncBytes := min(len(usb_trans_buffer), len(udd_ep_control_cache_buffer), int(maxLen))
data = data[:min(len(data), (maxEncBytes-2)/2)]
// Write the header.
buf := usb_trans_buffer[:2*len(data)+2]
hdr, body := buf[:2], buf[2:]
hdr[0] = byte(len(buf))
hdr[1] = descriptor.TypeString
// Convert the string to UTF16.
// NOTE: Using range here would cause the length to disagree when multibyte codepoints are present.
for i := 0; i < len(data); i++ {
body[2*i] = byte(data[i])
body[2*i+1] = 0
}
sendUSBPacket(0, buf)
}
func handleStandardSetup(setup usb.Setup) bool { func handleStandardSetup(setup usb.Setup) bool {
switch setup.BRequest { switch setup.BRequest {
case usb.GET_STATUS: case usb.GET_STATUS:
@@ -206,7 +226,7 @@ func handleStandardSetup(setup usb.Setup) bool {
} }
} }
sendUSBPacket(0, usb_trans_buffer[:2], setup.WLength) sendDescriptorData(usb_trans_buffer[:2], setup.WLength)
return true return true
case usb.CLEAR_FEATURE: case usb.CLEAR_FEATURE:
@@ -251,7 +271,7 @@ func handleStandardSetup(setup usb.Setup) bool {
case usb.GET_CONFIGURATION: case usb.GET_CONFIGURATION:
usb_trans_buffer[0] = usbConfiguration usb_trans_buffer[0] = usbConfiguration
sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength) sendDescriptorData(usb_trans_buffer[:1], setup.WLength)
return true return true
case usb.SET_CONFIGURATION: case usb.SET_CONFIGURATION:
@@ -271,7 +291,7 @@ func handleStandardSetup(setup usb.Setup) bool {
case usb.GET_INTERFACE: case usb.GET_INTERFACE:
usb_trans_buffer[0] = usbSetInterface usb_trans_buffer[0] = usbSetInterface
sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength) sendDescriptorData(usb_trans_buffer[:1], setup.WLength)
return true return true
case usb.SET_INTERFACE: case usb.SET_INTERFACE:
@@ -285,6 +305,21 @@ func handleStandardSetup(setup usb.Setup) bool {
} }
} }
// sendDescriptorData sends a descriptor, truncating it to fit maxLen or the buffer size.
func sendDescriptorData(data []byte, maxLen uint16) {
data = lenToCap(data)
data = data[:min(len(data), len(udd_ep_control_cache_buffer), int(maxLen))]
sendUSBPacket(0, data)
}
// Set the cap of the slice to the length.
// This is safe, but cannot be proven by the compiler.
//
//go:nobounds
func lenToCap(b []byte) []byte {
return b[:len(b):len(b)]
}
func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) { func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
if len(usbDescriptor.Device) == 0 { if len(usbDescriptor.Device) == 0 {
usbDescriptor = descriptor.CDC usbDescriptor = descriptor.CDC
+3 -10
View File
@@ -3,19 +3,18 @@ package runtime
// This file implements compiler builtins for slices: append() and copy(). // This file implements compiler builtins for slices: append() and copy().
import ( import (
"internal/gclayout"
"math/bits" "math/bits"
"unsafe" "unsafe"
) )
// Builtin append(src, elements...) function: append elements to src and return // Builtin append(src, elements...) function: append elements to src and return
// the modified (possibly expanded) slice. // the modified (possibly expanded) slice.
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) { func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) {
newLen := srcLen + elemsLen newLen := srcLen + elemsLen
if elemsLen > 0 { if elemsLen > 0 {
// Allocate a new slice with capacity for elemsLen more elements, if necessary; // Allocate a new slice with capacity for elemsLen more elements, if necessary;
// otherwise, reuse the passed slice. // otherwise, reuse the passed slice.
srcBuf, _, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize) srcBuf, _, srcCap = sliceGrow(srcBuf, srcLen, srcCap, newLen, elemSize, layout)
// Append the new elements in-place. // Append the new elements in-place.
memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize) memmove(unsafe.Add(srcBuf, srcLen*elemSize), elemsBuf, elemsLen*elemSize)
@@ -36,7 +35,7 @@ func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr
} }
// sliceGrow returns a new slice with space for at least newCap elements // sliceGrow returns a new slice with space for at least newCap elements
func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr) { func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) {
if oldCap >= newCap { if oldCap >= newCap {
// No need to grow, return the input slice. // No need to grow, return the input slice.
return oldBuf, oldLen, oldCap return oldBuf, oldLen, oldCap
@@ -48,12 +47,6 @@ func sliceGrow(oldBuf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr)
// memory allocators, this causes some difficult to debug issues. // memory allocators, this causes some difficult to debug issues.
newCap = 1 << bits.Len(uint(newCap)) newCap = 1 << bits.Len(uint(newCap))
var layout unsafe.Pointer
// less type info here; can only go off element size
if elemSize < unsafe.Sizeof(uintptr(0)) {
layout = gclayout.NoPtrs.AsPtr()
}
buf := alloc(newCap*elemSize, layout) buf := alloc(newCap*elemSize, layout)
if oldLen > 0 { if oldLen > 0 {
// copy any data to new slice // copy any data to new slice
+4
View File
@@ -190,6 +190,7 @@
- pkg: tiff - pkg: tiff
- repo: github.com/golang/geo - repo: github.com/golang/geo
subdirs: subdirs:
- pkg: earth
- pkg: r1 - pkg: r1
- pkg: r2 - pkg: r2
- pkg: r3 - pkg: r3
@@ -285,3 +286,6 @@
- repo: github.com/julienschmidt/httprouter - repo: github.com/julienschmidt/httprouter
- repo: github.com/openhistogram/circonusllhist - repo: github.com/openhistogram/circonusllhist
skipwasi: true # math.MaxInt64 (untyped int constant 9223372036854775807) overflows int skipwasi: true # math.MaxInt64 (untyped int constant 9223372036854775807) overflows int
- repo: github.com/philhofer/fwd
- repo: github.com/blevesearch/sear
- repo: github.com/steveyen/gtreap