Skip to content

Commit c2b67a0

Browse files
committed
pbio/drv/usb/usb_ev3.c: Add WebUSB and Microsoft descriptors
This allows for more friendly device setup when connecting to a computer.
1 parent 1b9934b commit c2b67a0

File tree

1 file changed

+231
-89
lines changed

1 file changed

+231
-89
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 231 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ enum {
5151
STRING_DESC_SERIAL,
5252
};
5353

54+
/**
55+
* Indices for vendor requests, which are used for platform descriptors
56+
*/
57+
enum {
58+
VENDOR_REQ_WEBUSB,
59+
VENDOR_REQ_MS_20,
60+
};
61+
62+
#define WEBUSB_LANDING_PAGE_URL_IDX 1
63+
5464
// Begin hardcoded USB descriptors
5565

5666
static const pbdrv_usb_dev_desc_union_t dev_desc = {
@@ -179,6 +189,112 @@ static const pbdrv_usb_ev3_conf_1_union_t configuration_1_desc_fs = {
179189
}
180190
};
181191

192+
#if PBIO_VERSION_LEVEL_HEX == 0xA
193+
#define PYBRICKS_CODE_URL "alpha.code.pybricks.com"
194+
#elif PBIO_VERSION_LEVEL_HEX == 0xB
195+
#define PYBRICKS_CODE_URL "beta.code.pybricks.com"
196+
#else
197+
#define PYBRICKS_CODE_URL "code.pybricks.com"
198+
#endif
199+
static const pbdrv_usb_webusb_url_desc_union_t webusb_landing_page = {
200+
.s = {
201+
.bLength = sizeof(pbdrv_usb_webusb_url_desc_t) + sizeof(PYBRICKS_CODE_URL) - 1,
202+
.bDescriptorType = WEBUSB_DESC_TYPE_URL,
203+
.bScheme = WEBUSB_URL_SCHEME_HTTPS,
204+
.url = PYBRICKS_CODE_URL,
205+
},
206+
};
207+
208+
#define MS_20_REGISTRY_DATA_EXTRA_SZ \
209+
2 + 2 * sizeof("DeviceInterfaceGUIDs") + \
210+
2 + 2 * sizeof("{A5C44A4C-53D4-4389-9821-AE95051908A1}") + \
211+
2
212+
213+
typedef struct PBDRV_PACKED {
214+
pbdrv_usb_ms_20_desc_set_header_t desc_set_hdr;
215+
pbdrv_usb_ms_20_compatible_t compatible;
216+
pbdrv_usb_ms_20_reg_prop_hdr_t reg_prop_hdr;
217+
uint16_t device_interface_guids[sizeof("DeviceInterfaceGUIDs") + 1];
218+
uint16_t device_interface_guid_val[sizeof("{A5C44A4C-53D4-4389-9821-AE95051908A1}") + 1];
219+
uint16_t _multi_sz_end;
220+
} pbdrv_usb_ev3_ms_20_desc_set_t;
221+
PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_ev3_ms_20_desc_set);
222+
223+
static const pbdrv_usb_ev3_ms_20_desc_set_union_t ms_20_desc_set = {
224+
.s = {
225+
.desc_set_hdr = {
226+
.wLength = sizeof(pbdrv_usb_ms_20_desc_set_header_t),
227+
.wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR,
228+
.dwWindowsVersion = MS_WINDOWS_VERSION_81,
229+
.wTotalLength = sizeof(pbdrv_usb_ev3_ms_20_desc_set_t),
230+
},
231+
.compatible = {
232+
.wLength = sizeof(pbdrv_usb_ms_20_compatible_t),
233+
.wDescriptorType = MS_OS_20_FEATURE_COMPATBLE_ID,
234+
.CompatibleID = "WINUSB",
235+
.SubCompatibleID = "",
236+
},
237+
.reg_prop_hdr = {
238+
.wLength = sizeof(pbdrv_usb_ms_20_reg_prop_hdr_t) + MS_20_REGISTRY_DATA_EXTRA_SZ,
239+
.wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY,
240+
.wPropertyDataType = MS_OS_20_REG_PROP_TYPE_REG_MULTI_SZ,
241+
},
242+
.device_interface_guids = {
243+
2 * sizeof("DeviceInterfaceGUIDs"), /* Length */
244+
'D', 'e', 'v', 'i', 'c', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 'G', 'U', 'I', 'D', 's',
245+
},
246+
.device_interface_guid_val = {
247+
2 * sizeof("{A5C44A4C-53D4-4389-9821-AE95051908A1}"), /* Length */
248+
'{', 'A', '5', 'C', '4', '4', 'A', '4', 'C', '-', '5', '3', 'D', '4', '-', '4', '3', '8', '9',
249+
'-', '9', '8', '2', '1', '-', 'A', 'E', '9', '5', '0', '5', '1', '9', '0', '8', 'A', '1', '}',
250+
}
251+
}
252+
};
253+
254+
typedef struct PBDRV_PACKED {
255+
pbdrv_usb_bos_desc_t bos;
256+
// WebUSB must occur before Microsoft OS descriptors
257+
pbdrv_usb_webusb_capability_t webusb;
258+
pbdrv_usb_microsoft_20_capability_t ms_20;
259+
} pbdrv_usb_ev3_bos_desc_set_t;
260+
PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_ev3_bos_desc_set);
261+
262+
static const pbdrv_usb_ev3_bos_desc_set_union_t bos_desc_set = {
263+
.s = {
264+
.bos = {
265+
.bLength = sizeof(pbdrv_usb_bos_desc_t),
266+
.bDescriptorType = DESC_TYPE_BOS,
267+
.wTotalLength = sizeof(pbdrv_usb_ev3_bos_desc_set_t),
268+
.bNumDeviceCaps = 2,
269+
},
270+
.webusb = {
271+
.hdr = {
272+
.bLength = sizeof(pbdrv_usb_webusb_capability_t),
273+
.bDescriptorType = DESC_TYPE_DEVICE_CAPABILITY,
274+
.bDevCapabilityType = USB_DEVICE_CAPABILITY_TYPE_PLATFORM,
275+
.bReserved = 0,
276+
.uuid = USB_PLATFORM_CAP_GUID_WEBUSB,
277+
},
278+
.bcdVersion = 0x0100,
279+
.bVendorCode = VENDOR_REQ_WEBUSB,
280+
.iLandingPage = WEBUSB_LANDING_PAGE_URL_IDX,
281+
},
282+
.ms_20 = {
283+
.hdr = {
284+
.bLength = sizeof(pbdrv_usb_microsoft_20_capability_t),
285+
.bDescriptorType = DESC_TYPE_DEVICE_CAPABILITY,
286+
.bDevCapabilityType = USB_DEVICE_CAPABILITY_TYPE_PLATFORM,
287+
.bReserved = 0,
288+
.uuid = USB_PLATFORM_CAP_GUID_MS_20,
289+
},
290+
.dwWindowsVersion = MS_WINDOWS_VERSION_81,
291+
.wMSOSDescriptorSetTotalLength = sizeof(pbdrv_usb_ev3_ms_20_desc_set_t),
292+
.bMS_VendorCode = VENDOR_REQ_MS_20,
293+
.bAltEnumCode = 0,
294+
}
295+
}
296+
};
297+
182298
typedef struct PBDRV_PACKED {
183299
uint8_t bLength;
184300
uint8_t bDescriptorType;
@@ -502,6 +618,11 @@ static bool usb_get_descriptor(uint16_t wValue) {
502618
return true;
503619
}
504620
break;
621+
622+
case DESC_TYPE_BOS:
623+
pbdrv_usb_setup_data_to_send = bos_desc_set.u;
624+
pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_ev3_bos_desc_set_t);
625+
return true;
505626
}
506627

