From e9d9fcde04411001ba8dff07b512fdc46ce13e47 Mon Sep 17 00:00:00 2001
From: Dean Camera <dean@fourwalledcubicle.com>
Date: Sun, 22 Apr 2018 16:08:12 +1000
Subject: [PATCH] Add MS OS Compatibility descriptors to RNDIS demos for
 driverless install on Windows.

---
 .../ClassDriver/RNDISEthernet/Descriptors.c   | 51 ++++++++++++++++++-
 .../ClassDriver/RNDISEthernet/Descriptors.h   | 22 ++++++++
 .../ClassDriver/RNDISEthernet/RNDISEthernet.c |  3 ++
 .../LowLevel/RNDISEthernet/Descriptors.c      | 51 ++++++++++++++++++-
 .../LowLevel/RNDISEthernet/Descriptors.h      | 22 ++++++++
 .../LowLevel/RNDISEthernet/RNDISEthernet.c    |  3 ++
 LUFA/DoxygenPages/ChangeLog.txt               |  2 +
 Projects/Webserver/Descriptors.c              | 51 ++++++++++++++++++-
 Projects/Webserver/Descriptors.h              | 22 ++++++++
 Projects/Webserver/USBDeviceMode.c            |  3 ++
 10 files changed, 224 insertions(+), 6 deletions(-)

diff --git a/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.c b/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.c
index 494150349..8e709cee9 100644
--- a/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.c
+++ b/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.c
@@ -46,7 +46,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 {
 	.Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
 
-	.USBSpecification       = VERSION_BCD(1,1,0),
+	.USBSpecification       = VERSION_BCD(2,0,0),
 	.Class                  = CDC_CSCP_CDCClass,
 	.SubClass               = CDC_CSCP_NoSpecificSubclass,
 	.Protocol               = CDC_CSCP_NoSpecificProtocol,
@@ -55,7 +55,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 
 	.VendorID               = 0x03EB,
 	.ProductID              = 0x204C,
-	.ReleaseNumber          = VERSION_BCD(0,0,1),
+	.ReleaseNumber          = VERSION_BCD(0,0,2),
 
 	.ManufacturerStrIndex   = STRING_ID_Manufacturer,
 	.ProductStrIndex        = STRING_ID_Product,
@@ -192,6 +192,32 @@ const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR
  */
 const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"LUFA RNDIS CDC Demo");
 
+/** Microsoft OS Compatibility string descriptor. This is a special string descriptor that Microsoft based OS hosts
+ *  will query at string descriptor ID 0xEE on initial enumeration, to test if the device supports the Microsoft OS
+ *  Compatibility descriptor extensions (used to give the host additional information on the device's general class
+ *  compatibility for driver-less installation).
+ */
+const USB_Descriptor_String_t PROGMEM MSConpatibilityString = USB_STRING_DESCRIPTOR_ARRAY('M','S','F','T','1','0','0', VENDOR_REQUEST_ID_MS_COMPAT);
+
+/** Microsoft OS Compatibility 1.0 descriptor. This is a special descriptor returned by the device on vendor request
+ *  from the host, giving the OS additional compatibility information. This allows the host to automatically install
+ *  the appropriate driver for various devices which share a common USB class (in this case RNDIS, which uses the
+ *  CDC-ACM class usually used by virtual to serial adapters).
+ */
+const USB_Descriptor_MSCompatibility_t PROGMEM MSCompatibilityDescriptor =
+	{
+		.dwLength                   = sizeof(USB_Descriptor_MSCompatibility_t),
+		.bcdVersion                 = VERSION_BCD(1,0,0),
+		.wIndex                     = 4,
+		.bCount                     = 1,
+		.bReserved                  = { 0 },
+		.bFirstInterfaceNumber      = INTERFACE_ID_CDC_CCI,
+		.bReserved2                 = 1, // Must always be 1 according to spec
+		.compatibleID               = "RNDIS",
+		.subCompatibleID            = "5162001",
+		.bReserved3                 = { 0 },
+	};
+
 /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
  *  documentation) by the application code so that the address and size of a requested descriptor can be given
  *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
