@@ -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
5666static 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+
182298typedef 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