507628
return false;
@@ -563,121 +684,142 @@ static void usb_device_intr(void) {
563684
setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0);
564685
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC;
565686

566-
if ((setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) == BM_REQ_TYPE_STANDARD) {
567-
switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) {
568-
case BM_REQ_RECIP_DEV:
569-
switch (setup_pkt.s.bRequest) {
570-
case SET_ADDRESS:
571-
pbdrv_usb_addr = setup_pkt.s.wValue;
572-
pbdrv_usb_addr_needs_setting = true;
573-
handled = true;
574-
break;
687+
switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) {
688+
case BM_REQ_TYPE_STANDARD:
689+
switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) {
690+
case BM_REQ_RECIP_DEV:
691+
switch (setup_pkt.s.bRequest) {
692+
case SET_ADDRESS:
693+
pbdrv_usb_addr = setup_pkt.s.wValue;
694+
pbdrv_usb_addr_needs_setting = true;
695+
handled = true;
696+
break;
575697

576-
case SET_CONFIGURATION:
577-
if (setup_pkt.s.wValue <= 1) {
578-
pbdrv_usb_config = setup_pkt.s.wValue;
698+
case SET_CONFIGURATION:
699+
if (setup_pkt.s.wValue <= 1) {
700+
pbdrv_usb_config = setup_pkt.s.wValue;
579701

580-
if (pbdrv_usb_config == 1) {
581-
// configuring
702+
if (pbdrv_usb_config == 1) {
703+
// configuring
582704

583-
// Reset data toggle, clear stall, flush fifo
584-
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
585-
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
586-
} else {
587-
// deconfiguring
705+
// Reset data toggle, clear stall, flush fifo
706+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
707+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
708+
} else {
709+
// deconfiguring
588710

589-
// Set stall condition
590-
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL;
591-
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL;
711+
// Set stall condition
712+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL;
713+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL;
714+
}
715+
handled = true;
592716
}
593-
handled = true;
594-
}
595-
break;
596-
597-
case GET_CONFIGURATION:
598-
pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config;
599-
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
600-
pbdrv_usb_setup_data_to_send_sz = 1;
601-
handled = true;
602-
break;
603-
604-
case GET_STATUS:
605-
pbdrv_usb_setup_misc_tx_byte = 1; // self-powered
606-
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
607-
pbdrv_usb_setup_data_to_send_sz = 2;
608-
handled = true;
609-
break;
610-
611-
case GET_DESCRIPTOR:
612-
if (usb_get_descriptor(setup_pkt.s.wValue)) {
613-
handled = true;
614-
}
615-
break;
616-
}
617-
break;
717+
break;
618718

619-
case BM_REQ_RECIP_IF:
620-
if (setup_pkt.s.wIndex == 0) {
621-
switch (setup_pkt.s.bRequest) {
622-
case GET_INTERFACE:
623-
pbdrv_usb_setup_misc_tx_byte = 0;
719+
case GET_CONFIGURATION:
720+
pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config;
624721
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
625722
pbdrv_usb_setup_data_to_send_sz = 1;
626723
handled = true;
627724
break;
628725

629726
case GET_STATUS:
630-
pbdrv_usb_setup_misc_tx_byte = 0;
727+
pbdrv_usb_setup_misc_tx_byte = 1; // self-powered
631728
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
632729
pbdrv_usb_setup_data_to_send_sz = 2;
633730
handled = true;
634731
break;
635-
}
636-
}
637-
break;
638-
639-
case BM_REQ_RECIP_EP:
640-
switch (setup_pkt.s.bRequest) {
641-
case GET_STATUS:
642-
if (setup_pkt.s.wIndex == 1) {
643-
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL);
644-
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
645-
pbdrv_usb_setup_data_to_send_sz = 2;
646-
handled = true;
647-
} else if (setup_pkt.s.wIndex == 0x81) {
648-
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL);
649-
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
650-
pbdrv_usb_setup_data_to_send_sz = 2;
651-
handled = true;
652-
}
653-
break;
654732

655-
case CLEAR_FEATURE:
656-
if (setup_pkt.s.wValue == 0) {
657-
if (setup_pkt.s.wIndex == 1) {
658-
HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL;
659-
handled = true;
660-
} else if (setup_pkt.s.wIndex == 0x81) {
661-
HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL;
733+
case GET_DESCRIPTOR:
734+
if (usb_get_descriptor(setup_pkt.s.wValue)) {
662735
handled = true;
663736
}
737+
break;
738+
}
739+
break;
740+
741+
case BM_REQ_RECIP_IF:
742+
if (setup_pkt.s.wIndex == 0) {
743+
switch (setup_pkt.s.bRequest) {
744+
case GET_INTERFACE:
745+
pbdrv_usb_setup_misc_tx_byte = 0;
746+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
747+
pbdrv_usb_setup_data_to_send_sz = 1;
748+
handled = true;
749+
break;
750+
751+
case GET_STATUS:
752+
pbdrv_usb_setup_misc_tx_byte = 0;
753+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
754+
pbdrv_usb_setup_data_to_send_sz = 2;
755+
handled = true;
756+
break;
664757
}
665-
break;
758+
}
759+
break;
666760

667-
case SET_FEATURE:
668-
if (setup_pkt.s.wValue == 0) {
761+
case BM_REQ_RECIP_EP:
762+
switch (setup_pkt.s.bRequest) {
763+
case GET_STATUS:
669764
if (setup_pkt.s.wIndex == 1) {
670-
HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL;
765+
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL);
766+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
767+
pbdrv_usb_setup_data_to_send_sz = 2;
671768
handled = true;
672769
} else if (setup_pkt.s.wIndex == 0x81) {
673-
HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL;
770+
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL);
771+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
772+
pbdrv_usb_setup_data_to_send_sz = 2;
674773
handled = true;
675774
}
676-
}
677-
break;
678-
}
679-
break;
680-
}
775+
break;
776+
777+
case CLEAR_FEATURE:
778+
if (setup_pkt.s.wValue == 0) {
779+
if (setup_pkt.s.wIndex == 1) {
780+
HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL;
781+
handled = true;
782+
} else if (setup_pkt.s.wIndex == 0x81) {
783+
HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL;
784+
handled = true;
785+
}
786+
}
787+
break;
788+
789+
case SET_FEATURE:
790+
if (setup_pkt.s.wValue == 0) {
791+
if (setup_pkt.s.wIndex == 1) {
792+
HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL;
793+
handled = true;
794+
} else if (setup_pkt.s.wIndex == 0x81) {
795+
HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL;
796+
handled = true;
797+
}
798+
}
799+
break;
800+
}
801+
break;
802+
}
803+
break;
804+
805+
case BM_REQ_TYPE_VENDOR:
806+
switch (setup_pkt.s.bRequest) {
807+
case VENDOR_REQ_WEBUSB:
808+
if (setup_pkt.s.wIndex == WEBUSB_REQ_GET_URL && setup_pkt.s.wValue == WEBUSB_LANDING_PAGE_URL_IDX) {
809+
pbdrv_usb_setup_data_to_send = webusb_landing_page.u;
810+
pbdrv_usb_setup_data_to_send_sz = webusb_landing_page.s.bLength;
811+
handled = true;
812+
}
813+
break;
814+
815+
case VENDOR_REQ_MS_20:
816+
if (setup_pkt.s.wIndex == MS_OS_20_DESCRIPTOR_INDEX) {
817+
pbdrv_usb_setup_data_to_send = ms_20_desc_set.u;
818+
pbdrv_usb_setup_data_to_send_sz = ms_20_desc_set.s.desc_set_hdr.wTotalLength;
819+
handled = true;
820+
}
821+
break;
822+
}
681823
}
682824

683825
if (!handled) {

0 commit comments

Comments
 (0)