@@ -233,6 +259,10 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 					Address = &ProductString;
 					Size    = pgm_read_byte(&ProductString.Header.Size);
 					break;
+				case STRING_ID_MS_Compat:
+					Address = &MSConpatibilityString;
+					Size    = pgm_read_byte(&MSConpatibilityString.Header.Size);
+					break;
 			}
 
 			break;
@@ -242,3 +272,20 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 	return Size;
 }
 
+/** Sends the special Microsoft OS Compatibility Descriptor to the host PC, if
+ *  the host is requesting it.
+ */
+void CheckIfMSCompatibilityDescriptorRequest(void)
+{
+	if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
+	{
+		if (USB_ControlRequest.bRequest == VENDOR_REQUEST_ID_MS_COMPAT)
+		{
+			Endpoint_ClearSETUP();
+
+			/* Write the OS compatibility descriptor to the control endpoint */
+			Endpoint_Write_Control_PStream_LE(&MSCompatibilityDescriptor, sizeof(MSCompatibilityDescriptor));
+			Endpoint_ClearOUT();
+		}
+	}
+}
diff --git a/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.h b/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.h
index 082f44afc..56a3acd23 100644
--- a/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.h
+++ b/Demos/Device/ClassDriver/RNDISEthernet/Descriptors.h
@@ -59,6 +59,10 @@
 		/** Size in bytes of the CDC data IN and OUT endpoints. */
 		#define CDC_TXRX_EPSIZE                64
 
+		/** Vendor request (0-255) the host should issue to retrieve the
+		 *  Microsoft OS Compatibility Descriptors. */
+		#define VENDOR_REQUEST_ID_MS_COMPAT    0x01
+
 	/* Type Defines: */
 		/** Type define for the device configuration descriptor structure. This must be defined in the
 		 *  application code, as the configuration descriptor contains several sub-descriptors which
@@ -81,6 +85,21 @@
 			USB_Descriptor_Endpoint_t             RNDIS_DataInEndpoint;
 		} USB_Descriptor_Configuration_t;
 
+		/** Type define for a Microsoft OS Compatibility 1.0 descriptor. */
+		typedef struct
+		{
+			uint32_t dwLength;
+			uint16_t bcdVersion;
+			uint16_t wIndex;
+			uint8_t  bCount;
+			uint8_t  bReserved[7];
+			uint8_t  bFirstInterfaceNumber;
+			uint8_t  bReserved2;
+			char     compatibleID[8];
+			char     subCompatibleID[8];
+			uint8_t  bReserved3[6];
+		} USB_Descriptor_MSCompatibility_t;
+
 		/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
 		 *  should have a unique ID index associated with it, which can be used to refer to the
 		 *  interface from other descriptors.
@@ -100,6 +119,7 @@
 			STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
 			STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
 			STRING_ID_Product      = 2, /**< Product string ID */
+			STRING_ID_MS_Compat    = 0xEE, /**< MS OS Compatibility string descriptor ID (magic value set by Microsoft) */
 		};
 
 	/* Function Prototypes: */
@@ -108,5 +128,7 @@
 		                                    const void** const DescriptorAddress)
 		                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
 
+		void     CheckIfMSCompatibilityDescriptorRequest(void);
+
 #endif
 
diff --git a/Demos/Device/ClassDriver/RNDISEthernet/RNDISEthernet.c b/Demos/Device/ClassDriver/RNDISEthernet/RNDISEthernet.c
index f963d48e1..cfe90ccd7 100644
--- a/Demos/Device/ClassDriver/RNDISEthernet/RNDISEthernet.c
+++ b/Demos/Device/ClassDriver/RNDISEthernet/RNDISEthernet.c
@@ -174,6 +174,9 @@ void EVENT_USB_Device_ConfigurationChanged(void)
 /** Event handler for the library USB Control Request reception event. */
 void EVENT_USB_Device_ControlRequest(void)
 {
+	/* Send MS OS Compatibility descriptor if requested by the host. */
+	CheckIfMSCompatibilityDescriptorRequest();
+
 	RNDIS_Device_ProcessControlRequest(&Ethernet_RNDIS_Interface);
 }
 
