Files
tinygo/src/machine/usb/dcd-hid.go
T
2022-05-30 18:18:35 +09:00

376 lines
9.4 KiB
Go

//go:build usb.hid
// +build usb.hid
package usb
import "unsafe"
//go:inline
func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 {
switch endpointNumber(endpoint) {
case descHIDEndpointCtrl:
return descControlPacketSize
case descHIDEndpointKeyboard:
return descHIDKeyboardTxPacketSize
case descHIDEndpointMouse:
return descHIDMouseTxPacketSize
case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx
switch endpoint {
case rxEndpoint(endpoint):
return descHIDSerialRxPacketSize
case txEndpoint(endpoint):
return descHIDSerialTxPacketSize
}
case descHIDEndpointJoystick:
return descHIDJoystickTxPacketSize
case descHIDEndpointMediaKey:
return descHIDMediaKeyTxPacketSize
}
return descControlPacketSize
}
//go:inline
func (d *dcd) controlEndpoint() uint8 {
return descHIDEndpointCtrl
}
//go:inline
func (d *dcd) controlSetConfiguration() {
d.serialConfigure()
d.keyboardConfigure()
d.mouseConfigure()
d.joystickConfigure()
}
func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage {
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
if sup.wLength <= descHIDSxSize {
descHID[d.cc.config-1].cx[0] = 0xE9
d.controlReceive(
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
uint32(sup.wLength), true)
return dcdStageDataOut
}
// HID | SET IDLE (0x0A)
case descHIDRequestSetIdle:
idleRate := sup.wValue >> 8
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_ = idleRate
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled request
}
// --- INTERFACE Tx (IN) ---
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// HID | GET REPORT (0x01)
case descHIDRequestGetReport:
reportType := uint8(sup.wValue >> 8)
reportID := uint8(sup.wValue)
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_, _ = reportType, reportID
d.controlTransmit(
d.controlStatusBuffer([]uint8{0, 0}),
2, false)
return dcdStageDataIn
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
return dcdStageStall
}
func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool {
switch sup.bRequest {
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
d.controlGetDescriptor(sup)
return true
// GET HID REPORT (0x01):
case descHIDRequestGetReport:
d.controlGetDescriptor(sup)
return true
}
return false
}
func (d *dcd) controlGetDescriptor(sup dcdSetup) {
hid := &descHID[d.cc.config-1]
dxn := uint8(0)
pos := uint8(0)
// Determine the type of descriptor being requested
switch sup.wValue >> 8 {
// Device descriptor
case descTypeDevice:
dxn = descLengthDevice
_ = copy(hid.dx[:], hid.device[:dxn])
// Configuration descriptor
case descTypeConfigure:
dxn = uint8(descHIDConfigSize)
_ = copy(hid.dx[:], hid.config[:dxn])
// String descriptor
case descTypeString:
if 0 == len(hid.locale) {
break // No string descriptors defined!
}
var sd []uint8
if 0 == uint8(sup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This case (setup.wValue = [0x03]00)
// is a string request from the host to determine what that language is.
//
// In subsequent string requests, the host will populate setup.wIndex
// with the language code we return here in this string descriptor.
//
// This way all strings returned to the host are in the same language,
// whatever language that may be.
code := int(sup.wIndex)
if code >= len(hid.locale) {
code = 0
}
sd = hid.locale[code].descriptor[sup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we specified in a
// previous request (above: setup.wValue = [0x03]00). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range hid.locale {
if sup.wIndex == hid.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) {
// Found language with a string defined at the requested index.
//
// TODO: Add API methods to device controller that allows the user
// to provide these strings at/before driver initialization.
//
// For now, we just always use the descCommon* strings.
var s string
switch uint8(sup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct + " HID"
case 3:
s = descCommonSerialNumber
}
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:]
// String descriptor format is 2-byte header + 2-bytes per rune
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
sd[1] = descTypeString // header[1] = descriptor type
// Copy UTF-8 string into string descriptor as UTF-16
for n, c := range s {
if 2+2*n >= len(sd) {
break
}
sd[2+2*n] = uint8(c)
sd[3+2*n] = 0
}
break // end search for matching language code
}
}
}
}
// Copy string descriptor into descriptor transmit buffer
if nil != sd && len(sd) >= 0 {
dxn = sd[0]
_ = copy(hid.dx[:], sd[:dxn])
}
// Device qualification descriptor
case descTypeQualification:
dxn = descLengthQualification
_ = copy(hid.dx[:], hid.qualif[:dxn])
// Alternate configuration descriptor
case descTypeOtherSpeedConfiguration:
// TODO
// HID descriptor
case descTypeHID:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
pos = descHIDConfigKeyboardPos
case descHIDInterfaceMouse:
pos = descHIDConfigMousePos
case descHIDInterfaceSerial:
pos = descHIDConfigSerialPos
case descHIDInterfaceJoystick:
pos = descHIDConfigJoystickPos
case descHIDInterfaceMediaKey:
pos = descHIDConfigMediaKeyPos
default:
// Unhandled HID interface
}
if 0 != pos {
dxn = descLengthInterface
_ = copy(hid.dx[:], hid.config[pos:pos+dxn])
}
// HID report descriptor
case descTypeHIDReport:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
dxn = uint8(len(descHIDReportKeyboard))
_ = copy(hid.dx[:], descHIDReportKeyboard[:])
case descHIDInterfaceMouse:
dxn = uint8(len(descHIDReportMouse))
_ = copy(hid.dx[:], descHIDReportMouse[:])
case descHIDInterfaceSerial:
dxn = uint8(len(descHIDReportSerial))
_ = copy(hid.dx[:], descHIDReportSerial[:])
case descHIDInterfaceJoystick:
dxn = uint8(len(descHIDReportJoystick))
_ = copy(hid.dx[:], descHIDReportJoystick[:])
case descHIDInterfaceMediaKey:
dxn = uint8(len(descHIDReportMediaKey))
_ = copy(hid.dx[:], descHIDReportMediaKey[:])
default:
// Unhandled HID interface
}
default:
// Unhandled descriptor type
}
if dxn > 0 {
if dxn > uint8(sup.wLength) {
dxn = uint8(sup.wLength)
}
flushCache(
uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn))
d.controlTransmit(
uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false)
}
}
// controlComplete handles the setup completion of control endpoint 0.
func (d *dcd) controlComplete() {
// First, switch on the type of request (standard, class, or vendor)
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Switch on the recepient and direction of the request
switch d.setup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch d.setup.bRequest {
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
hid := &descHID[d.cc.config-1]
// Determine interface destination of the request
switch d.setup.wIndex {
// HID Keyboard Interface
case descHIDInterfaceKeyboard:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// Configuration descriptor
case descTypeConfigure:
if 1 == d.setup.wLength {
hid.keyboard.led = hid.cx[0]
d.controlTransmit(uintptr(0), 0, false)
}
default:
// Unhandled descriptor type
}
// HID Serial Interface
case descHIDInterfaceSerial:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// String descriptor
case descTypeString:
if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) {
d.enableSOF(true, descHIDInterfaceCount)
}
default:
// Unhandled descriptor type
}
default:
// Unhandled device interface
}
default:
// Unhandled request
}
default:
// Unhandled recepient or direction
}
default:
// Unhandled request type
}
}