diff --git a/Demos/Device/LowLevel/RNDISEthernet/Descriptors.c b/Demos/Device/LowLevel/RNDISEthernet/Descriptors.c
index 494150349..8e709cee9 100644
--- a/Demos/Device/LowLevel/RNDISEthernet/Descriptors.c
+++ b/Demos/Device/LowLevel/RNDISEthernet/Descriptors.c
@@ -46,7 +46,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 {
 	.Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
 
-	.USBSpecification       = VERSION_BCD(1,1,0),
+	.USBSpecification       = VERSION_BCD(2,0,0),
 	.Class                  = CDC_CSCP_CDCClass,
 	.SubClass               = CDC_CSCP_NoSpecificSubclass,
 	.Protocol               = CDC_CSCP_NoSpecificProtocol,
@@ -55,7 +55,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 
 	.VendorID               = 0x03EB,
 	.ProductID              = 0x204C,
-	.ReleaseNumber          = VERSION_BCD(0,0,1),
+	.ReleaseNumber          = VERSION_BCD(0,0,2),
 
 	.ManufacturerStrIndex   = STRING_ID_Manufacturer,
 	.ProductStrIndex        = STRING_ID_Product,
@@ -192,6 +192,32 @@ const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR
  */
 const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"LUFA RNDIS CDC Demo");
 
+/** Microsoft OS Compatibility string descriptor. This is a special string descriptor that Microsoft based OS hosts
+ *  will query at string descriptor ID 0xEE on initial enumeration, to test if the device supports the Microsoft OS
+ *  Compatibility descriptor extensions (used to give the host additional information on the device's general class
+ *  compatibility for driver-less installation).
+ */
+const USB_Descriptor_String_t PROGMEM MSConpatibilityString = USB_STRING_DESCRIPTOR_ARRAY('M','S','F','T','1','0','0', VENDOR_REQUEST_ID_MS_COMPAT);
+
+/** Microsoft OS Compatibility 1.0 descriptor. This is a special descriptor returned by the device on vendor request
+ *  from the host, giving the OS additional compatibility information. This allows the host to automatically install
+ *  the appropriate driver for various devices which share a common USB class (in this case RNDIS, which uses the
+ *  CDC-ACM class usually used by virtual to serial adapters).
+ */
+const USB_Descriptor_MSCompatibility_t PROGMEM MSCompatibilityDescriptor =
+	{
+		.dwLength                   = sizeof(USB_Descriptor_MSCompatibility_t),
+		.bcdVersion                 = VERSION_BCD(1,0,0),
+		.wIndex                     = 4,
+		.bCount                     = 1,
+		.bReserved                  = { 0 },
+		.bFirstInterfaceNumber      = INTERFACE_ID_CDC_CCI,
+		.bReserved2                 = 1, // Must always be 1 according to spec
+		.compatibleID               = "RNDIS",
+		.subCompatibleID            = "5162001",
+		.bReserved3                 = { 0 },
+	};
+
 /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
  *  documentation) by the application code so that the address and size of a requested descriptor can be given
  *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
@@ -233,6 +259,10 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 					Address = &ProductString;
 					Size    = pgm_read_byte(&ProductString.Header.Size);
 					break;
+				case STRING_ID_MS_Compat:
+					Address = &MSConpatibilityString;
+					Size    = pgm_read_byte(&MSConpatibilityString.Header.Size);
+					break;
 			}
 
 			break;
@@ -242,3 +272,20 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 	return Size;
 }
 
+/** Sends the special Microsoft OS Compatibility Descriptor to the host PC, if
+ *  the host is requesting it.
+ */
+void CheckIfMSCompatibilityDescriptorRequest(void)
+{
+	if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
+	{
+		if (USB_ControlRequest.bRequest == VENDOR_REQUEST_ID_MS_COMPAT)
+		{
+			Endpoint_ClearSETUP();
+
+			/* Write the OS compatibility descriptor to the control endpoint */
+			Endpoint_Write_Control_PStream_LE(&MSCompatibilityDescriptor, sizeof(MSCompatibilityDescriptor));
+			Endpoint_ClearOUT();
+		}
+	}
+}
diff --git a/Demos/Device/LowLevel/RNDISEthernet/Descriptors.h b/Demos/Device/LowLevel/RNDISEthernet/Descriptors.h
index 31c56a00b..8f407fc14 100644
--- a/Demos/Device/LowLevel/RNDISEthernet/Descriptors.h
+++ b/Demos/Device/LowLevel/RNDISEthernet/Descriptors.h
@@ -59,6 +59,10 @@
 		/** Size in bytes of the CDC device-to-host notification IN endpoint. */
 		#define CDC_NOTIFICATION_EPSIZE        8
 
+		/** Vendor request (0-255) the host should issue to retrieve the
+		 *  Microsoft OS Compatibility Descriptors. */
+		#define VENDOR_REQUEST_ID_MS_COMPAT    0x01
+
 	/* Type Defines: */
 		/** Type define for the device configuration descriptor structure. This must be defined in the
 		 *  application code, as the configuration descriptor contains several sub-descriptors which
@@ -81,6 +85,21 @@
 			USB_Descriptor_Endpoint_t             RNDIS_DataInEndpoint;
 		} USB_Descriptor_Configuration_t;
 
+		/** Type define for a Microsoft OS Compatibility 1.0 descriptor. */
+		typedef struct
+		{
+			uint32_t dwLength;
+			uint16_t bcdVersion;
+			uint16_t wIndex;
+			uint8_t  bCount;
+			uint8_t  bReserved[7];
+			uint8_t  bFirstInterfaceNumber;
+			uint8_t  bReserved2;
+			char     compatibleID[8];
+			char     subCompatibleID[8];
+			uint8_t  bReserved3[6];
+		} USB_Descriptor_MSCompatibility_t;
+
 		/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
 		 *  should have a unique ID index associated with it, which can be used to refer to the
 		 *  interface from other descriptors.
@@ -100,6 +119,7 @@
 			STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
 			STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
 			STRING_ID_Product      = 2, /**< Product string ID */
+			STRING_ID_MS_Compat    = 0xEE, /**< MS OS Compatibility string descriptor ID (magic value set by Microsoft) */
 		};
 
 	/* Function Prototypes: */
@@ -108,5 +128,7 @@
 		                                    const void** const DescriptorAddress)
 		                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
 
+		void     CheckIfMSCompatibilityDescriptorRequest(void);
+
 #endif
 
diff --git a/Demos/Device/LowLevel/RNDISEthernet/RNDISEthernet.c b/Demos/Device/LowLevel/RNDISEthernet/RNDISEthernet.c
index 80cde672c..247458f39 100644
--- a/Demos/Device/LowLevel/RNDISEthernet/RNDISEthernet.c
+++ b/Demos/Device/LowLevel/RNDISEthernet/RNDISEthernet.c
@@ -130,6 +130,9 @@ void EVENT_USB_Device_ConfigurationChanged(void)
  */
 void EVENT_USB_Device_ControlRequest(void)
 {
+	/* Send MS OS Compatibility descriptor if requested by the host. */
+	CheckIfMSCompatibilityDescriptorRequest();
+
 	/* Process RNDIS class commands */
 	switch (USB_ControlRequest.bRequest)
 	{
diff --git a/LUFA/DoxygenPages/ChangeLog.txt b/LUFA/DoxygenPages/ChangeLog.txt
index bfeb0f121..68acba2d2 100644
--- a/LUFA/DoxygenPages/ChangeLog.txt
+++ b/LUFA/DoxygenPages/ChangeLog.txt
@@ -10,6 +10,8 @@
   *  <b>New:</b>
   *  - Core:
   *   - The USE_INTERNAL_SERIAL definition can now be overridden by the user to a custom string index (thanks to Nicohood)
+  *  - Library Applications:
+  *   - Added Microsoft OS Compatibility descriptors to the RNDIS demos for driverless install on Windows 7 and newer
   *
   *  <b>Fixed:</b>
   *  - Core:
diff --git a/Projects/Webserver/Descriptors.c b/Projects/Webserver/Descriptors.c
index e4166228b..95a0c8f1e 100644
--- a/Projects/Webserver/Descriptors.c
+++ b/Projects/Webserver/Descriptors.c
@@ -47,7 +47,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 {
 	.Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
 
-	.USBSpecification       = VERSION_BCD(1,1,0),
+	.USBSpecification       = VERSION_BCD(2,0,0),
 	.Class                  = USB_CSCP_IADDeviceClass,
 	.SubClass               = USB_CSCP_IADDeviceSubclass,
 	.Protocol               = USB_CSCP_IADDeviceProtocol,
@@ -56,7 +56,7 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 
 	.VendorID               = 0x03EB,
 	.ProductID              = 0x2069,
-	.ReleaseNumber          = VERSION_BCD(0,0,1),
+	.ReleaseNumber          = VERSION_BCD(0,0,2),
 
 	.ManufacturerStrIndex   = STRING_ID_Manufacturer,
 	.ProductStrIndex        = STRING_ID_Product,
@@ -243,6 +243,32 @@ const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR
  */
 const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"LUFA Webserver");
 
+/** Microsoft OS Compatibility string descriptor. This is a special string descriptor that Microsoft based OS hosts
+ *  will query at string descriptor ID 0xEE on initial enumeration, to test if the device supports the Microsoft OS
+ *  Compatibility descriptor extensions (used to give the host additional information on the device's general class
+ *  compatibility for driver-less installation).
+ */
+const USB_Descriptor_String_t PROGMEM MSConpatibilityString = USB_STRING_DESCRIPTOR_ARRAY('M','S','F','T','1','0','0', VENDOR_REQUEST_ID_MS_COMPAT);
+
+/** Microsoft OS Compatibility 1.0 descriptor. This is a special descriptor returned by the device on vendor request
+ *  from the host, giving the OS additional compatibility information. This allows the host to automatically install
+ *  the appropriate driver for various devices which share a common USB class (in this case RNDIS, which uses the
+ *  CDC-ACM class usually used by virtual to serial adapters).
+ */
+const USB_Descriptor_MSCompatibility_t PROGMEM MSCompatibilityDescriptor =
+	{
+		.dwLength                   = sizeof(USB_Descriptor_MSCompatibility_t),
+		.bcdVersion                 = VERSION_BCD(1,0,0),
+		.wIndex                     = 4,
+		.bCount                     = 1,
+		.bReserved                  = { 0 },
+		.bFirstInterfaceNumber      = INTERFACE_ID_CDC_CCI,
+		.bReserved2                 = 1, // Must always be 1 according to spec
+		.compatibleID               = "RNDIS",
+		.subCompatibleID            = "5162001",
+		.bReserved3                 = { 0 },
+	};
+
 /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
  *  documentation) by the application code so that the address and size of a requested descriptor can be given
  *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
@@ -284,6 +310,10 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 					Address = &ProductString;
 					Size    = pgm_read_byte(&ProductString.Header.Size);
 					break;
+				case STRING_ID_MS_Compat:
+					Address = &MSConpatibilityString;
+					Size    = pgm_read_byte(&MSConpatibilityString.Header.Size);
+					break;
 			}
 
 			break;
@@ -293,3 +323,20 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 	return Size;
 }
 
+/** Sends the special Microsoft OS Compatibility Descriptor to the host PC, if
+ *  the host is requesting it.
+ */
+void CheckIfMSCompatibilityDescriptorRequest(void)
+{
+	if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_VENDOR | REQREC_DEVICE))
+	{
+		if (USB_ControlRequest.bRequest == VENDOR_REQUEST_ID_MS_COMPAT)
+		{
+			Endpoint_ClearSETUP();
+
+			/* Write the OS compatibility descriptor to the control endpoint */
+			Endpoint_Write_Control_PStream_LE(&MSCompatibilityDescriptor, sizeof(MSCompatibilityDescriptor));
+			Endpoint_ClearOUT();
+		}
+	}
+}
diff --git a/Projects/Webserver/Descriptors.h b/Projects/Webserver/Descriptors.h
index 2008b08cb..64c700bbd 100644
--- a/Projects/Webserver/Descriptors.h
+++ b/Projects/Webserver/Descriptors.h
@@ -68,6 +68,10 @@
 		/** Size in bytes of the CDC data IN and OUT endpoints. */
 		#define CDC_TXRX_EPSIZE                64
 
+		/** Vendor request (0-255) the host should issue to retrieve the
+		 *  Microsoft OS Compatibility Descriptors. */
+		#define VENDOR_REQUEST_ID_MS_COMPAT    0x01
+
 	/* Type Defines: */
 		/** Type define for the device configuration descriptor structure. This must be defined in the
 		 *  application code, as the configuration descriptor contains several sub-descriptors which
@@ -96,6 +100,21 @@
 			USB_Descriptor_Endpoint_t              MS_DataOutEndpoint;
 		} USB_Descriptor_Configuration_t;
 
+		/** Type define for a Microsoft OS Compatibility 1.0 descriptor. */
+		typedef struct
+		{
+			uint32_t dwLength;
+			uint16_t bcdVersion;
+			uint16_t wIndex;
+			uint8_t  bCount;
+			uint8_t  bReserved[7];
+			uint8_t  bFirstInterfaceNumber;
+			uint8_t  bReserved2;
+			char     compatibleID[8];
+			char     subCompatibleID[8];
+			uint8_t  bReserved3[6];
+		} USB_Descriptor_MSCompatibility_t;
+
 		/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
 		 *  should have a unique ID index associated with it, which can be used to refer to the
 		 *  interface from other descriptors.
@@ -116,6 +135,7 @@
 			STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
 			STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
 			STRING_ID_Product      = 2, /**< Product string ID */
+			STRING_ID_MS_Compat    = 0xEE, /**< MS OS Compatibility string descriptor ID (magic value set by Microsoft) */
 		};
 
 	/* Function Prototypes: */
@@ -124,5 +144,7 @@
 		                                    const void** const DescriptorAddress)
 		                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
 
+		void     CheckIfMSCompatibilityDescriptorRequest(void);
+
 #endif
 
diff --git a/Projects/Webserver/USBDeviceMode.c b/Projects/Webserver/USBDeviceMode.c
index 547291475..267e42c2b 100644
--- a/Projects/Webserver/USBDeviceMode.c
+++ b/Projects/Webserver/USBDeviceMode.c
@@ -141,6 +141,9 @@ void EVENT_USB_Device_ConfigurationChanged(void)
 /** Event handler for the library USB Control Request reception event. */
 void EVENT_USB_Device_ControlRequest(void)
 {
+	/* Send MS OS Compatibility descriptor if requested by the host. */
+	CheckIfMSCompatibilityDescriptorRequest();
+
 	RNDIS_Device_ProcessControlRequest(&Ethernet_RNDIS_Interface_Device);
 	MS_Device_ProcessControlRequest(&Disk_MS_Interface);
 }
-- 
GitLab