boart test board

Dependencies:   USBDevice mbed-dev lwip

Fork of USBSerial_HelloWorld by Compass Yap

usbd_desc2.cpp

Committer:
ua1arn
Date:
2018-08-03
Revision:
23:121b78470d39
Parent:
21:85a0f94a84cd

File content as of revision 23:121b78470d39:


#include <string.h>
#include <wchar.h>
#include <stdint.h>
#include <stdarg.h>

//#include "usb_conf.h"
//#include "usbd_core.h"
//#include "usbd_desc.h"
//#include "usbd_conf.h"

//#include "debug.h"

// TODO: move to right place
#include "usbd_desc2.h"


#define ALIGNX_BEGIN //__ALIGN_BEGIN
#define ALIGNX_END //__ALIGN_END

#define CPUSTYLE_STM32F	1

/* USB Descriptor Types */
#define USB_DEVICE_DESCRIPTOR_TYPE                  1
#define USB_CONFIGURATION_DESCRIPTOR_TYPE           2
#define USB_STRING_DESCRIPTOR_TYPE                  3
#define USB_INTERFACE_DESCRIPTOR_TYPE               4
#define USB_ENDPOINT_DESCRIPTOR_TYPE                5
#define USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE        6
#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE      7
#define USB_INTERFACE_POWER_DESCRIPTOR_TYPE         8
#define USB_OTG_DESCRIPTOR_TYPE                     9
#define USB_DEBUG_DESCRIPTOR_TYPE                  10
#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE  11
#define USB_DEVICE_CAPABITY_DESCRIPTOR_TYPE                           0x10

/* Wireless USB extension Descriptor Type. */
#define USB_SECURITY_TYPE                          12
#define USB_KEY_TYPE                               13
#define USB_ENCRIPTION_TYPE                        14
#define USB_BOS_TYPE                               15
#define USB_DEVICE_CAPABILITY_TYPE                 16
#define USB_WIRELESS_ENDPOINT_COMPANION_TYPE       17

/* USB Device Classes */
#define USB_DEVICE_CLASS_RESERVED              0x00
#define USB_DEVICE_CLASS_AUDIO                 0x01
#define USB_DEVICE_CLASS_COMMUNICATIONS        0x02
#define USB_DEVICE_CLASS_HUMAN_INTERFACE       0x03
#define USB_DEVICE_CLASS_MONITOR               0x04
#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE    0x05
#define USB_DEVICE_CLASS_POWER                 0x06
#define USB_DEVICE_CLASS_PRINTER               0x07
#define USB_DEVICE_CLASS_STORAGE               0x08
#define USB_DEVICE_CLASS_HUB                   0x09
#define USB_DEVICE_CLASS_WIRELESS_CONTROLLER   0xE0
#define USB_DEVICE_CLASS_MISCELLANEOUS         0xEF
#define USB_DEVICE_CLASS_VENDOR_SPECIFIC       0xFF

#define UNICODE_ENGLISH     (0x0409)    /* US_English (Ref: USB_LANGIDs.pdf) */

// --- адаптация к использованию



// +++ адаптация к использованию
#include "usb200.h"


#if WITHUSBHW

//#include "usb200.h"
//#include "usbch9.h"

// UAC audio device
// USB\VID_FFFF&PID_0736&REV_0100&MI_00
// USB\VID_FFFF&PID_0736&MI_00

// ACM serial device:
//
// USB\VID_FFFF&PID_0736&REV_0100&MI_03
// USB\VID_FFFF&PID_0736&MI_03

// CDC ECM
//
// USB\Class_02&SubClass_06

// CDC EEM 
//
// USB\Class_02&SubClass_0c&Prot_07
// USB\Class_02&SubClass_0c
// USB\Class_02
//
// http://blog.metrotek.spb.ru/2011/07/07/usb-set-na-cortex-m3/

#define USB_FUNCTION_BCD_USB	0x0200	// 0x0201 in ST samples
#define USB_FUNCTION_VENDOR_ID	0xFFFF	// Generic
//#define USB_FUNCTION_VENDOR_ID	0x041C	// Altera Corp.
//#define USB_FUNCTION_VENDOR_ID	0x04d9	// Holtek Semiconductor, Inc.

#define USB_FUNCTION_PRODUCT_ID	0x0001

// From STMicroelectronics Comunication Device Class driver (CDC) INF FILE:
//#define USB_FUNCTION_VENDOR_ID	0x0483	// STM
//#define USB_FUNCTION_PRODUCT_ID	0x5740
//#define USB_FUNCTION_RELEASE_NO	0x0200

#if WITHUSBUAC && WITHUSBUAC3
	#if WITHRTS96
		#define BUILD_ID 6	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0106
	#elif WITHRTS192
		#define BUILD_ID 5	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0105
	#else
		#define BUILD_ID 4	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0104
	#endif
#else /* WITHUSBUAC && WITHUSBUAC3 */
	#if WITHRTSNOAUDIO
		#define BUILD_ID 3	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0103
	#elif WITHRTS96
		#define BUILD_ID 2	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0102
	#elif WITHRTS192
		#define BUILD_ID 1	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0101
	#else
		#define BUILD_ID 0	// модификатор serial sumber
		#define USB_FUNCTION_RELEASE_NO	0x0100
	#endif
#endif /* WITHUSBUAC && WITHUSBUAC3 */

enum
{
	STRING_ID_0 = 0, /* Language ID */

	STRING_ID_1, /* Manufacturer */
	STRING_ID_2, /* Product */
	STRING_ID_3, /* SerialNumber */

	// USB CDC strings
	STRING_ID_4a, /*  */
	STRING_ID_4b, /*  */

	STRING_ID_5,
	STRING_ID_5a,
	STRING_ID_MACADDRESS,	// iMacAddress

	// USB UAC strings
	STRING_ID_a0, /* tag for Interface Descriptor 0/0 Audio */
	STRING_ID_a1, /* tag for Interface Descriptor 0/0 Spectrum */

	STRING_ID_d0,
	STRING_ID_d1,

	STRING_ID_e0,
	STRING_ID_e1,

	STRING_ID_x0, /*  */
	STRING_ID_x1, /*  */

	STRING_ID_y0, /*  */
	STRING_ID_y1, /*  */

	STRING_ID_z0, /*  */
	STRING_ID_z1, /*  */

	//STRING_ID_b,	// tag for USB Speaker Audio Feature Unit Descriptor

	STRING_ID_Left, STRING_ID_Right,	// Идут подряд

	STRING_ID_RNDIS,
	STRING_ID_HIDa,
	STRING_ID_IQSPECTRUM,
	// 
	STRING_ID_count
};

struct stringtempl
{
	uint_fast8_t id;
	const char * str;
};

static const struct stringtempl strtemplates [] =
{
	{ STRING_ID_1, "Smekalka.com", },		// Manufacturer
	{ STRING_ID_2, "HLAB reader", },	// Product
	{ STRING_ID_4a, "HLAB reader CAT", },
	{ STRING_ID_4b, "HLAB reader CTL", },
	{ STRING_ID_5, "HLAB reader CDC EEM", },
	{ STRING_ID_5a, "HLAB reader CDC ECM", },
	{ STRING_ID_RNDIS, "HLAB reader Remote NDIS", },

	{ STRING_ID_a0, "HLAB reader Voice", },	// tag for Interface Descriptor 0/0 Audio
	{ STRING_ID_a1, "HLAB reader Spectrum", },	// tag for Interface Descriptor 0/0 Audio

	//{ STRING_ID_b, "xxx_id11", },	// tag for USB Speaker Audio Feature Unit Descriptor

	{ STRING_ID_d0, "Transmitter Input1", },	// Audio Control Input Terminal Descriptor 
	{ STRING_ID_d1, "Transmitter Input2", },	// Audio Control Input Terminal Descriptor 

	{ STRING_ID_e0, "Receiver Output 1", },	// Audio Control Output Terminal Descriptor 
	{ STRING_ID_e1, "Receiver Output 2", },	// Audio Control Output Terminal Descriptor 

	{ STRING_ID_x0, "xxxx In 1", },	// Audio Control Output Terminal Descriptor 
	{ STRING_ID_x1, "xxxx In 2", },	// Audio Control Output Terminal Descriptor 

	{ STRING_ID_y0, "yyyy In 1", },	// Audio Control Output Terminal Descriptor 
	{ STRING_ID_y1, "yyyy In 2", },	// Audio Control Output Terminal Descriptor 

	{ STRING_ID_z0, "zzzz In 1", },	// Audio Control Output Terminal Descriptor 
	{ STRING_ID_z1, "zzzz In 2", },	// Audio Control Output Terminal Descriptor 

	{ STRING_ID_Left, "USB", },	// tag for USB Speaker Audio Feature Unit Descriptor
	{ STRING_ID_Right, "LSB", },	// tag for USB Speaker Audio Feature Unit Descriptor
	{ STRING_ID_HIDa, "HID xxx", },
	{ STRING_ID_IQSPECTRUM, "RX IQ Output", },
};
#if 0

static int
toprintc(int c)
{
	if (c < 0x20 || c >= 0x7f)
		return '.';
	return c;
}

void
static printhex(unsigned long voffs, const unsigned char * buff, unsigned length)
{
	unsigned i, j;
	unsigned rows = (length + 15) / 16;

	for (i = 0; i < rows; ++ i)
	{
		int trl = 16;
		//const int trl = ((length - 1) - i * 16) % 16 + 1;	// количество символов в данной строке

		PRINTF(PSTR("%08lX "), voffs + i * 16);
		for (j = 0; j < trl; ++ j)
			PRINTF(PSTR(" %02X"), buff [i * 16 + j]);

		PRINTF(PSTR("%*s"), (16 - trl) * 3, "");

		PRINTF(PSTR("  "));
		for (j = 0; j < trl; ++ j)
			PRINTF(PSTR("%c"), toprintc(buff [i * 16 + j]));

		PRINTF(PSTR("\n"));
	}
}
#endif


// Инициализация дескриптора произвольным массивом данных
static unsigned fill_pattern_descriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, const void * pattern, unsigned length)
{
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		memcpy(buff, pattern, length);
	}
	return length;
}

// usb_20.pdf:
// 5.9 High-Speed, High Bandwidth Endpoints
// 9.6.6 Endpoint
// Table 9-14. Allowed wMaxPacketSize Values for Different Numbers of Transactions per Microframe
static uint_fast16_t encodeMaxPacketSize(uint_fast32_t size)
{
	// For all endpoints, bits 10..0 specify the maximum
	// packet size (in bytes).

	// A high-speed endpoint can move up to 3072 bytes per microframe
	// For high-speed isochronous and interrupt endpoints:
	// Bits 12..11 specify the number of additional transaction
	// opportunities per microframe:
	// 00 = None (1 transaction per microframe)
	// 01 = 1 additional (2 per microframe)
	// 10 = 2 additional (3 per microframe)

	if (size <= 1024)
		return size;	// 1..1024
	if (size <= 2048)
		return (0x01 << 11) | ((size + 1) / 2);	// 513..1024
	else
		return (0x02 << 11) | ((size + 2) / 3);	// 683..1024
}

#if WITHUSBUAC

#if 0
	// Вариант Oleg UR3IQO
	static const uint_fast8_t USBD_UACIN_EP_ATTRIBUTES = 
		USB_ENDPOINT_USAGE_DATA | 
		USB_ENDPOINT_SYNC_SYNCHRONOUS | 
		USB_ENDPOINT_TYPE_ISOCHRONOUS;

	static const uint_fast8_t USBD_UACOUT_EP_ATTRIBUTES = 
		USB_ENDPOINT_USAGE_DATA | 
		USB_ENDPOINT_SYNC_SYNCHRONOUS | 
		USB_ENDPOINT_TYPE_ISOCHRONOUS;
#else
	// Мой вариант
	static const uint_fast8_t USBD_UACIN_EP_ATTRIBUTES = 
		USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK | 
		USB_ENDPOINT_SYNC_ASYNCHRONOUS | 
		USB_ENDPOINT_TYPE_ISOCHRONOUS;

	static const uint_fast8_t USBD_UACOUT_EP_ATTRIBUTES = 
		USB_ENDPOINT_USAGE_DATA | 
		USB_ENDPOINT_SYNC_ASYNCHRONOUS | 
		USB_ENDPOINT_TYPE_ISOCHRONOUS;
#endif


#if 0

//In the following code bmAttributes field is 0x01; 
//which means that clock type is internal fixed clock.
/* Clock Source Descriptor(4.7.2.1) */ 
static unsigned r9fill_clock_source(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения; а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CS_INTERFACE;       /* bDescriptorType(0x24): CS_INTERFACE */ 
		* buff ++ = 0x0A;       /* bDescriptorSubType(0x0A): CLOCK_SOURCE */ 
		* buff ++ = 0x10;       /* bClockID(0x10): CLOCK_SOURCE_ID */ 
		* buff ++ = 0x01;       /* bmAttributes(0x01): internal fixed clock */ 
		* buff ++ = 0x07;       /* bmControls(0x07):                    
								clock frequency control: 0b11 - host programmable;                    
								clock validity control: 0b01 - host read only */ 
		* buff ++ = TERMINAL_ID_UNDEFINED;       /* bAssocTerminal(0x00) */ 
		* buff ++ = 0x01;       /* iClockSource(0x01): Not requested */
	}
	return length;
}

#endif

/* UAC IAD */
// Interface Association Descriptor Audio
// Audio10.pdf 4.3.2.8 Associated Interface Descriptor
// documented in USB ECN : Interface Association Descriptor - InterfaceAssociationDescriptor_ecn.pdf
static unsigned UAC_InterfaceAssociationDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bFirstInterface,
	uint_fast8_t bInterfaceCount,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE;	// bDescriptorType: IAD
		* buff ++ = bFirstInterface;			// bFirstInterface
		* buff ++ = bInterfaceCount;	// bInterfaceCount
		* buff ++ = USB_DEVICE_CLASS_AUDIO;	// bFunctionClass: Audio
		* buff ++ = 0x00;	// bFunctionSubClass
		* buff ++ = 0x00;	// bFunctionProtocol
		* buff ++ = STRING_ID_a0 + offset;	// Interface string index
	}
	return length;
}

/* USB Speaker Standard interface descriptor */
// Interface Descriptor 0/0 Audio, 0 Endpoints
static unsigned r9fill_3(uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	uint_fast8_t bInterfaceNumber,
	uint_fast8_t bAlternateSetting,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;      /* bDescriptorType */
		* buff ++ = bInterfaceNumber;					/* bInterfaceNumber */
		* buff ++ = bAlternateSetting;					/* bAlternateSetting */
		* buff ++ = 0x00;                               /* bNumEndpoints */
		* buff ++ = USB_DEVICE_CLASS_AUDIO;             /* bInterfaceClass */
		* buff ++ = AUDIO_SUBCLASS_AUDIOCONTROL;        /* bInterfaceSubClass */
		* buff ++ = AUDIO_PROTOCOL_UNDEFINED;           /* bInterfaceProtocol */
		* buff ++ = STRING_ID_a0 + offset;               /* iInterface */
		/* 09 byte*/
	}
	return length;
}

// Audio Control Input Terminal Descriptor 
// audio10.pdf: Table 4-3: Input Terminal Descriptor
// Sereo signal source
// Audio или RTS
static unsigned UAC_AudioControlIT_IN(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bTerminalID, uint_fast8_t offset)
{
	const uint_fast8_t length = 12;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 4.3.2.1 Input Terminal Descriptor 
		const uint_fast16_t wTerminalType = AUDIO_TERMINAL_RADIO_RECEIVER;
		const uint_fast8_t bNrChannels = HARDWARE_USBD_AUDIO_IN_CHANNELS;
		const uint_fast16_t wChannelConfig = bNrChannels == 1 ? 
			AUDIO_CHANNEL_M : // Mono
			(AUDIO_CHANNEL_L | AUDIO_CHANNEL_R);	// Left Front & Right Front
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE; // CS_INTERFACE Descriptor Type
		* buff ++ = AUDIO_CONTROL_INPUT_TERMINAL;    // INPUT_TERMINAL 0x02 descriptor subtype
		* buff ++ = bTerminalID;                   // bTerminalID ID of this Terminal.
		* buff ++ = LO_BYTE(wTerminalType);			/* wTerminalType */
		* buff ++ = HI_BYTE(wTerminalType);
		* buff ++ = TERMINAL_ID_UNDEFINED;        // bAssocTerminal No association
		// The bNrChannels, wChannelConfig and iChannelNames fields together constitute the cluster descriptor
		* buff ++ = bNrChannels;    /* bNrChannels */
		* buff ++ = LO_BYTE(wChannelConfig);   /* bmChannelConfig size = 4 bytes Mono sets no position bits */
		* buff ++ = HI_BYTE(wChannelConfig);
		* buff ++ = STRING_ID_Left;							/* iChannelNames */
		* buff ++ = 0;						// iTerminal - Index of a string descriptor, describing the Input Terminal. Receiver Output

	}
	return length;
}

static unsigned UAC_AudioControlIT_INRTS(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bTerminalID, uint_fast8_t offset)
{
	const uint_fast8_t length = 12;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 4.3.2.1 Input Terminal Descriptor 
		const uint_fast16_t wTerminalType = AUDIO_TERMINAL_RADIO_RECEIVER;
		const uint_fast8_t bNrChannels = HARDWARE_USBD_AUDIO_IN_CHANNELS_RTS; // для канала со спектром всегда стерео. но это не тут указано
		const uint_fast16_t wChannelConfig = bNrChannels == 1 ? 
			AUDIO_CHANNEL_M : // Mono
			(AUDIO_CHANNEL_L | AUDIO_CHANNEL_R);	// Left Front & Right Front
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE; // CS_INTERFACE Descriptor Type
		* buff ++ = AUDIO_CONTROL_INPUT_TERMINAL;    // INPUT_TERMINAL 0x02 descriptor subtype
		* buff ++ = bTerminalID;                   // bTerminalID ID of this Terminal.
		* buff ++ = LO_BYTE(wTerminalType);			/* wTerminalType */
		* buff ++ = HI_BYTE(wTerminalType);
		* buff ++ = TERMINAL_ID_UNDEFINED;        // bAssocTerminal No association
		// The bNrChannels, wChannelConfig and iChannelNames fields together constitute the cluster descriptor
		* buff ++ = bNrChannels;    /* bNrChannels */
		* buff ++ = LO_BYTE(wChannelConfig);   /* bmChannelConfig size = 4 bytes Mono sets no position bits */
		* buff ++ = HI_BYTE(wChannelConfig);
		* buff ++ = 0;							/* iChannelNames */
		* buff ++ = STRING_ID_IQSPECTRUM;		// iTerminal - Index of a string descriptor, describing the Input Terminal. Receiver Output

	}
	return length;
}

/* !USB Speaker Input Terminal Descriptor */
// Audio Control Input Terminal Descriptor 
// audio10.pdf: Table 4-3: Input Terminal Descriptor
// audio48 only
static unsigned UAC_AudioControlIT_OUT(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bTerminalID,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 12;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 4.3.2.1 Input Terminal Descriptor 
		const uint_fast16_t wTerminalType = AUDIO_TERMINAL_USB_STREAMING;
		const uint_fast8_t bNrChannels = HARDWARE_USBD_AUDIO_OUT_CHANNELS;
		const uint_fast16_t wChannelConfig = bNrChannels == 1 ? 
			AUDIO_CHANNEL_M : // Mono
			(AUDIO_CHANNEL_L | AUDIO_CHANNEL_R);	// Left Front & Right Front

		* buff ++ = length;									/* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;		/* bDescriptorType */
		* buff ++ = AUDIO_CONTROL_INPUT_TERMINAL;			/* bDescriptorSubtype */
		* buff ++ = bTerminalID;							/* bTerminalID */
		* buff ++ = LO_BYTE(wTerminalType);					/* wTerminalType */
		* buff ++ = HI_BYTE(wTerminalType);
		* buff ++ = TERMINAL_ID_UNDEFINED;					/* bAssocTerminal */
		// The bNrChannels, wChannelConfig and iChannelNames fields together constitute the cluster descriptor
		* buff ++ = bNrChannels;							/* bNrChannels */
		* buff ++ = LO_BYTE(wChannelConfig);                /* wChannelConfig 0x0003  Front Left; Front Right */
		* buff ++ = HI_BYTE(wChannelConfig);
		* buff ++ = STRING_ID_Left;							/* iChannelNames */
		* buff ++ = STRING_ID_d0 + offset;					/* iTerminal - появляется как pop-up в панели управления ASIO4ALL */
		/* 12 bytes*/
	}
	return length;
}

/*! USB Microphone Output Terminal Descriptor bSourceID -> bTerminalID */
// Audio Control Output Terminal Descriptor 
// Audio или RTS
static unsigned UAC_AudioControlOT_IN(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bTerminalID,
	uint_fast8_t bSourceID,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 
		const uint_fast16_t wTerminalType = AUDIO_TERMINAL_USB_STREAMING;
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE; // CS_INTERFACE Descriptor Type (bDescriptorType)
		* buff ++ = AUDIO_CONTROL_OUTPUT_TERMINAL;   // OUTPUT_TERMINAL descriptor subtype (bDescriptorSubtype)
		* buff ++ = bTerminalID;                            // ID of this Terminal. (bTerminalID)
		* buff ++ = LO_BYTE(wTerminalType);					/* wTerminalType */
		* buff ++ = HI_BYTE(wTerminalType);
		* buff ++ = TERMINAL_ID_UNDEFINED;           // unused         (bAssocTerminal)
		* buff ++ = bSourceID;                            // From Input Terminal.(bSourceID)
		* buff ++ = STRING_ID_e0 + offset;					// unused  (iTerminal)
	}
	return length;
}

#if 0
// Selector Unit Descriptor
static unsigned UAC_AudioSelectorUnit_IN(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	uint_fast8_t bUnitID
	)
{
	//unsigned i;
	const uint_fast8_t bNrInPins = TERMINAL_ID_SELECTOR_6_INPUTS;	// количество входных потоков
	const uint_fast8_t length = 6 + bNrInPins;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		* buff ++ = length;							/* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;/* bDescriptorType */
		* buff ++ = AUDIO_CONTROL_SELECTOR_UNIT;	/* bDescriptorSubtype */
		* buff ++ = bUnitID;             			/* bUnitID */
		* buff ++ = bNrInPins;             			/* bNrInPins */
		* buff ++ = TERMINAL_ID_IT_2;				/* baSourceID(0) */
		* buff ++ = TERMINAL_ID_FU_5c;				/* baSourceID(1) */
		* buff ++ = 0;								/* iSelector (string ID) - unused */
	}
	return length;
}
#endif

// Audio Control Feature Unit Descriptor 
// See 4.3.2.5 Feature Unit Descriptor for details
// В нашем случае используется для подавления отображения раздельных элементов регулировки уровня по каналам
static unsigned UAC_AudioFeatureUnit(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	uint_fast8_t bUnitID,
	uint_fast8_t bSourceID
	)
{
	// Параметр определяет, ккие управляющие элементы появляются на страничке управления "Custom"
	// Причем, на этой страничке собраны все управляющие элементы со всех Feature Unit Descriptor 
	// в пути сигнала данного источника звука.
	// Не может быть нулём.
	const uint_fast32_t bmaControls = 
		AUDIO_CONTROL_MUTE |
		AUDIO_CONTROL_VOLUME |
		//AUDIO_CONTROL_AUTOMATIC_GAIN |
		//AUDIO_CONTROL_GRAPHIC_EQUALIZER |
		//AUDIO_CONTROL_LOUDNESS |		// "Custom" property page added
		0;

	const uint_fast8_t n = 1; // 1: Only master channel controls, 3: master, left and right
	const uint_fast8_t bControlSize = 2;	/* Достаточно, чтобы вместить все определенные для bmaControls биты */
	const uint_fast8_t length = 7 + bControlSize * n;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		uint_fast8_t i;
		// See 4.3.2.5 Feature Unit Descriptor for details
		* buff ++ = length;							/* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;/* bDescriptorType */
		* buff ++ = AUDIO_CONTROL_FEATURE_UNIT;     /* bDescriptorSubtype */
		* buff ++ = bUnitID;             			/* bUnitID */
		* buff ++ = bSourceID;						/* bSourceID */
		* buff ++ = bControlSize;                   /* bControlSize - колтчество элементов в следующем элементе, повторяющемся для каждого канала */
		for (i = 0; i < n; ++ i)
		{
			uint_fast32_t v = bmaControls;
			uint_fast8_t cs = bControlSize;
			while (cs --)
			{
				* buff ++ = (uint8_t) v;
				v >>= 8;
			}
		}
		* buff ++ = 0;//STRING_ID_b;                    /* iTerminal */
		/* 10 byte*/
	}
	return length;
}

//
// Если выход AUDIO_TERMINAL_RADIO_TRANSMITTER, закладки enchancements нет
// Audio Control Output Terminal Descriptor 
// bSourceID -> bTerminalID
// audio48 only
static unsigned UAC_AudioControlOT_OUT(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bTerminalID,
	uint_fast8_t bSourceID
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 4.3.2.2 Output Terminal Descriptor 
		const uint_fast16_t wTerminalType = AUDIO_TERMINAL_RADIO_TRANSMITTER;

		* buff ++ = length;							/* 0 bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;/* 1 bDescriptorType */
		* buff ++ = AUDIO_CONTROL_OUTPUT_TERMINAL;  /* 2 bDescriptorSubtype */
		* buff ++ = bTerminalID;					/* 3 bTerminalID */
		* buff ++ = LO_BYTE(wTerminalType);			/* 4 wTerminalType */
		* buff ++ = HI_BYTE(wTerminalType);
		* buff ++ = TERMINAL_ID_UNDEFINED;          /* 6 bAssocTerminal */
		* buff ++ = bSourceID;						/* 7 bSourceID */
		* buff ++ = 0;                    /* 8 iTerminal*/
		/* 9 byte*/
	}
	return length;
}

#define WITHUSENOFU 0	// 1 - без использования Feature Unit, 0 - с использованием, игнорирование управления громкостью

// Заполнение схемы ввода звука
// IN data flow
static unsigned UAC_AudioControlIfCircuitIN(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	if (WITHUSENOFU)
	{
		// Только один источник для компьютера
		n += UAC_AudioControlIT_IN(fill, p + n, maxsize - n, TERMINAL_ID_IT_2 + offset, offset);	/* AUDIO_TERMINAL_RADIO_RECEIVER */
		n += UAC_AudioControlOT_IN(fill, p + n, maxsize - n,  TERMINAL_ID_OT_4 + offset, TERMINAL_ID_IT_2 + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Terminal Descriptor TERMINAL_ID_IT_2 -> TERMINAL_ID_OT_4 */
	}
	else
	{
		// Только один источник для компьютера
		n += UAC_AudioControlIT_IN(fill, p + n, maxsize - n, TERMINAL_ID_IT_2 + offset, offset);	/* AUDIO_TERMINAL_RADIO_RECEIVER */
		n += UAC_AudioFeatureUnit(fill, p + n, maxsize - n, TERMINAL_ID_FU_AUDIO + offset, TERMINAL_ID_IT_2 + offset);	/* USB microphone Audio Feature Unit Descriptor TERMINAL_ID_IT_1 -> TERMINAL_ID_FU_AUDIO */
		n += UAC_AudioControlOT_IN(fill, p + n, maxsize - n,  TERMINAL_ID_OT_4 + offset, TERMINAL_ID_FU_AUDIO + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Terminal Descriptor TERMINAL_ID_FU_AUDIO -> TERMINAL_ID_OT_4 */
	}

	return n;
}

// Заполнение схемы вывода звука
// OUT data flow
// audio48 only
static unsigned UAC_AudioControlIfCircuitOUT(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	if (WITHUSENOFU)
	{
		// без feature unit между IT и OT
		n += UAC_AudioControlIT_OUT(fill, p + n, maxsize - n, TERMINAL_ID_IT_1 + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Input Terminal Descriptor TERMINAL_ID_IT_1 + offset */
		n += UAC_AudioControlOT_OUT(fill, p + n, maxsize - n, TERMINAL_ID_OT_3 + offset, TERMINAL_ID_IT_1 + offset);	/* AUDIO_TERMINAL_RADIO_TRANSMITTER Output Terminal Descriptor TERMINAL_ID_IT_1 + offset -> TERMINAL_ID_OT_3 + offset */
	}
	else
	{
		n += UAC_AudioControlIT_OUT(fill, p + n, maxsize - n, TERMINAL_ID_IT_1 + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Input Terminal Descriptor TERMINAL_ID_IT_1 */
		n += UAC_AudioFeatureUnit(fill, p + n, maxsize - n, TERMINAL_ID_FU_5 + offset, TERMINAL_ID_IT_1 + offset);	/* USB Speaker Audio Feature Unit Descriptor TERMINAL_ID_IT_1 -> TERMINAL_ID_FU_5 */
		n += UAC_AudioControlOT_OUT(fill, p + n, maxsize - n, TERMINAL_ID_OT_3 + offset, TERMINAL_ID_FU_5 + offset);	/* AUDIO_TERMINAL_RADIO_TRANSMITTER Output Terminal Descriptor TERMINAL_ID_FU_5 -> TERMINAL_ID_OT_3 */
	}

	return n;
}

static unsigned UAC_AudioControlIfCircuitINRTS(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	if (WITHUSENOFU)
	{
		// Только один источник для компьютера
		n += UAC_AudioControlIT_INRTS(fill, p + n, maxsize - n, TERMINAL_ID_ITRTS_2 + offset, offset);	/* AUDIO_TERMINAL_RADIO_RECEIVER */
		n += UAC_AudioControlOT_IN(fill, p + n, maxsize - n,  TERMINAL_ID_OTRTS_4 + offset, TERMINAL_ID_ITRTS_2 + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Terminal Descriptor TERMINAL_ID_IT_2 -> TERMINAL_ID_OT_4 */
	}
	else
	{
		n += UAC_AudioControlIT_INRTS(fill, p + n, maxsize - n, TERMINAL_ID_ITRTS_2 + offset, offset);	/* AUDIO_TERMINAL_RADIO_RECEIVER */
		n += UAC_AudioFeatureUnit(fill, p + n, maxsize - n, TERMINAL_ID_FU_RTS + offset, TERMINAL_ID_ITRTS_2 + offset);	/* USB microphone Audio Feature Unit Descriptor TERMINAL_ID_IT_1 -> TERMINAL_ID_FU_RTS */
		n += UAC_AudioControlOT_IN(fill, p + n, maxsize - n,  TERMINAL_ID_OTRTS_4 + offset, TERMINAL_ID_FU_RTS + offset, offset);	/* AUDIO_TERMINAL_USB_STREAMING Terminal Descriptor TERMINAL_ID_IT_2 -> TERMINAL_ID_OT_4 */
	}

	return n;
}

static unsigned UAC_AudioControlIfCircuits(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	n += UAC_AudioControlIfCircuitIN(fill, p + n, maxsize - n, offset);	// Заполнение схемы ввода звука
	n += UAC_AudioControlIfCircuitOUT(fill, p + n, maxsize - n, offset);	// Заполнение схемы вывода звука

	return n;
}


/* USB Speaker Class-specific AC Interface Descriptor */
// Audio Control Interface Header Descriptor 
static unsigned UACINOUT_AudioControlIfHeader(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	const uint_fast8_t * coll,
	uint_fast8_t bInCollection,
	unsigned (* audiocontrolifcircuits)(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset),
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 8 + bInCollection;
	const unsigned wTotalLength = length + audiocontrolifcircuits(0, buff + length, maxsize - length, offset);
	uint_fast8_t i;
	ASSERT(maxsize >= wTotalLength);
	if (maxsize < wTotalLength)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 4.3.2 Class-Specific AC Interface Descriptor
		const uint_fast16_t bcdADC = 0x0100;	// Revision of class specification - 1.0
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;/* bDescriptorType */
		* buff ++ = AUDIO_CONTROL_HEADER;           /* bDescriptorSubtype */
		* buff ++ = LO_BYTE(bcdADC);				/* bcdADC */
		* buff ++ = HI_BYTE(bcdADC);
		* buff ++ = LO_BYTE(wTotalLength);			/* wTotalLength */
		* buff ++ = HI_BYTE(wTotalLength);
		* buff ++ = bInCollection;					/* bInCollection=2:  1 - AudioStreaming Out; 2 - AudioStreaming In*/
		for (i = 0; i < bInCollection; ++ i)
			* buff ++ = coll [i];					/* baInterfaceNr(i) */
		/* 10 bytes*/
		audiocontrolifcircuits(fill, buff, maxsize - length, offset);
	}
	return wTotalLength;

}

/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwith */
/* Interface 1, Alternate Setting 0                                             */
// USBLyzer: Interface Descriptor 0/0 Audio, 0 Endpoints
static unsigned r9fill_10(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	uint_fast8_t bInterfaceNumber,
	uint_fast8_t bAlternateSetting,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType */
		* buff ++ = bInterfaceNumber; //;                 /* bInterfaceNumber */
		* buff ++ = bAlternateSetting;								/* bAlternateSetting  - zero-based index */
		* buff ++ = 0x00;                           /* bNumEndpoints */
		* buff ++ = USB_DEVICE_CLASS_AUDIO;         /* bInterfaceClass */
		* buff ++ = AUDIO_SUBCLASS_AUDIOSTREAMING;  /* bInterfaceSubClass */
		* buff ++ = AUDIO_PROTOCOL_UNDEFINED;       /* bInterfaceProtocol - unused */
		* buff ++ = STRING_ID_y0 + offset;          /* iInterface - unused */
		/* 9 byte*/
	}
	return length;
}

/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */
/* 48000 Hz sample rate, 2 channel, stereo - for stereo signal */
/* Interface 1, Alternate Setting 1                                           */
// USBLyzer: Interface Descriptor 1/1 Audio
// USBLyzer: Interface Descriptor 1/2 Audio
static unsigned r9fill_11(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize,
	uint_fast8_t bInterfaceNumber,
	uint_fast8_t bAlternateSetting,
	uint_fast8_t offset
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType */
		* buff ++ = bInterfaceNumber;	            /* bInterfaceNumber */
		* buff ++ = bAlternateSetting;				/* bAlternateSetting  - zero-based index */
		* buff ++ = 0x01;							/* bNumEndpoints */
		* buff ++ = USB_DEVICE_CLASS_AUDIO;         /* bInterfaceClass */
		* buff ++ = AUDIO_SUBCLASS_AUDIOSTREAMING;  /* bInterfaceSubClass */
		* buff ++ = AUDIO_PROTOCOL_UNDEFINED;       /* bInterfaceProtocol - unused */
		* buff ++ = STRING_ID_z0 + offset;                    /* iInterface - unused */
		/* 9 byte*/
	}
	return length;
}

/* USB Speaker Audio Type I Format Interface Descriptor */
// USBLyzer: Audio Streaming Format Type Descriptor 
static unsigned r9fill_13(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 11;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t samplefreq1 = dsp_get_samplerateuacout();
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;		/* bDescriptorType */
		* buff ++ = AUDIO_STREAMING_FORMAT_TYPE;			/* bDescriptorSubtype */
		* buff ++ = AUDIO_FORMAT_TYPE_I;							/* bFormatType */
		* buff ++ = HARDWARE_USBD_AUDIO_OUT_CHANNELS;		/* bNrChannels */
		* buff ++ = (HARDWARE_USBD_AUDIO_OUT_SAMPLEBITS + 7) / 8; /* bSubFrameSize :  2 Bytes per frame (16bits) */
		* buff ++ = HARDWARE_USBD_AUDIO_OUT_SAMPLEBITS;		/* bBitResolution (16-bits per sample) */
		* buff ++ = 1;										/* bSamFreqType only one frequency supported */
		* buff ++ = LO_BYTE(samplefreq1);	/* Audio sampling frequency coded on 3 bytes */
		* buff ++ = HI_BYTE(samplefreq1);
		* buff ++ = HI_24BY(samplefreq1);
		/* 11 byte*/
	}
	return length;
}

/* Endpoint 1 - Standard Descriptor */
// out: from computer to our device
static unsigned r9fill_14(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress, int highspeed)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_AUDIO_PORT_DATA_SIZE_OUT);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;         /* bDescriptorType */
		* buff ++ = bEndpointAddress;               /* bEndpointAddress 1 out endpoint*/
		* buff ++ = USBD_UACOUT_EP_ATTRIBUTES;       						    /* bmAttributes */
		* buff ++ = LO_BYTE(wMaxPacketSize);              /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_AUDIO48 : FSINTERVAL_AUDIO48;    /* bInterval */
		* buff ++ = 0x00;                                 /* bRefresh */
		* buff ++ = 0;                       /* bSynchAddress */
		/* 9 byte*/
	}
	return length;
}

/* Endpoint - Audio Streaming Descriptor*/
static unsigned r9fill_15(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wLockDelay = 0;
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = AUDIO_ENDPOINT_DESCRIPTOR_TYPE; /* 0x25 bDescriptorType */
		* buff ++ = AUDIO_ENDPOINT_GENERAL;         /* 0x01 bDescriptor */
		* buff ++ = 0x00;                           /* bmAttributes */
		* buff ++ = 0x00;                           /* bLockDelayUnits */
		* buff ++ = LO_BYTE(wLockDelay);			/* wLockDelay */
		* buff ++ = HI_BYTE(wLockDelay);
		/* 07 byte*/
	}
	return length;
}

// Interface Descriptor 2/1 Audio, 1 Endpoint
static unsigned r9fill_24(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bInterfaceNumber, 
	uint_fast8_t bAlternateSetting, 
	uint_fast8_t bNumEndpoints, uint_fast8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  // INTERFACE descriptor type (bDescriptorType)
		* buff ++ = bInterfaceNumber;			// Index of this interface. (bInterfaceNumber)
		* buff ++ = bAlternateSetting;				// Index of this alternate setting. (bAlternateSetting) - zero-based index 
		* buff ++ = bNumEndpoints;					// bNumEndpoints
		* buff ++ = USB_DEVICE_CLASS_AUDIO;			// AUDIO (bInterfaceClass)
		* buff ++ = AUDIO_SUBCLASS_AUDIOSTREAMING;  // AUDIO_STREAMING (bInterfaceSubclass)
		* buff ++ = AUDIO_PROTOCOL_UNDEFINED;             /* bInterfaceProtocol */
		* buff ++ = STRING_ID_x0 + offset;					/* Unused iInterface */
		/* 9 byte*/
	}
	return length;
}

#if ! WITHRTSNOAUDIO
/* USB Microphone Type I Format Type Descriptor (CODE == 6)*/
// Audio Streaming Format Type Descriptor 
static unsigned UAC_r9fill_26_audio48(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 11;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t samplefreq1 = dsp_get_samplerateuacin_audio48();
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;// CS_INTERFACE Descriptor Type (bDescriptorType) 0x24
		* buff ++ = AUDIO_STREAMING_FORMAT_TYPE;   // FORMAT_TYPE subtype. (bDescriptorSubtype) 0x02
		* buff ++ = AUDIO_FORMAT_TYPE_I;							/* bFormatType */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_CHANNELS_AUDIO48;		/* bNrChannels */
		* buff ++ = (HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_AUDIO48 + 7) / 8; /* bSubFrameSize :  2 Bytes per frame (16bits) */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_AUDIO48;		/* bBitResolution (16-bits per sample) */
		* buff ++ = 1;										/* bSamFreqType only one frequency supported */
		* buff ++ = LO_BYTE(samplefreq1);	/* Audio sampling frequency coded on 3 bytes */
		* buff ++ = HI_BYTE(samplefreq1);
		* buff ++ = HI_24BY(samplefreq1);
	}
	return length;
}

/* USB Microphone Standard Endpoint Descriptor (CODE == 8)*/ //Standard AS Isochronous Audio Data Endpoint Descriptor
// Endpoint Descriptor 82 2 In, Isochronous, 125 us
static unsigned UAC_r9fill_27_audio48(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress, uint8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_AUDIO_PORT_DATA_SIZE_IN_AUDIO48); // was: 0x300
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	// bDescriptorType
		* buff ++ = bEndpointAddress;                    // bEndpointAddress
		* buff ++ = USBD_UACIN_EP_ATTRIBUTES; // bmAttributes
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_AUDIO48 : FSINTERVAL_AUDIO48;    /* bInterval */
		* buff ++ = 0x00;                       // Unused. (bRefresh)
		* buff ++ = 0x00;                       // Unused. (bSynchAddress)
	}
	return length;
}
#endif /* ! WITHRTSNOAUDIO */

#if WITHRTS96

/* USB Microphone Type I Format Type Descriptor (CODE == 6)*/
// Audio Streaming Format Type Descriptor 
static unsigned UAC_r9fill_26_rts96(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 11;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t samplefreq1 = dsp_get_samplerateuacin_rts96();
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;// CS_INTERFACE Descriptor Type (bDescriptorType) 0x24
		* buff ++ = AUDIO_STREAMING_FORMAT_TYPE;   // FORMAT_TYPE subtype. (bDescriptorSubtype) 0x02
		* buff ++ = AUDIO_FORMAT_TYPE_I;							/* bFormatType */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_CHANNELS_RTS;		/* bNrChannels */
		* buff ++ = (HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_RTS96 + 7) / 8; /* bSubFrameSize :  2 Bytes per frame (16bits) */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_RTS96;		/* bBitResolution (16-bits per sample) */
		* buff ++ = 1;										/* bSamFreqType only one frequency supported */
		* buff ++ = LO_BYTE(samplefreq1);	/* Audio sampling frequency coded on 3 bytes */
		* buff ++ = HI_BYTE(samplefreq1);
		* buff ++ = HI_24BY(samplefreq1);
	}
	return length;
}

/* USB Microphone Standard Endpoint Descriptor (CODE == 8)*/ //Standard AS Isochronous Audio Data Endpoint Descriptor
// Endpoint Descriptor 82 2 In, Isochronous, 125 us
static unsigned UAC_r9fill_27_rts96(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress, uint8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_AUDIO_PORT_DATA_SIZE_IN_RTS96); // was: 0x300
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	// bDescriptorType
		* buff ++ = bEndpointAddress;                    // bEndpointAddress
		* buff ++ = USBD_UACIN_EP_ATTRIBUTES; // bmAttributes
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_RTS96 : FSINTERVAL_RTS96;    /* bInterval */
		* buff ++ = 0x00;                       // Unused. (bRefresh)
		* buff ++ = 0x00;                       // Unused. (bSynchAddress)
	}
	return length;
}

#endif /* WITHRTS96 */

#if WITHRTS192

/* USB Microphone Type I Format Type Descriptor (CODE == 6)*/
// Audio Streaming Format Type Descriptor 
static unsigned UAC_r9fill_26_rts192(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 11;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t samplefreq1 = dsp_get_samplerateuacin_rts192();
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;// CS_INTERFACE Descriptor Type (bDescriptorType) 0x24
		* buff ++ = AUDIO_STREAMING_FORMAT_TYPE;   // FORMAT_TYPE subtype. (bDescriptorSubtype) 0x02
		* buff ++ = AUDIO_FORMAT_TYPE_I;							/* bFormatType */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_CHANNELS_RTS;		/* bNrChannels */
		* buff ++ = (HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_RTS192 + 7) / 8; /* bSubFrameSize :  2 Bytes per frame (16bits) */
		* buff ++ = HARDWARE_USBD_AUDIO_IN_SAMPLEBITS_RTS192;		/* bBitResolution (16-bits per sample) */
		* buff ++ = 1;										/* bSamFreqType only one frequency supported */
		* buff ++ = LO_BYTE(samplefreq1);	/* Audio sampling frequency coded on 3 bytes */
		* buff ++ = HI_BYTE(samplefreq1);
		* buff ++ = HI_24BY(samplefreq1);
	}
	return length;
}

/* USB Microphone Standard Endpoint Descriptor (CODE == 8)*/ //Standard AS Isochronous Audio Data Endpoint Descriptor
// Endpoint Descriptor 82 2 In, Isochronous, 125 us
static unsigned UAC_r9fill_27_rts192(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress, uint8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_AUDIO_PORT_DATA_SIZE_IN_RTS192); // was: 0x300
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	// bDescriptorType
		* buff ++ = bEndpointAddress;                    // bEndpointAddress
		* buff ++ = USBD_UACIN_EP_ATTRIBUTES; // bmAttributes
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_RTS192 : FSINTERVAL_RTS192;    /* bInterval */
		* buff ++ = 0x00;                       // Unused. (bRefresh)
		* buff ++ = 0x00;                       // Unused. (bSynchAddress)
	}
	return length;
}

#endif /* WITHRTS192 */

/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
static unsigned r9fill_28(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wLockDelay = 0;
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_ENDPOINT_DESCRIPTOR_TYPE;    // CS_ENDPOINT Descriptor Type (bDescriptorType) 0x25
		* buff ++ = AUDIO_ENDPOINT_GENERAL;            // GENERAL subtype. (bDescriptorSubtype) 0x01
		* buff ++ = 0x00;                              // No sampling frequency control; no pitch control; no packet padding.(bmAttributes)
		* buff ++ = 0x02;                              // bLockDelayUnits
		* buff ++ = LO_BYTE(wLockDelay);			/* wLockDelay */
		* buff ++ = HI_BYTE(wLockDelay);
	}
	return length;
}


/* USB Speaker Audio Streaming Interface Descriptor */
// USBLyzer: Audio Streaming Interface Descriptor 
// audio10.pdf: Table 4-19: Class-Specific AS Interface Descriptor
static unsigned UAC_AudioStreamingIf(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bTerminalLink)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wFormatTag = AUDIO_FORMAT_PCM;	/* wFormatTag */
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = AUDIO_INTERFACE_DESCRIPTOR_TYPE;      /* bDescriptorType CS_INTERFACE */
		* buff ++ = AUDIO_STREAMING_GENERAL;              /* bDescriptorSubtype AS_GENERAL */
		* buff ++ = bTerminalLink;                        /* bTerminalLink */
		* buff ++ = 0x01;                                 /* bDelay */
		* buff ++ = LO_BYTE(wFormatTag);                  /* wFormatTag - Audio Data Format that */
		* buff ++ = HI_BYTE(wFormatTag);
		/* 07 byte*/
	}
	return length;
}

#if WITHUSBUAC3

// UNUSED
static unsigned UAC_AudioControlIfCircuitsAUDIOandRTS(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	n += UAC_AudioControlIfCircuits(fill, p + n, maxsize - n, offset);	// Заполнение схемы ввода звука
	n += UAC_AudioControlIfCircuitINRTS(fill, p + n, maxsize - n, offset);	// Заполнение схемы ввода звука

	return n;
}

// AUDIO48 and RTS output audio function
// не используется - так как запуск HDSDR & ASIO4ALL сьивает акдиоканал
static unsigned fill_UACINOUT48andRTS_function_UNUSED(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed, uint_fast8_t offset)
{
	unsigned n = 0;
	const uint_fast8_t controlifv = INTERFACE_AUDIO_CONTROL_0;
	const uint_fast8_t mikeifv = INTERFACE_AUDIO_MIKE_2;
	const uint_fast8_t modulatorifv = INTERFACE_AUDIO_SPK_1;
	const uint_fast8_t rtsifv = INTERFACE_AUDIO_RTS_3;
	const uint_fast8_t coll [] =
	{
		modulatorifv,
		mikeifv,
		rtsifv,
	};

	const uint_fast8_t epin = USB_ENDPOINT_IN(USBD_EP_AUDIO_IN);
	const uint_fast8_t epout = USB_ENDPOINT_OUT(USBD_EP_AUDIO_OUT);
	const uint_fast8_t epinrts = USB_ENDPOINT_IN(USBD_EP_RTS_IN);

	n += UAC_InterfaceAssociationDescriptor(fill, p + n, maxsize - n, controlifv, 4, offset);	/* INTERFACE_AUDIO_CONTROL_0 Interface Association Descriptor Audio */
	// INTERFACE_AUDIO_CONTROL_0 - audio conntrol interface
	n += r9fill_3(fill, p + n, maxsize - n, controlifv, 0x00, offset);	/* INTERFACE_AUDIO_CONTROL_0 - Interface Descriptor 0/0 Audio, 0 Endpoints */
	n += UACINOUT_AudioControlIfHeader(fill, p + n, maxsize - n, coll, sizeof coll / sizeof coll [0], UAC_AudioControlIfCircuitsAUDIOandRTS, offset);	/* bcdADC Audio Control Interface Header Descriptor */

	// OUT data flow: USB Speaker
	// INTERFACE_AUDIO_SPK_1 - audio streaming interface
	n += r9fill_10(fill, p + n, maxsize - n, modulatorifv, 0x00, offset);	/* INTERFACE_AUDIO_SPK_1 - Interface 1, Alternate Setting 0 */

	n += r9fill_11(fill, p + n, maxsize - n, modulatorifv, 0x01, offset);	/* INTERFACE_AUDIO_SPK_1 -  Interface 1, Alternate Setting 1 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_IT_1 + offset);	/* USB Speaker Audio Streaming Interface Descriptor (for output TERMINAL_ID_IT_1 + offset) */
	n += r9fill_13(fill, p + n, maxsize - n);	/* USB Speaker Audio Type I Format Interface Descriptor (one sample rate) 48000 */
	n += r9fill_14(fill, p + n, maxsize - n, epout, highspeed);	/* Endpoint USBD_EP_AUDIO_OUT - Standard Descriptor */
	n += r9fill_15(fill, p + n, maxsize - n);	/* Endpoint - Audio Streaming Descriptor */
	// IN data flow: USB Microphone
	// INTERFACE_AUDIO_MIKE_2 - audio streaming interface
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_NONE, 0, offset);	/* USB Microphone Standard AS Interface Descriptor (Alt. Set. 0) (CODE == 3) */ //zero-bandwidth interface

	// IN data flow: radio RX audio data
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_AUDIO48, 1, offset);	/* INTERFACE_AUDIO_MIKE_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OT_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_audio48(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_audio48(fill, p + n, maxsize - n, highspeed, epin, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/

#if WITHRTS96 || WITHRTS192
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_NONE, 0, offset);	/* USB Microphone Standard AS Interface Descriptor (Alt. Set. 0) (CODE == 3) */ //zero-bandwidth interface

#if WITHRTS96
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_RTS96, 1, offset);	/* INTERFACE_AUDIO_RTS_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OTRTS_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts96(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts96(fill, p + n, maxsize - n, highspeed, epinrts, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS96 */

#if WITHRTS192
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_RTS192, 1, offset);	/* INTERFACE_AUDIO_RTS_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OTRTS_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts192(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts192(fill, p + n, maxsize - n, highspeed, epinrts, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS192 */

#endif /* WITHRTS96 || WITHRTS192 */

	return n;
}

// AUDIO48 only output audio function
static unsigned fill_UACINOUT48_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed, uint_fast8_t offset)
{
	unsigned n = 0;
	const uint_fast8_t controlifv = INTERFACE_AUDIO_CONTROL_0;
	const uint_fast8_t mikeifv = INTERFACE_AUDIO_MIKE_2;
	const uint_fast8_t modulatorifv = INTERFACE_AUDIO_SPK_1;
	const uint_fast8_t coll [] =
	{
		modulatorifv,
		mikeifv,
	};

	const uint_fast8_t epin = USB_ENDPOINT_IN(USBD_EP_AUDIO_IN);
	const uint_fast8_t epout = USB_ENDPOINT_OUT(USBD_EP_AUDIO_OUT);

	n += UAC_InterfaceAssociationDescriptor(fill, p + n, maxsize - n, controlifv, 3, offset);	/* INTERFACE_AUDIO_CONTROL_0 Interface Association Descriptor Audio */
	// INTERFACE_AUDIO_CONTROL_0 - audio conntrol interface
	n += r9fill_3(fill, p + n, maxsize - n, controlifv, 0x00, offset);	/* INTERFACE_AUDIO_CONTROL_0 - Interface Descriptor 0/0 Audio, 0 Endpoints */
	n += UACINOUT_AudioControlIfHeader(fill, p + n, maxsize - n, coll, sizeof coll / sizeof coll [0], UAC_AudioControlIfCircuits, offset);	/* bcdADC Audio Control Interface Header Descriptor */

	// OUT data flow: USB Speaker
	// INTERFACE_AUDIO_SPK_1 - audio streaming interface
	n += r9fill_10(fill, p + n, maxsize - n, modulatorifv, 0x00, offset);	/* INTERFACE_AUDIO_SPK_1 - Interface 1, Alternate Setting 0 */

	n += r9fill_11(fill, p + n, maxsize - n, modulatorifv, 0x01, offset);	/* INTERFACE_AUDIO_SPK_1 -  Interface 1, Alternate Setting 1 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_IT_1 + offset);	/* USB Speaker Audio Streaming Interface Descriptor (for output TERMINAL_ID_IT_1 + offset) */
	n += r9fill_13(fill, p + n, maxsize - n);	/* USB Speaker Audio Type I Format Interface Descriptor (one sample rate) 48000 */
	n += r9fill_14(fill, p + n, maxsize - n, epout, highspeed);	/* Endpoint USBD_EP_AUDIO_OUT - Standard Descriptor */
	n += r9fill_15(fill, p + n, maxsize - n);	/* Endpoint - Audio Streaming Descriptor */
	// IN data flow: USB Microphone
	// INTERFACE_AUDIO_MIKE_2 - audio streaming interface
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_NONE, 0, offset);	/* USB Microphone Standard AS Interface Descriptor (Alt. Set. 0) (CODE == 3) */ //zero-bandwidth interface

	// IN data flow: radio RX audio data
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_AUDIO48, 1, offset);	/* INTERFACE_AUDIO_MIKE_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OT_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_audio48(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_audio48(fill, p + n, maxsize - n, highspeed, epin, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - ???????????? ?????????????*/

	return n;
}

#if 1


static unsigned UAC_AudioControlIfCircuitsRTS(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t offset)
{
	unsigned n = 0;

	n += UAC_AudioControlIfCircuitINRTS(fill, p + n, maxsize - n, offset);	// Заполнение схемы ввода звука

	return n;
}

static unsigned fill_UACINRTS_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed, uint_fast8_t offset)
{
	unsigned n = 0;
	const uint_fast8_t rtscontrolifv = INTERFACE_AUDIO_CONTROL_1;
	const uint_fast8_t rtsifv = INTERFACE_AUDIO_RTS_3;

	const uint_fast8_t epinrts = USB_ENDPOINT_IN(USBD_EP_RTS_IN);

	n += UAC_InterfaceAssociationDescriptor(fill, p + n, maxsize - n, rtscontrolifv, 2, offset);	/* INTERFACE_AUDIO_CONTROL_0 Interface Association Descriptor Audio */

	// IN data flow: USB Microphone
	// INTERFACE_AUDIO_MIKE_2 - audio streaming interface
	n += r9fill_3(fill, p + n, maxsize - n, rtscontrolifv, 0x00, offset);	/* INTERFACE_AUDIO_CONTROL_2 - Interface Descriptor 0/0 Audio, 0 Endpoints */
	n += UACINOUT_AudioControlIfHeader(fill, p + n, maxsize - n, & rtsifv, 1, UAC_AudioControlIfCircuitsRTS, offset);	/* bcdADC Audio Control Interface Header Descriptor */
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_NONE, 0, offset);	/* USB Microphone Standard AS Interface Descriptor (Alt. Set. 0) (CODE == 3) */ //zero-bandwidth interface

#if WITHRTS96
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_RTS96, 1, offset);	/* INTERFACE_AUDIO_RTS_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OTRTS_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts96(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts96(fill, p + n, maxsize - n, highspeed, epinrts, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS96 */

#if WITHRTS192
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, rtsifv, UACINRTSALT_RTS192, 1, offset);	/* INTERFACE_AUDIO_RTS_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OTRTS_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts192(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts192(fill, p + n, maxsize - n, highspeed, epinrts, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS192 */
	return n;
}
#endif

#else /* WITHUSBUAC3 */

static unsigned fill_UACINOUT_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed, uint_fast8_t offset)
{
	unsigned n = 0;
	const uint_fast8_t controlifv = INTERFACE_AUDIO_CONTROL_0 + offset * INTERFACE_UAC_count;
	const uint_fast8_t mikeifv = INTERFACE_AUDIO_MIKE_2 + offset * INTERFACE_UAC_count;
	const uint_fast8_t modulatorifv = INTERFACE_AUDIO_SPK_1 + offset * INTERFACE_UAC_count;
	const uint_fast8_t coll [] =
	{
		modulatorifv,
		mikeifv,
	};
	const uint_fast8_t epin = USB_ENDPOINT_IN(USBD_EP_AUDIO_IN + offset);
	const uint_fast8_t epout = USB_ENDPOINT_OUT(USBD_EP_AUDIO_OUT + offset);

	n += UAC_InterfaceAssociationDescriptor(fill, p + n, maxsize - n, controlifv, 3, offset);	/* INTERFACE_AUDIO_CONTROL_0 Interface Association Descriptor Audio */
	// INTERFACE_AUDIO_CONTROL_0 - audio conntrol interface
	n += r9fill_3(fill, p + n, maxsize - n, controlifv, 0x00, offset);	/* INTERFACE_AUDIO_CONTROL_0 - Interface Descriptor 0/0 Audio, 0 Endpoints */
	n += UACINOUT_AudioControlIfHeader(fill, p + n, maxsize - n, coll, sizeof coll / sizeof coll [0], UAC_AudioControlIfCircuits, offset);	/* bcdADC Audio Control Interface Header Descriptor */

	// OUT data flow: USB Speaker
	// INTERFACE_AUDIO_SPK_1 - audio streaming interface
	n += r9fill_10(fill, p + n, maxsize - n, modulatorifv, 0x00, offset);	/* INTERFACE_AUDIO_SPK_1 - Interface 1, Alternate Setting 0 */

	n += r9fill_11(fill, p + n, maxsize - n, modulatorifv, 0x01, offset);	/* INTERFACE_AUDIO_SPK_1 -  Interface 1, Alternate Setting 1 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_IT_1 + offset);	/* USB Speaker Audio Streaming Interface Descriptor (for output TERMINAL_ID_IT_1 + offset) */
	n += r9fill_13(fill, p + n, maxsize - n);	/* USB Speaker Audio Type I Format Interface Descriptor (one sample rate) 48000 */
	n += r9fill_14(fill, p + n, maxsize - n, epout, highspeed);	/* Endpoint USBD_EP_AUDIO_OUT - Standard Descriptor */
	n += r9fill_15(fill, p + n, maxsize - n);	/* Endpoint - Audio Streaming Descriptor */
	// IN data flow: USB Microphone
	// INTERFACE_AUDIO_MIKE_2 - audio streaming interface
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_NONE, 0, offset);	/* USB Microphone Standard AS Interface Descriptor (Alt. Set. 0) (CODE == 3) */ //zero-bandwidth interface

#if ! WITHRTSNOAUDIO
	// IN data flow: radio RX audio data
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_AUDIO48, 1, offset);	/* INTERFACE_AUDIO_MIKE_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OT_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_audio48(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_audio48(fill, p + n, maxsize - n, highspeed, epin, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* ! WITHRTSNOAUDIO */

#if WITHRTS96
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_RTS96, 1, offset);	/* INTERFACE_AUDIO_MIKE_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OT_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts96(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts96(fill, p + n, maxsize - n, highspeed, epin, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS96 */

#if WITHRTS192
	// IN data flow: radio RX specrum data
	n += r9fill_24(fill, p + n, maxsize - n, mikeifv, UACINALT_RTS192, 1, offset);	/* INTERFACE_AUDIO_MIKE_2 Interface Descriptor 2/1 Audio, 1 Endpoint, bAlternateSetting=0x01 */
	n += UAC_AudioStreamingIf(fill, p + n, maxsize - n, TERMINAL_ID_OT_4 + offset);	/* USB Microphone Class-specific AS General Interface Descriptor (for output TERMINAL_ID_OT_4) (CODE == 5) */
	n += UAC_r9fill_26_rts192(fill, p + n, maxsize - n);		/* USB Microphone Type I Format Type Descriptor (CODE == 6) 48000 */
	n += UAC_r9fill_27_rts192(fill, p + n, maxsize - n, highspeed, epin, offset);	/* Endpoint Descriptor USBD_EP_AUDIO_IN In, Isochronous, 125 us */
	n += r9fill_28(fill, p + n, maxsize - n);	/* USB Microphone Class-specific Isoc. Audio Data Endpoint Descriptor (CODE == 7) OK - подтверждено документацией*/
#endif /* WITHRTS192 */
	return n;
}
#endif /* WITHUSBUAC3 */

#endif /* WITHUSBUAC */

#if WITHUSBCDC
/* CDC IAD */

// ISBLyzer: Interface Association Descriptor Abstract Control Model
// documented in USB ECN : Interface Association Descriptor - InterfaceAssociationDescriptor_ecn.pdf
static unsigned CDCACM_InterfaceAssociationDescriptor_a(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t offset)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 0x02/0x02/0x01 - cdc
		// 0x02/0x0c/0x07 - CDC Ethernet Emulation Model
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE;	// bDescriptorType: IAD
		* buff ++ = INTERFACE_CDC_CONTROL_3a + offset * INTERFACE_CDCACM_count;	// bFirstInterface
		* buff ++ = INTERFACE_CDCACM_count;	// bInterfaceCount
		* buff ++ = USB_DEVICE_CLASS_COMMUNICATIONS;	// bFunctionClass: CDC
		* buff ++ = CDC_ABSTRACT_CONTROL_MODEL;			// bFunctionSubClass
		* buff ++ = CDC_PROTOCOL_COMMON_AT_COMMANDS;	// bFunctionProtocol
		* buff ++ = STRING_ID_4a + offset;				// iFunction - Storch HF TRX CAT - появляется, если сделать не тот bFunctionSubClass
	}
	return length;
}

/*Interface Descriptor*/
// USBLyzer: Interface Descriptor 3/0 CDC Control, 1 Endpoint 
static unsigned CDCACM_InterfaceDescriptorControlIf_a(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType: Interface */  /* Interface descriptor type */
		* buff ++ = INTERFACE_CDC_CONTROL_3a + offset * INTERFACE_CDCACM_count;   /* bInterfaceNumber: Number of Interface */
		* buff ++ = 0;		/* bAlternateSetting: Alternate setting  - zero-based index */
		* buff ++ = 0x01;   /* bNumEndpoints: One endpoints used (interrupt type) */
		* buff ++ = CDC_COMMUNICATION_INTERFACE_CLASS;   /* bInterfaceClass: Communication Interface Class */
		* buff ++ = 0x02;   /* bInterfaceSubClass: Abstract Control Model */
		* buff ++ = 0x01;   /* bInterfaceProtocol: Common AT commands */
		* buff ++ = STRING_ID_0;   /* iInterface */
	}
	return length;
}

/* Call Managment Functional Descriptor */
// Call Management Functional Descriptor 
static unsigned CDCACM_r9fill_32_a(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t offset)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CS_INTERFACE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x01;   /* bDescriptorSubtype: Call Management Func Desc */
		* buff ++ = 0x00;   /* bmCapabilities: D0+D1 */
		* buff ++ = INTERFACE_CDC_DATA_4a + offset * INTERFACE_CDCACM_count;   /* bDataInterface: Zero based index of the interface in this configuration.(bInterfaceNum) */
	}
	return length;
}

/* Union Functional Descriptor */
// Union Functional Descriptor 
static unsigned CDC_UnionFunctionalDescriptor_a(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t offset)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bFunctionLength */
		* buff ++ = CS_INTERFACE;				/* bDescriptorType: CS_INTERFACE */
		* buff ++ = CDC_UNION;						/* bDescriptorSubtype: Union func desc */
		* buff ++ = INTERFACE_CDC_CONTROL_3a + offset * INTERFACE_CDCACM_count;	/* bMasterInterface: Communication class interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
		* buff ++ = INTERFACE_CDC_DATA_4a + offset * INTERFACE_CDCACM_count;		/* bSlaveInterface0: Data Class Interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
	}
	return length;
}

/* Data class interface descriptor*/
// USBLyzer: Interface Descriptor 4/0 CDC Data, 2 Endpoints
static unsigned CDC_InterfaceDescriptorDataIf_a(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t offset)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType: */
		* buff ++ = INTERFACE_CDC_DATA_4a + offset * INTERFACE_CDCACM_count;   /* bInterfaceNumber: Number of Interface */
		* buff ++ = 0;		/* bAlternateSetting: Alternate setting  - zero-based index  */
		* buff ++ = 0x02;   /* bNumEndpoints: Two endpoints used: data in and data out */
		* buff ++ = CDC_DATA_INTERFACE_CLASS;   /* bInterfaceClass: CDC */
		* buff ++ = 0x00;   /* bInterfaceSubClass: */
		* buff ++ = 0x00;   /* bInterfaceProtocol: */
		* buff ++ = STRING_ID_0;   /* iInterface: */
	}
	return length;
}

/* Header Functional Descriptor */
static unsigned r9fill_31(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{ 
		const uint_fast16_t bcdCDC = CDC_V1_10;	/* bcdCDC: spec release number */
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CDC_INTERFACE_DESCRIPTOR_TYPE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x00;   /* bDescriptorSubtype: Header Func Desc */
		* buff ++ = LO_BYTE(bcdCDC);			/* bcdCDC: spec release number */
		* buff ++ = HI_BYTE(bcdCDC);
	}
	return length;
}

/* ACM Functional Descriptor */
// Abstract Control Management Functional Descriptor
static unsigned r9fill_33(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 4;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		// defined in PSTN120.pdf 5.3.2 Abstract Control Management Functional Descriptor 
		* buff ++ = length;						  /* bLength */
		* buff ++ = CS_INTERFACE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x02;   /* bDescriptorSubtype: Abstract Control Management desc */
		* buff ++ = 0x02;   /* bmCapabilities 0x02: Line Coding requests and Serial State notification supported */
	}
	return length;
}

/* Endpoint 3 Descriptor */
// Endpoint Descriptor 86 6 In, Interrupt
static unsigned r9fill_35(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_COM_PORT_INT_SIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE; 	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;			/* bEndpointAddress: (IN) */
		* buff ++ = USB_ENDPOINT_TYPE_INTERRUPT;   	/* bmAttributes: Interrupt */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? 0x10 : 0xFF;   						/* bInterval: 255 mS */
	}
	return length;
}

/*Endpoint 2 OUT Descriptor*/
// Endpoint Descriptor 03 3 Out, Bulk, 64 bytes
static unsigned r9fill_37(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_COM_PORT_DATA_SIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;   /* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (OUT2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

/*Endpoint 2 IN Descriptor*/
// Endpoint Descriptor 84 4 In, Bulk, 64 bytes
static unsigned r9fill_38(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(VIRTUAL_COM_PORT_DATA_SIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (IN2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

static unsigned fill_CDCACM_function_a(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed, uint_fast8_t offset)
{
	unsigned n = 0;
	const uint8_t inep = USBD_EP_CDC_IN + offset;
	const uint8_t outnep = USBD_EP_CDC_OUT + offset;
	const uint8_t intnep = USBD_EP_CDC_INT + offset;

	// CDC
	n += CDCACM_InterfaceAssociationDescriptor_a(fill, p + n, maxsize - n, offset);	/* CDC: Interface Association Descriptor Abstract Control Model */
	n += CDCACM_InterfaceDescriptorControlIf_a(fill, p + n, maxsize - n, offset);	/* INTERFACE_CDC_CONTROL_3a Interface Descriptor 3/0 CDC Control, 1 Endpoint */
	n += r9fill_31(fill, p + n, maxsize - n);	/* Header Functional Descriptor*/
	n += CDCACM_r9fill_32_a(fill, p + n, maxsize - n, offset);	/* Call Managment Functional Descriptor*/
	n += r9fill_33(fill, p + n, maxsize - n);	/* ACM Functional Descriptor */
	n += CDC_UnionFunctionalDescriptor_a(fill, p + n, maxsize - n, offset);	/* Union Functional Descriptor INTERFACE_CDC_CONTROL_3a & INTERFACE_CDC_DATA_4a */
	n += r9fill_35(fill, p + n, maxsize - n, highspeed, USB_ENDPOINT_IN(intnep));	/* Endpoint Descriptor 86 6 In, Interrupt */

	n += CDC_InterfaceDescriptorDataIf_a(fill, p + n, maxsize - n, offset);	/* INTERFACE_CDC_DATA_4a Data class interface descriptor */
	n += r9fill_37(fill, p + n, maxsize - n, USB_ENDPOINT_OUT(outnep));	/* Endpoint Descriptor USBD_EP_CDC_OUT Out, Bulk, 64 bytes */
	n += r9fill_38(fill, p + n, maxsize - n, USB_ENDPOINT_IN(inep));	/* Endpoint Descriptor USBD_EP_CDC_IN In, Bulk, 64 bytes */

	return n;
}

#endif /* WITHUSBCDC */

#if WITHUSBCDCEEM

static unsigned CDCEEM_InterfaceAssociationDescriptor(
	uint_fast8_t fill, 
	uint8_t * buff, 
	unsigned maxsize
	)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 0x02/0x02/0x01 - cdc
		// 0x02/0x0c/0x07 - CDC Ethernet Emulation Model
		// http://blog.metrotek.spb.ru/2011/07/07/usb-set-na-cortex-m3/

		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE;	// bDescriptorType: IAD
		* buff ++ = INTERFACE_CDCEEM_DATA_6;				// bFirstInterface
		* buff ++ = INTERFACE_CDCEEM_count;	// bInterfaceCount
		* buff ++ = USB_DEVICE_CLASS_COMMUNICATIONS;	// bFunctionClass: CDC
		* buff ++ = CDC_ETHERNET_EMULATION_MODEL;		// bFunctionSubClass - Ethernet Networking
		* buff ++ = 0x07;						// bFunctionProtocol
		* buff ++ = STRING_ID_5;				// iFunction - CDC Ethernet Control Model (EEM)
	}
	return length;
}

// Информация о типе требуемого драйвера берется отсюда по кодам в bInterfaceClass, bInterfaceSubclass, bInterfaceProtocol
static unsigned CDCEEM_r9fill_24(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bInterfaceNumber, 
	uint_fast8_t bAlternateSetting, 
	uint_fast8_t bNumEndpoints
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 0x02/0x0c/0x07 - CDC Ethernet Emulation Model
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;    // INTERFACE descriptor type (bDescriptorType) 0x04
		* buff ++ = bInterfaceNumber; // Index of this interface. (bInterfaceNumber) ?????????? (3<) (1<<) (1<M)
		* buff ++ = bAlternateSetting;				// 0 Index of this alternate setting. (bAlternateSetting) - zero-based index 
		* buff ++ = bNumEndpoints;					// bNumEndpoints
		* buff ++ = 0x02;							// bInterfaceClass
		* buff ++ = CDC_ETHERNET_EMULATION_MODEL;	/* bInterfaceSubclass */
		* buff ++ = 0x07;							/* bInterfaceProtocol */
		* buff ++ = STRING_ID_5;					/* Unused iInterface */
		/* 9 byte*/
	}
	return length;
}

// Endpoint Descriptor
static unsigned CDCEEM_r9fill_37(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(USBD_CDCEEM_BUFSIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;   /* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (OUT2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x01;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

/*Endpoint 2 IN Descriptor*/
// Endpoint Descriptor 84 4 In, Bulk, 64 bytes
static unsigned CDCEEM_r9fill_38(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(USBD_CDCEEM_BUFSIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (IN2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x01;    						/* bInterval: ignore for full speed Bulk endpoints */
	}
	return length;
}

/* CDC Ethernet Emulation Model */
static unsigned fill_CDCEEM_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed)
{
	unsigned n = 0;

	// iadclasscode_r10.pdf
	// InterfaceAssociationDescriptor требуется только для многоинтерфейсных 
	// Провда, там написано что iadclasscode_r10.pdf
	n += CDCEEM_InterfaceAssociationDescriptor(fill, p + n, maxsize - n);	/* CDC EEM: Interface Association Descriptor Abstract Control Model */
	n += CDCEEM_r9fill_24(fill, p + n, maxsize - n, INTERFACE_CDCEEM_DATA_6, 0x00, 2);	/* INTERFACE_CDCEEM_DATA_6 Data class interface descriptor */
	n += CDCEEM_r9fill_38(fill, p + n, maxsize - n, USB_ENDPOINT_IN(USBD_EP_CDCEEM_IN));	/* Endpoint Descriptor USBD_EP_CDCECM_IN In, Bulk, 64 bytes */
	n += CDCEEM_r9fill_37(fill, p + n, maxsize - n, USB_ENDPOINT_OUT(USBD_EP_CDCEEM_OUT));	/* Endpoint Descriptor USBD_EP_CDCECM_OUT Out, Bulk, 64 bytes */

	return n;
}

#endif /* WITHUSBCDCEEM */

#if WITHUSBCDCECM

// ISBLyzer: Interface Association Descriptor Abstract Control Model
// documented in USB ECN : Interface Association Descriptor - InterfaceAssociationDescriptor_ecn.pdf
static unsigned CDCECM_InterfaceAssociationDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// 0x02/0x02/0x01 - cdc
		// 0x02/0x0c/0x07 - CDC Ethernet Emulation Model
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE;	// bDescriptorType: IAD
		* buff ++ = INTERFACE_CDCECM_CONTROL_5;				// bFirstInterface
		* buff ++ = INTERFACE_CDCECM_count;	// bInterfaceCount
		* buff ++ = USB_DEVICE_CLASS_COMMUNICATIONS;	// bFunctionClass: CDC
		* buff ++ = CDC_ETHERNET_NETWORKING_CONTROL_MODEL;						// bFunctionSubClass - Ethernet Networking
		* buff ++ = 0x00;						// bFunctionProtocol
		* buff ++ = STRING_ID_5a;				// iFunction - CDC Ethernet Control Model (ECM)
	}
	return length;
}


/*Interface Descriptor*/
// USBLyzer: Interface Descriptor 3/0 CDC Control, 1 Endpoint 
static unsigned CDCECM_InterfaceDescriptorControlIf(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType: Interface */  /* Interface descriptor type */
		* buff ++ = INTERFACE_CDCECM_CONTROL_5;   /* bInterfaceNumber: Number of Interface */
		* buff ++ = 0;		/* bAlternateSetting: Alternate setting  - zero-based index */
		* buff ++ = 0x01;   /* bNumEndpoints: One endpoints used (interrupt type) */
		* buff ++ = CDC_COMMUNICATION_INTERFACE_CLASS;   /* bInterfaceClass: Communication Interface Class */
		* buff ++ = CDC_ETHERNET_NETWORKING_CONTROL_MODEL;   /* bInterfaceSubClass: Ethernet Networking */
		* buff ++ = 0x00;   /* bInterfaceProtocol */
		* buff ++ = STRING_ID_0;   /* iInterface */
	}
	return length;
}


/* Union Functional Descriptor */
// Union Functional Descriptor 
static unsigned CDCECM_UnionFunctionalDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bFunctionLength */
		* buff ++ = CS_INTERFACE;				/* bDescriptorType: CS_INTERFACE */
		* buff ++ = CDC_UNION;						/* bDescriptorSubtype: Union func desc */
		* buff ++ = INTERFACE_CDCECM_CONTROL_5;	/* bMasterInterface: Communication class interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
		* buff ++ = INTERFACE_CDCECM_DATA_6;	/* bSlaveInterface0: Data Class Interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
	}
	return length;
}

// Ethernet Networking Functional Descriptor
static unsigned CDCECM_EthernetNetworkingFunctionalDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 13;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t bmEthernetStatistics = 0;
		const uint_fast16_t wMaxSegmentSize = 1514;
		const uint_fast16_t wNumberMCFilters = 0;
		const uint_fast8_t bNumberPowerFilters = 0;

		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bFunctionLength */
		* buff ++ = CS_INTERFACE;				/* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x0F;						/* bDescriptorSubtype: Ethernet Networking Functional Descriptor */
		* buff ++ = STRING_ID_MACADDRESS;		/* iMacAddress */
		* buff ++ = LO_BYTE(bmEthernetStatistics);	/* bmEthernetStatistics */
		* buff ++ = HI_BYTE(bmEthernetStatistics);
		* buff ++ = HI_24BY(bmEthernetStatistics);
		* buff ++ = HI_32BY(bmEthernetStatistics);
		* buff ++ = LO_BYTE(wMaxSegmentSize);	/* wMaxSegmentSize */
		* buff ++ = HI_BYTE(wMaxSegmentSize);
		* buff ++ = LO_BYTE(wNumberMCFilters);	/* wNumberMCFilters */
		* buff ++ = HI_BYTE(wNumberMCFilters);
		* buff ++ = bNumberPowerFilters;	/* bNumberPowerFilters */
	}
	return length;
}


/* Header Functional Descriptor */
static unsigned CDCECM_r9fill_31(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{ 
		const uint_fast16_t bcdCDC = CDC_V1_10;	/* bcdCDC: spec release number */
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CDC_INTERFACE_DESCRIPTOR_TYPE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x00;   /* bDescriptorSubtype: Header Func Desc */
		* buff ++ = LO_BYTE(bcdCDC);			/* bcdCDC: spec release number */
		* buff ++ = HI_BYTE(bcdCDC);
	}
	return length;
}

/* Endpoint 3 Descriptor */
// Endpoint Descriptor 86 6 In, Interrupt

static unsigned CDCECM_r9fill_35(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(CDCECM_NOTIFICATION_IN_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE; 	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;			/* bEndpointAddress: (IN) */
		* buff ++ = USB_ENDPOINT_TYPE_INTERRUPT;   	/* bmAttributes: Interrupt */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_32MS : FSINTERVAL_32MS;   		/* bInterval: 32 mS */
	}
	return length;
}

// Endpoint Descriptor
static unsigned CDCECM_r9fill_37(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(CDCECM_DATA_OUT_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;   /* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (OUT2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

/*Endpoint 2 IN Descriptor*/
// Endpoint Descriptor 84 4 In, Bulk, 64 bytes
static unsigned CDCECM_r9fill_38(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(CDCECM_DATA_IN_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (IN2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

static unsigned CDCECM_InterfaceDescriptorDataIf(
	uint_fast8_t fill, uint8_t * buff, unsigned maxsize, 
	uint_fast8_t bInterfaceNumber, 
	uint_fast8_t bAlternateSetting, 
	uint_fast8_t bNumEndpoints
	)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;    // INTERFACE descriptor type (bDescriptorType) 0x04
		* buff ++ = bInterfaceNumber; // Index of this interface. (bInterfaceNumber) ?????????? (3<) (1<<) (1<M)
		* buff ++ = bAlternateSetting;				// 0 Index of this alternate setting. (bAlternateSetting) - zero-based index 
		* buff ++ = bNumEndpoints;							// 2 endpoints.   (bNumEndpoints)
		* buff ++ = CDC_DATA_INTERFACE_CLASS;		// 10 CDC Data (bInterfaceClass)
		* buff ++ = 0x00;							// bInterfaceSubclass)
		* buff ++ = 0x00;             /* 0 bInterfaceProtocol */
		* buff ++ = STRING_ID_0;                   /* 0 Unused iInterface */
		/* 9 byte*/
	}
	return length;
}

/* CDC Ethernet Control Model */
static unsigned fill_CDCECM_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed)
{
	unsigned n = 0;

	n += CDCECM_InterfaceAssociationDescriptor(fill, p + n, maxsize - n);	/* CDC: Interface Association Descriptor Abstract Control Model */
	n += CDCECM_InterfaceDescriptorControlIf(fill, p + n, maxsize - n);	/* INTERFACE_CDC_CONTROL_3a Interface Descriptor 3/0 CDC Control, 1 Endpoint */
	n += CDCECM_r9fill_31(fill, p + n, maxsize - n);	/* Header Functional Descriptor*/
	n += CDCECM_UnionFunctionalDescriptor(fill, p + n, maxsize - n);	/* Union Functional Descriptor INTERFACE_CDC_CONTROL_3a & INTERFACE_CDC_DATA_4a */
	n += CDCECM_EthernetNetworkingFunctionalDescriptor(fill, p + n, maxsize - n);	/* Union Functional Descriptor INTERFACE_CDC_CONTROL_3a & INTERFACE_CDC_DATA_4a */
	n += CDCECM_r9fill_35(fill, p + n, maxsize - n, highspeed, USB_ENDPOINT_IN(USBD_EP_CDCECM_INT));	/* Endpoint Descriptor 86 6 In, Interrupt */

	n += CDCECM_InterfaceDescriptorDataIf(fill, p + n, maxsize - n, INTERFACE_CDCECM_DATA_6, 0x00, 0);	/* INTERFACE_CDCECM_DATA_6 Data class interface descriptor */

	n += CDCECM_InterfaceDescriptorDataIf(fill, p + n, maxsize - n, INTERFACE_CDCECM_DATA_6, 0x01, 2);	/* INTERFACE_CDCECM_DATA_6 Data class interface descriptor */
	n += CDCECM_r9fill_37(fill, p + n, maxsize - n, USB_ENDPOINT_OUT(USBD_EP_CDCECM_OUT));	/* Endpoint Descriptor USBD_EP_CDCECM_OUT Out, Bulk, 64 bytes */
	n += CDCECM_r9fill_38(fill, p + n, maxsize - n, USB_ENDPOINT_IN(USBD_EP_CDCECM_IN));	/* Endpoint Descriptor USBD_EP_CDCECM_IN In, Bulk, 64 bytes */

	return n;
}

#endif /* WITHUSBCDCECM */

#if WITHUSBRNDIS


// Interface Association Descriptor RF Controller
// documented in USB ECN : Interface Association Descriptor - InterfaceAssociationDescriptor_ecn.pdf
static unsigned RNDIS_InterfaceAssociationDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 8;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_ASSOC_DESCRIPTOR_TYPE;	// bDescriptorType: IAD
		* buff ++ = INTERFACE_RNDIS_CONTROL_5;				// bFirstInterface
		* buff ++ = INTERFACE_RNDIS_count;	// bInterfaceCount
		* buff ++ = USB_DEVICE_CLASS_WIRELESS_CONTROLLER;	// bFunctionClass: CDC
		* buff ++ = 0x01;						// bFunctionSubClass - RF Controller
		* buff ++ = 0x03;						// bFunctionProtocol - Remote NDIS
		* buff ++ = STRING_ID_RNDIS;			// iFunction - Remote NDIS
	}
	return length;
}


/*Interface Descriptor*/
// Interface Descriptor 0/0 Wireless Controller, 1 Endpoint
//  Communication Class INTERFACE descriptor          
// https://msdn.microsoft.com/en-US/library/ee485851(v=winembedded.60).aspx
static unsigned RNDIS_InterfaceDescriptorControlIf(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType: Interface */  /* Interface descriptor type */
		* buff ++ = INTERFACE_RNDIS_CONTROL_5;   /* bInterfaceNumber: Number of Interface */
		* buff ++ = 0;		/* bAlternateSetting: Alternate setting  - zero-based index */
		* buff ++ = 0x01;   /* bNumEndpoints: One endpoints used (interrupt type) */
		* buff ++ = USB_DEVICE_CLASS_WIRELESS_CONTROLLER;	/* bInterfaceClass: Wireless Controller */
		* buff ++ = 0x01;						// bFunctionSubClass - RF Controller
		* buff ++ = 0x03;   /* bInterfaceProtocol - Remote NDIS */
		* buff ++ = STRING_ID_0;   /* iInterface */
	}
	return length;
}

/* Header Functional Descriptor */
static unsigned RNDIS_r9fill_31(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{ 
		const uint_fast16_t bcdCDC = CDC_V1_10;	/* bcdCDC: spec release number */
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CDC_INTERFACE_DESCRIPTOR_TYPE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x00;   /* bDescriptorSubtype: Header Func Desc */
		* buff ++ = LO_BYTE(bcdCDC);			/* bcdCDC: spec release number */
		* buff ++ = HI_BYTE(bcdCDC);
	}
	return length;
}


/* Call Managment Functional Descriptor */
// Call Management Functional Descriptor 
static unsigned RNDIS_r9fill_32(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = CS_INTERFACE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x01;   /* bDescriptorSubtype: Call Management Func Desc */
		* buff ++ = 0x00;   /* bmCapabilities: D0+D1 */
		* buff ++ = INTERFACE_RNDIS_DATA_6;   /* bDataInterface: Zero based index of the interface in this configuration.(bInterfaceNum) */
	}
	return length;
}

// Abstract Control Management Functional Descriptor
static unsigned RNDIS_r9fill_33(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 4;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		// defined in PSTN120.pdf 5.3.2 Abstract Control Management Functional Descriptor 
		* buff ++ = length;						  /* bLength */
		* buff ++ = CS_INTERFACE;   /* bDescriptorType: CS_INTERFACE */
		* buff ++ = 0x02;   /* bDescriptorSubtype: Abstract Control Management desc */
		* buff ++ = 0x00;   /* bmCapabilities 0x00: Requests/notifications not supported */
	}
	return length;
}


/* Union Functional Descriptor */
// Union Functional Descriptor 
static unsigned RNDIS_UnionFunctionalDescriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 5;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bFunctionLength */
		* buff ++ = CS_INTERFACE;				/* bDescriptorType: CS_INTERFACE */
		* buff ++ = CDC_UNION;						/* bDescriptorSubtype: Union func desc */
		* buff ++ = INTERFACE_RNDIS_CONTROL_5;	/* bMasterInterface: Communication class interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
		* buff ++ = INTERFACE_RNDIS_DATA_6;	/* bSlaveInterface0: Data Class Interface -  Zero based index of the interface in this configuration (bInterfaceNum) */
	}
	return length;
}

/* Endpoint 3 Descriptor */
// Endpoint Descriptor 86 6 In, Interrupt

static unsigned RNDIS_r9fill_35(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(RNDIS_NOTIFICATION_IN_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE; 	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;			/* bEndpointAddress: (IN) */
		* buff ++ = USB_ENDPOINT_TYPE_INTERRUPT;   	/* bmAttributes: Interrupt */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? HSINTERVAL_32MS : FSINTERVAL_32MS;   		/* bInterval: 32 mS */
	}
	return length;
}

/* Data class interface descriptor*/
// USBLyzer: Interface Descriptor 1/0 CDC Data, 2 Endpoints
static unsigned RNDIS_InterfaceDescriptorDataIf(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						  /* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;  /* bDescriptorType: */
		* buff ++ = INTERFACE_RNDIS_DATA_6;			 /* bInterfaceNumber: Number of Interface */
		* buff ++ = 0;		/* bAlternateSetting: Alternate setting  - zero-based index  */
		* buff ++ = 0x02;   /* bNumEndpoints: Two endpoints used: data in and data out */
		* buff ++ = CDC_DATA_INTERFACE_CLASS;   /* bInterfaceClass: CDC */
		* buff ++ = 0x00;   /* bInterfaceSubClass: */
		* buff ++ = 0x00;   /* bInterfaceProtocol: */
		* buff ++ = STRING_ID_0;   /* iInterface: */
	}
	return length;
}

/*Endpoint 2 IN Descriptor*/
// Endpoint Descriptor 84 4 In, Bulk, 64 bytes
static unsigned RNDIS_r9fill_38(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(RNDIS_DATA_IN_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (IN2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}

// Endpoint Descriptor
static unsigned RNDIS_r9fill_37(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(RNDIS_DATA_OUT_SZ);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE;   /* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;				/* bEndpointAddress: (OUT2) */
		* buff ++ = USB_ENDPOINT_TYPE_BULK;   		/* bmAttributes: Bulk */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = 0x00;    						/* bInterval: ignore for Bulk transfer */
	}
	return length;
}


/* CDC Ethernet Control Model */
static unsigned fill_RNDIS_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed)
{
	unsigned n = 0;
	// Interface Association Descriptor RF Controller
	// Configuration descriptor           https://msdn.microsoft.com/en-US/library/ee482887(v=winembedded.60).aspx
	n += RNDIS_InterfaceAssociationDescriptor(fill, p + n, maxsize - n);	/* CDC: Interface Association Descriptor Abstract Control Model */
	// Interface Descriptor 0/0 Wireless Controller, 1 Endpoint
	//  Communication Class INTERFACE descriptor          https://msdn.microsoft.com/en-US/library/ee485851(v=winembedded.60).aspx
	n += RNDIS_InterfaceDescriptorControlIf(fill, p + n, maxsize - n);	/* INTERFACE_CDC_CONTROL_3a Interface Descriptor 3/0 CDC Control, 1 Endpoint */
	//  Functional Descriptors for Communication Class Interface per RNDIS spec.
	// Header Functional Descriptor
	n += RNDIS_r9fill_31(fill, p + n, maxsize - n);	// TODO: not need, not documented for this device class
	// Call Management Functional Descriptor
	n += RNDIS_r9fill_32(fill, p + n, maxsize - n);
	// Abstract Control Management Functional Descriptor
	n += RNDIS_r9fill_33(fill, p + n, maxsize - n);
	// Union Functional Descriptor
	n += RNDIS_UnionFunctionalDescriptor(fill, p + n, maxsize - n);
	// Endpoint descriptors for Communication Class Interface     https://msdn.microsoft.com/en-US/library/ee482509(v=winembedded.60).aspx
	n += RNDIS_r9fill_35(fill, p + n, maxsize - n, highspeed, USB_ENDPOINT_IN(RNDIS_NOTIFICATION_IN_EP));	/* Endpoint Descriptor 86 6 In, Interrupt */
	//  Data Class INTERFACE descriptor           https://msdn.microsoft.com/en-US/library/ee481260(v=winembedded.60).aspx
	n += RNDIS_InterfaceDescriptorDataIf(fill, p + n, maxsize - n);	/* INTERFACE_CDC_DATA_4a Data class interface descriptor */
	// IN Endpoint descriptor     https://msdn.microsoft.com/en-US/library/ee484483(v=winembedded.60).aspx
	n += RNDIS_r9fill_38(fill, p + n, maxsize - n, USB_ENDPOINT_IN(RNDIS_DATA_IN_EP));	/* Endpoint Descriptor USBD_EP_CDCECM_IN In, Bulk, 64 bytes */
	// OUT Endpoint descriptor     https://msdn.microsoft.com/en-US/library/ee482464(v=winembedded.60).aspx
	n += RNDIS_r9fill_37(fill, p + n, maxsize - n, USB_ENDPOINT_OUT(RNDIS_DATA_OUT_EP));	/* Endpoint Descriptor USBD_EP_CDCECM_OUT Out, Bulk, 64 bytes */
	return n;
	}

#endif /* WITHUSBRNDIS */

#if WITHUSBHID

static unsigned HID_InterfaceDescriptorXXXX(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;								/* bLength */
		* buff ++ = USB_INTERFACE_DESCRIPTOR_TYPE;		/* bDescriptorType: */
		* buff ++ = INTERFACE_HID_CONTROL_7;			// bInterfaceNumber
		* buff ++ = 0x00;								/* bAlternateSetting */
		* buff ++ = 0;//0x01;								/* bNumEndpoints */
		* buff ++ = USB_DEVICE_CLASS_HUMAN_INTERFACE;   /* bInterfaceClass */
		* buff ++ = 0;//0x01;								/* bInterfaceSubClass = boot interfsce */
		* buff ++ = 0;//0x02;							    /* bInterfaceProtocol: 1 = keyboard, 2 = mouse */
		* buff ++ = 0;//STRING_ID_HIDa;						/* iInterface */
	}
	return length;
}

static unsigned HID_Descriptor(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, unsigned wDescriptorLength)
{
	const uint_fast8_t length = 9;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t bcdHID = 0x0111;	// Revision of class specification - 1.11
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;					/* bLength */
		* buff ++ = HID_DESCRIPTOR_TYPE;	/* bDescriptorType = HID */
		* buff ++ = LO_BYTE(bcdHID);		/* bcdHID */
		* buff ++ = HI_BYTE(bcdHID);
		* buff ++ = 0x00;					/* bCountryCode */
		* buff ++ = 1;						/* bNumDescriptors=1 */
		* buff ++ = HID_REPORT_DESC;		/* bDescriptorType: 0x22: report */
		* buff ++ = LO_BYTE(wDescriptorLength);			/* wTotalLength */
		* buff ++ = HI_BYTE(wDescriptorLength);
		/* 9 bytes*/
	}
	return length;

}

#if 0
// Endpoint Descriptor In, Interrupt
static unsigned fill_HID_Mouse_IntEP(uint_fast8_t fill, uint8_t * buff, unsigned maxsize, int highspeed, uint_fast8_t bEndpointAddress)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast16_t wMaxPacketSize = encodeMaxPacketSize(HIDMOUSE_INT_DATA_SIZE);
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;							/* bLength */
		* buff ++ = USB_ENDPOINT_DESCRIPTOR_TYPE; 	/* bDescriptorType: Endpoint */
		* buff ++ = bEndpointAddress;			/* bEndpointAddress: (IN) */
		* buff ++ = USB_ENDPOINT_TYPE_INTERRUPT;   	/* bmAttributes: Interrupt */
		* buff ++ = LO_BYTE(wMaxPacketSize);        /* wMaxPacketSize */
		* buff ++ = HI_BYTE(wMaxPacketSize); 
		* buff ++ = highspeed ? 0x07 : 0x0A;   						/* bInterval: 10 mS */
	}
	return length;
}
#endif

// HID Report Descriptor
// mouse
static const uint8_t HID_report_desc_mouse []=
{
	0x05, 0x01, //	Usage Page (Generic Desktop)
	0x09, 0x02, //	Usage (Mouse)
	0xA1, 0x01, //	Collection (Application)
	0x09, 0x01, //     Usage (Pointer)
	0xA1, 0x00, //     Collection (Physical)
	0x05, 0x09, //         Usage Page (Button)
	0x19, 0x01, //         Usage Minimum (Button 1)
	0x29, 0x03, //         Usage Maximum (Button 3)
	0x15, 0x00, //         Logical Minimum (0)
	0x25, 0x01, //         Logical Maximum (1)
	0x95, 0x08, //         Report Count (8)
	0x75, 0x01, //         Report Size (1)
	0x81, 0x02, //         Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
	0x05, 0x01, //         Usage Page (Generic Desktop)
	0x09, 0x30, //         Usage (X)
	0x09, 0x31, //         Usage (Y)
	0x09, 0x38, //         Usage (Wheel)
	0x15, 0x81, //         Logical Minimum (-127)
	0x25, 0x7F, //         Logical Maximum (127)
	0x75, 0x08, //         Report Size (8)
	0x95, 0x03, //         Report Count (3)
	0x81, 0x06, //         Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)
	0xC0,		//		End Collection
	0xC0,		//	End Collection
};

// HID Report Descriptor
// keyboard
static const uint8_t HID_report_desc_keyboard []=
{
    0x05, 0x01, // Usage Page (Generic Desktop)
    0x09, 0x06, // Usage (Keyboard)
    0xA1, 0x01, // Collection (Application)
    0x05, 0x07, // Usage Page (Key Codes)
    0x19, 0xE0, // Usage Minimum (224)
    0x29, 0xE7, // Usage Maximum (231)
    0x15, 0x00, // Logical Minimum (0)
    0x25, 0x01, // Logical Maximum (1)
    0x75, 0x01, // Report Size (1)
    0x95, 0x08, // Report Count (8)
    0x81, 0x02, // Input (Data, Variable, Absolute) -- Modifier byte
    0x95, 0x01, // Report Count (1)
    0x75, 0x08, // Report Size (8)
    0x81, 0x03, // (81 01) Input (Constant) -- Reserved byte
    0x95, 0x05, // Report Count (5)
    0x75, 0x01, // Report Size (1)
    0x05, 0x08, // Usage Page (Page# for LEDs)
    0x19, 0x01, // Usage Minimum (1)
    0x29, 0x05, // Usage Maximum (5)
    0x91, 0x02, // Output (Data, Variable, Absolute) -- LED report
    0x95, 0x01, // Report Count (1)
    0x75, 0x03, // Report Size (3)
    0x91, 0x03, // (91 03) Output (Constant) -- LED report padding
    0x95, 0x06, // Report Count (6)
    0x75, 0x08, // Report Size (8)
    0x15, 0x00, // Logical Minimum (0)
    0x25, 0x66, // Logical Maximum(102)  // was 0x65
    0x05, 0x07, // Usage Page (Key Codes)
    0x19, 0x00, // Usage Minimum (0)
    0x29, 0x66, // Usage Maximum (102) // was 0x65
    0x81, 0x00, // Input (Data, Array) -- Key arrays (6 bytes)
    0xC0,       // End Collection

};


// HID Report Descriptor
// keyboard
static const uint8_t HID_report_desc_display []=
{
    0x05, 0x14,                    // USAGE_PAGE (Alphnumeric Display)
    0x09, 0x01,                    // USAGE (Alphanumeric Display)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0xa1, 0x02,                    // COLLECTION (Logical)
    0x09, 0x20,                    //   USAGE (Display Attributes Report)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x09, 0x35,                    //     USAGE (Rows)
    0x09, 0x36,                    //     USAGE (Columns)
    0x09, 0x3d,                    //     USAGE (Character Width)
    0x09, 0x3e,                    //     USAGE (Character Height)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x25, 0x1f,                    //     LOGICAL_MAXIMUM (31)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x09, 0x21,                    //     USAGE (ASCII Character Set)
    0x09, 0x22,                    //     USAGE (Data Read Back)
    0x09, 0x29,                    //     USAGE (Vertical Scroll)
    0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x25, 0x02,                    //   LOGICAL_MAXIMUM (2)
    0x09, 0x2d,                    //   USAGE (Display Status)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x09, 0x2e,                    //     USAGE (Stat Not Ready)
    0x09, 0x2f,                    //     USAGE (Stat Ready)
    0x09, 0x30,                    //     USAGE (Err Not a loadable character)
    0x81, 0x40,                    //     INPUT (Data,Ary,Abs,Null)
    0xc0,                          //   END_COLLECTION
    0x09, 0x32,                    //   USAGE (Cursor Position Report)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x85, 0x02,                    //     REPORT_ID (2)
    0x75, 0x04,                    //     REPORT_SIZE (4)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x25, 0x0f,                    //     LOGICAL_MAXIMUM (15)
    0x09, 0x34,                    //     USAGE (Column)
    0xb1, 0x22,                    //     FEATURE (Data,Var,Abs,NPrf)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x09, 0x33,                    //     USAGE (Row)
    0xb1, 0x22,                    //     FEATURE (Data,Var,Abs,NPrf)
    0xc0,                          //   END_COLLECTION
    0x09, 0x2b,                    //   USAGE (Character Report)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x85, 0x03,                    //     REPORT_ID (3)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x25, 0x7e,                    //     LOGICAL_MAXIMUM (126)
    0x09, 0x2c,                    //     USAGE (Display Data)
    0xb2, 0x02, 0x01,              //     FEATURE (Data,Var,Abs,Buf)
    0xc0,                          //   END_COLLECTION
    0x85, 0x04,                    //   REPORT_ID (4)
    0x09, 0x3b,                    //   USAGE (Font Report)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x7e,                    //     LOGICAL_MAXIMUM (126)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x09, 0x2c,                    //     USAGE (Display Data)
    0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
    0x95, 0x05,                    //     REPORT_COUNT (5)
    0x09, 0x3c,                    //     USAGE (Font Data)
    0x92, 0x02, 0x01,              //     OUTPUT (Data,Var,Abs,Buf)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION
};


// HID Report Descriptor
// keyboard
static const uint8_t hid_report_desc_telephony [] =
{
    0x95, 0x01,                    // REPORT_COUNT (1)
    0x05, 0x0b,                    // USAGE_PAGE (Telephony Devices)
    0x09, 0x01,                    // USAGE (Phone)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x07,                    //   USAGE (Programmable Button)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x06,                    //     USAGE_MAXIMUM (Button 6)
    0x75, 0x03,                    //     REPORT_SIZE (3)
    0x15, 0x01,                    //     LOGICAL_MINIMUM (1)
    0x25, 0x06,                    //     LOGICAL_MAXIMUM (6)
    0x81, 0x00,                    //     INPUT (Data,Ary,Abs)
    0xc0,                          //   END_COLLECTION
    0x05, 0x0b,                    //   USAGE_PAGE (Telephony Devices)
    0x09, 0x06,                    //   USAGE (Telephony Key Pad)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x0c,                    //     USAGE_MAXIMUM (Button 12)
    0x25, 0x0c,                    //     LOGICAL_MAXIMUM (12)
    0x75, 0x04,                    //     REPORT_SIZE (4)
    0x81, 0x00,                    //     INPUT (Data,Ary,Abs)
    0xc0,                          //   END_COLLECTION
    0x05, 0x0b,                    //   USAGE_PAGE (Telephony Devices)
    0x09, 0x20,                    //   USAGE (Hook Switch)
    0x09, 0x29,                    //   USAGE (Alternate Function)
    0x09, 0x2c,                    //   USAGE (Conference)
    0x09, 0x25,                    //   USAGE (Transfer)
    0x09, 0x26,                    //   USAGE (Drop)
    0x09, 0x23,                    //   USAGE (Hold)
    0x09, 0x2b,                    //   USAGE (Speaker Phone)
    0x25, 0x07,                    //   LOGICAL_MAXIMUM (7)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0x05, 0x0c,                    //   USAGE_PAGE (Consumer Devices)
    0x09, 0xe0,                    //   USAGE (Volume)
    0x15, 0xff,                    //   LOGICAL_MINIMUM (-1)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x02,                    //   REPORT_SIZE (2)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x75, 0x04,                    //   REPORT_SIZE (4)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x09, 0x3a,                    //   USAGE (Usage Selected Indicator)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x05, 0x0b,                    //     USAGE_PAGE (Telephony Devices)
    0x09, 0x07,                    //     USAGE (Programmable Button)
    0xa1, 0x02,                    //     COLLECTION (Logical)
    0x05, 0x09,                    //       USAGE_PAGE (Button)
    0x19, 0x01,                    //       USAGE_MINIMUM (Button 1)
    0x29, 0x02,                    //       USAGE_MAXIMUM (Button 2)
    0x95, 0x02,                    //       REPORT_COUNT (2)
    0x91, 0x02,                    //       OUTPUT (Data,Var,Abs)
    0xc0,                          //     END_COLLECTION
    0xc0,                          //   END_COLLECTION
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x09, 0x3b,                    //   USAGE (Usage In Use Indicator)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x05, 0x0b,                    //     USAGE_PAGE (Telephony Devices)
    0x09, 0x07,                    //     USAGE (Programmable Button)
    0xa1, 0x02,                    //     COLLECTION (Logical)
    0x05, 0x09,                    //       USAGE_PAGE (Button)
    0x19, 0x01,                    //       USAGE_MINIMUM (Undefined)
    0x29, 0x06,                    //       USAGE_MAXIMUM (Undefined)
    0x95, 0x06,                    //       REPORT_COUNT (6)
    0x91, 0x02,                    //       OUTPUT (Data,Var,Abs)
    0xc0,                          //     END_COLLECTION
    0x05, 0x0b,                    //     USAGE_PAGE (Telephony Devices)
    0x09, 0x29,                    //     USAGE (Alternate Function)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x09, 0x3c,                    //   USAGE (Usage Multi Mode Indicator)
    0xa1, 0x02,                    //   COLLECTION (Logical)
    0x05, 0x0b,                    //     USAGE_PAGE (Telephony Devices)
    0x09, 0x73,                    //     USAGE (Message)
    0xa1, 0x02,                    //     COLLECTION (Logical)
    0x05, 0x08,                    //       USAGE_PAGE (LEDs)
    0x09, 0x3d,                    //       USAGE (Indicator On)
    0x09, 0x40,                    //       USAGE (Indicator Fast Blink)
    0x09, 0x41,                    //       USAGE (Indicator Off)
    0x75, 0x02,                    //       REPORT_SIZE (2)
    0x91, 0x00,                    //       OUTPUT (Data,Ary,Abs)
    0xc0,                          //     END_COLLECTION
    0xc0,                          //   END_COLLECTION
    0x75, 0x05,                    //   REPORT_SIZE (5)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0xc0                           // END_COLLECTION
};


/* HID Human Interface Device */
static unsigned fill_HID_XXXX_function(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed)
{
	// HID Report Descriptor used for this function
	#define HIDREPORTDESC HID_report_desc_keyboard
	//#define HIDREPORTDESC HID_report_desc_mouse
	//#define HIDREPORTDESC HID_report_desc_display
	//#define HIDREPORTDESC hid_report_desc_telephony

	const void * const pattern = HIDREPORTDESC;
	const unsigned patternlength = sizeof HIDREPORTDESC;
	//
	unsigned n = 0;
	//
	n += HID_InterfaceDescriptorXXXX(fill, p + n, maxsize - n);	/* HID: HID Interface Descriptor */
	n += HID_Descriptor(fill, p + n, maxsize - n, patternlength);	/* HID Descriptor */
	//n += fill_HID_Mouse_IntEP(fill, p + n, maxsize - n, highspeed, USB_ENDPOINT_IN(USBD_EP_HIDMOUSE_INT));

	return n;
}

#endif /* WITHUSBHID */

static unsigned fill_Configuration_main_group(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed)
{
	unsigned n = 0;
	unsigned offset;

#if WITHUSBRNDIS
	n += fill_RNDIS_function(fill, p + n, maxsize - n, highspeed);
#endif /* WITHUSBRNDIS */

#if WITHUSBUAC
#if WITHUSBUAC3
		n += fill_UACINOUT48_function(fill, p + n, maxsize - n, highspeed, 0);
		//n += fill_UACINOUT48andRTS_function(fill, p + n, maxsize - n, highspeed, 0);
#if WITHRTS96 || WITHRTS192
		n += fill_UACINRTS_function(fill, p + n, maxsize - n, highspeed, 1);
#else /* WITHRTS96 || WITHRTS192 */
	#error WITHRTS96 or WITHRTS192 required for WITHUSBUAC3
#endif /* WITHRTS96 || WITHRTS192 */
#else /* WITHUSBUAC3 */
	n += fill_UACINOUT_function(fill, p + n, maxsize - n, highspeed, 0);
#endif /* WITHUSBUAC3 */
#endif /* WITHUSBUAC */

#if WITHUSBCDC
	for (offset = 0; offset < WITHUSBHWCDC_N; ++ offset)
		n += fill_CDCACM_function_a(fill, p + n, maxsize - n, highspeed, offset);
#endif /* WITHUSBCDC */

#if WITHUSBCDCEEM
	n += fill_CDCEEM_function(fill, p + n, maxsize - n, highspeed);
#endif /* WITHUSBCDCEEM */

#if WITHUSBCDCECM
	n += fill_CDCECM_function(fill, p + n, maxsize - n, highspeed);
#endif /* WITHUSBCDCECM */

#if WITHUSBHID
	n += fill_HID_XXXX_function(fill, p + n, maxsize - n, highspeed);
#endif /* WITHUSBHID */
	return n;
}

typedef unsigned (* fill_func_t)(uint_fast8_t fill, uint8_t * p, unsigned maxsize, int highspeed);

// Only for high speed capable devices 
// Other Speed Configuration descriptor - pass highspeed=1
// For all devices
// Configuration descriptor - pass highspeed=0
static unsigned fill_Configuration_descriptor(
		uint8_t * buff, unsigned maxsize, int highspeed,
		const uint_fast8_t bConfigurationValue, // = 0x01;
		const uint_fast8_t bNumInterfaces, // = INTERFACE_count;
		const fill_func_t fill_main_group	// fill functional descriptor(s)
		)
{
#if WITHUSBHWHIGHSPEED && WITHUSBHWHIGHSPEEDDESC
	const int highspeedEPs = 1;
#else /* WITHUSBHWHIGHSPEED && WITHUSBHWHIGHSPEEDDESC */
	const int highspeedEPs = 0;
#endif /* WITHUSBHWHIGHSPEED && WITHUSBHWHIGHSPEEDDESC */
	unsigned length = 9;
	unsigned totalsize = length + fill_main_group(0, buff, maxsize - length, highspeedEPs);
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                                      /* bLength */ 
		* buff ++ = highspeed ? USB_OTHER_SPEED_CONFIG_DESCRIPTOR_TYPE : USB_CONFIGURATION_DESCRIPTOR_TYPE;      /* descriptor type */ 
		* buff ++ = LO_BYTE(totalsize);		/* length of packed config descr. (16 bit) */
		* buff ++ = HI_BYTE(totalsize);		/* length of packed config descr. (16 bit) */
		* buff ++ = bNumInterfaces;			/* bNumInterfaces  */
		* buff ++ = bConfigurationValue;    /* bConfigurationValue - Value to use as an argument to the SetConfiguration() request to select this configuration */
		* buff ++ = STRING_ID_0;       		/* iConfiguration - Index of string descriptor describing this configuration */
		* buff ++ = 0xC0;                   /* bmAttributes  BUS Powred, self powered */
		* buff ++ = USB_CONFIG_POWER_MA(250);/* bMaxPower = 250 mA. Сделано как попытка улучшить работу через активные USB изоляторы для обеспечения их питания. */

		fill_main_group(1, buff, maxsize - length, highspeedEPs);
	}
	return totalsize;
}

// Device Descriptor
static unsigned fill_Device_descriptor(uint8_t * buff, unsigned maxsize, uint_fast8_t bNumConfigurations)
{
	const unsigned length = 18;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;					    /*  0:bLength */
		* buff ++ = USB_DEVICE_DESCRIPTOR_TYPE;         /*  1:bDescriptorType */
		* buff ++ = LO_BYTE(USB_FUNCTION_BCD_USB);      /*  2:bcdUSB_FUNCTION_lo */
		* buff ++ = HI_BYTE(USB_FUNCTION_BCD_USB);		/*  3:bcdUSB_FUNCTION_hi */
		* buff ++ = USB_DEVICE_CLASS_MISCELLANEOUS;		/*  4:bDeviceClass */
		* buff ++ = 2;		                            /*  5:bDeviceSubClass - Common Class Sub Class */
		* buff ++ = 1;									/*  6:bDeviceProtocol - Interface Association Descriptor protocol */
		* buff ++ = USB_OTG_MAX_EP0_SIZE;               /*  7:bMaxPacketSize0 (for DCP) */
		* buff ++ = LO_BYTE(USB_FUNCTION_VENDOR_ID);    /*  8:idVendor_lo */
		* buff ++ = HI_BYTE(USB_FUNCTION_VENDOR_ID);	/*  9:idVendor_hi */
		* buff ++ = LO_BYTE(USB_FUNCTION_PRODUCT_ID);   /* 10:idProduct_lo */
		* buff ++ = HI_BYTE(USB_FUNCTION_PRODUCT_ID);	/* 11:idProduct_hi */
		* buff ++ = LO_BYTE(USB_FUNCTION_RELEASE_NO);   /* 12:bcdDevice_lo */
		* buff ++ = HI_BYTE(USB_FUNCTION_RELEASE_NO);	/* 13:bcdDevice_hi */
		* buff ++ = STRING_ID_1;                        /* 14:iManufacturer */
		* buff ++ = STRING_ID_2;                        /* 15:iProduct */
		* buff ++ = STRING_ID_3;                        /* 16:iSerialNumber */
		* buff ++ = bNumConfigurations;                 /* 17:bNumConfigurations */
	}
	return length;
}

// Only for high speed capable devices 
// Device Qualifier Descriptor 
static unsigned fill_DeviceQualifier_descriptor(uint8_t * buff, unsigned maxsize)
{
	const unsigned length = 10;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                                 /*  0:bLength */
		* buff ++ = USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE;	/*  1:bDescriptorType */
		* buff ++ = LO_BYTE(USB_FUNCTION_BCD_USB);			/*  2:bcdUSB_FUNCTION_lo */
		* buff ++ = HI_BYTE(USB_FUNCTION_BCD_USB);			/*  3:bcdUSB_FUNCTION_hi */
		* buff ++ = USB_DEVICE_CLASS_MISCELLANEOUS;			/*  4:bDeviceClass - Miscellaneous */
		* buff ++ = 2;										/*  5:bDeviceSubClass - Common Class Sub Class */
		* buff ++ = 1;										/*  6:bDeviceProtocol - Interface Association Descriptor protocol */
		* buff ++ = USB_OTG_MAX_EP0_SIZE;                   /*  7:bMaxPacketSize0 (for DCP) */
		* buff ++ = 1;                                      /*  8:bNumConfigurations */
		* buff ++ = 0;                                      /*  9:bReserved */
	}
	return length;
}

// Device Capability Descriptor - USB 2.0 Extension
static unsigned fill_devcaps_usb20ext(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t length = 7;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		const uint_fast32_t bmAttributes = 
			(1uL << 1) |	/* Link Power Management capability bit set */
			0;
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bLength */
		* buff ++ = USB_DEVICE_CAPABITY_DESCRIPTOR_TYPE;	/* bDescriptorType: Device Capability */
		* buff ++ = 0x02;						/* bDevCapabilityType: 0x02: USB 2.0 Extension */
		* buff ++ = LO_BYTE(bmAttributes);
		* buff ++ = HI_BYTE(bmAttributes);
		* buff ++ = HI_24BY(bmAttributes);
		* buff ++ = HI_32BY(bmAttributes);
	}
	return length;
}

// Device Capability Descriptor - Container ID 
static unsigned fill_devcaps_ContainerID(uint_fast8_t fill, uint8_t * buff, unsigned maxsize)
{
    typedef struct _GUID {
        uint32_t Data1;
        uint16_t Data2;
        uint16_t Data3;
        uint8_t Data4 [8];
    } GUID;

	const uint_fast8_t length = 20;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (fill != 0 && buff != NULL)
	{
		// {1D614FE7-58C2-42d8-942E-CC2A696218DB}
		static const GUID ContainerID = 
			{ 0x1d614fe7, 0x58c2, 0x42d8, { 0x94, 0x2e, 0xcc, 0x2a, 0x69, 0x62, 0x18, 0xdb } };

		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;						/* bLength */
		* buff ++ = USB_DEVICE_CAPABITY_DESCRIPTOR_TYPE;	/* bDescriptorType: Device Capability */
		* buff ++ = 0x04;						/* bDevCapabilityType: 0x04: Container ID */
		* buff ++ = 0x00;						/* bReserved */
		memcpy(buff, & ContainerID, 16);		/* ContainerID */
	}
	return length;
}

static unsigned fill_DevCaps_group(uint_fast8_t fill, uint8_t * p, unsigned maxsize, uint_fast8_t bNumDeviceCaps)
{
	unsigned n = 0;
	// Device Capability Descriptor - USB 2.0 Extension 
	n += fill_devcaps_usb20ext(fill, p + n, maxsize - n);
	// Device Capability Descriptor - SuperSpeed USB 
	//n += fill_devcaps_ContainerID(fill, p + n, maxsize - n);
	// Device Capability Descriptor - Container ID 
	n += fill_devcaps_ContainerID(fill, p + n, maxsize - n);

	ASSERT(bNumDeviceCaps == 2);
	return n;
}

static unsigned fill_BinaryDeviceObjectStore_descriptor(uint8_t * buff, unsigned maxsize)
{
	const uint_fast8_t bNumDeviceCaps = 2;
	unsigned length = 5;
	unsigned totalsize = length + fill_DevCaps_group(0, buff, maxsize - length, bNumDeviceCaps);
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                                 /*  0:bLength */
		* buff ++ = USB_BOS_TYPE;							/*  1:bDescriptorType */
		* buff ++ = LO_BYTE(totalsize);						/* wTotalLength length of packed config descr. (16 bit) */
		* buff ++ = HI_BYTE(totalsize);						/* wTotalLength length of packed config descr. (16 bit) */
		* buff ++ = bNumDeviceCaps;										/*  4:bNumDeviceCaps */
		fill_DevCaps_group(1, buff, maxsize - length, bNumDeviceCaps);
	}
	return totalsize;
}

static unsigned fill_align4(uint8_t * buff, unsigned maxsize)
{
	const uintptr_t granulation = 32;
	return (granulation - ((uintptr_t) buff & (granulation - 1))) & (granulation - 1);
}

static unsigned fill_langid_descriptor(uint8_t * buff, unsigned maxsize, uint_fast16_t langid)
{
	const unsigned length = 4;
	ASSERT(maxsize >= length);
	if (maxsize < length)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                          /* bLength */ 
		* buff ++ = USB_STRING_DESCRIPTOR_TYPE;           /* descriptor type */ 
		* buff ++ = LO_BYTE(langid); 
		* buff ++ = HI_BYTE(langid); 
	}
	return length;
}

// todo: ограничить размер дескриптора значением 254
static unsigned fill_string_descriptor(uint8_t * buff, unsigned maxsize, const char * s)
{
	const unsigned length = 2 + 2 * strlen(s);
	ASSERT(length < 256 && maxsize >= length);
	if (maxsize < length || length >= 256)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                                      /* bLength */ 
		* buff ++ = USB_STRING_DESCRIPTOR_TYPE;           /* descriptor type */ 
		for (;;)
		{
			const int c = (unsigned char) * s ++;
			if (c == '\0')
				break;
			const wchar_t wc = c;	// todo: для не-english преобразовывать
			* buff ++ = LO_BYTE(wc);
			* buff ++ = HI_BYTE(wc);
		}
	}
	return length;
}

// todo: ограничить размер дескриптора значением 254
static unsigned fill_wstring_descriptor(uint8_t * buff, unsigned maxsize, const wchar_t * s)
{
	const unsigned length = 2 + 2 * wcslen(s);
	ASSERT(length < 256 && maxsize >= length);
	if (maxsize < length || length >= 256)
		return 0;
	if (buff != NULL)
	{
		// Вызов для заполнения, а не только для проверки занимаемого места в буфере
		* buff ++ = length;                                      /* bLength */ 
		* buff ++ = USB_STRING_DESCRIPTOR_TYPE;           /* descriptor type */ 
		for (;;)
		{
			const int c = * s ++;
			if (c == L'\0')
				break;
			const wchar_t wc = c;
			* buff ++ = LO_BYTE(wc);
			* buff ++ = HI_BYTE(wc);
		}
	}
	return length;
}


static ALIGNX_BEGIN uint8_t alldescbuffer [1440] ALIGNX_END;

struct descholder StringDescrTbl [STRING_ID_count];
struct descholder ConfigDescrTbl [USBD_NCONFIGS];
struct descholder DeviceDescrTbl [USBD_NCONFIGS];

struct descholder OtherSpeedConfigurationTbl [USBD_NCONFIGS];
struct descholder DeviceQualifierTbl [USBD_NCONFIGS];
struct descholder BinaryDeviceObjectStoreTbl [1];
struct descholder HIDReportDescrTbl [1];


uint_fast8_t usbd_get_stringsdesc_count(void)
{
	return ARRAY_SIZE(StringDescrTbl);
}


// Динамическое формирование дескрипторов
/* вызывается при запрещённых прерываниях. */
void usbd_descriptors_initialize(uint_fast8_t HSdesc)
{
	unsigned score = 0;

	static const struct
	{
		fill_func_t fp;
		uint_fast8_t count;
		uint_fast8_t confvalue;
	} funcs [] =
	{
#if WITHPLAINDESCROPTOR
			{ fill_Configuration_main_group,	INTERFACE_COMPOSITE_count, 0x01, },
#else /* WITHPLAINDESCROPTOR */
	#if WITHUSBRNDIS
			{ fill_RNDIS_function,		INTERFACE_RNDIS_count, 	RNDIS_cfgidx, },
	#endif /* WITHUSBRNDIS */
	#if WITHUSBCDCECM
			{ fill_CDCECM_function,		INTERFACE_CDCECM_count, CDCECM_cfgidx },
	#endif /* WITHUSBCDCECM */
#endif /* WITHPLAINDESCROPTOR */
	};
	const uint_fast8_t bNumConfigurations = ARRAY_SIZE(funcs);

	{
		// Device Descriptor
		unsigned partlen;
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_Device_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, bNumConfigurations);
		//partlen = fill_pattern_descriptor(1, alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, fullSpdDesc, sizeof fullSpdDesc);
		DeviceDescrTbl [0].size = partlen;
		DeviceDescrTbl [0].data = alldescbuffer + score;
		score += partlen;
	}

	{
		// Configuration Descriptors list
		unsigned partlen;
		uint_fast8_t index;
		for (index = 0; index < bNumConfigurations && index < ARRAY_SIZE(ConfigDescrTbl); ++ index)
		{
			// Configuration Descriptor
			score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
			partlen = fill_Configuration_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, 0, funcs [index].confvalue, funcs [index].count, funcs [index].fp);
			ConfigDescrTbl [index].size = partlen;
			ConfigDescrTbl [index].data = alldescbuffer + score;
			score += partlen;
		}
	}

	if (HSdesc != 0)
	{
		unsigned partlen;
		// Device Qualifier
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_DeviceQualifier_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		DeviceQualifierTbl [0].size = partlen;
		DeviceQualifierTbl [0].data = alldescbuffer + score;
		score += partlen;

#if 0
		/*
			The other_speed_configuration descriptor shown in Table 9-11 describes a 
			configuration of a highspeed capable device if it were operating at its 
			other possible speed.  
			The structure of the other_speed_configuration is identical to a 
			configuration descriptor.
		*/
		{
			// Other Speed Configuration descriptors list
			unsigned partlen;
			uint_fast8_t index;
			for (index = 0; index < bNumConfigurations && index < ARRAY_SIZE(OtherSpeedConfigurationTbl); ++ index)
			{
				// Configuration Descriptor
				score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
				partlen = fill_Configuration_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, ! HSdesc, funcs [index].confvalue, funcs [index].count, funcs [index].fp);
				OtherSpeedConfigurationTbl [index].size = partlen;
				OtherSpeedConfigurationTbl [index].data = alldescbuffer + score;
				score += partlen;
			}
		}
#endif
	}
	else
	{
		// Device Qualifier
		DeviceQualifierTbl [0].size = 0;
		DeviceQualifierTbl [0].data = NULL;

		// Other Speed Configuration
		OtherSpeedConfigurationTbl [0].size = 0;
		OtherSpeedConfigurationTbl [0].data = NULL;
	}

	// Binary Device Object Store (BOS) Descriptor
	if (USB_FUNCTION_BCD_USB > 0x0200)
	{
		unsigned partlen;
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_BinaryDeviceObjectStore_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		BinaryDeviceObjectStoreTbl [0].size = partlen;
		BinaryDeviceObjectStoreTbl [0].data = alldescbuffer + score;
		score += partlen;
	}
	else
	{
		BinaryDeviceObjectStoreTbl [0].size = 0;
		BinaryDeviceObjectStoreTbl [0].data = NULL;
	}

	// String descriptors
	{
		// Language ID (mandatory)
		unsigned partlen;
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_langid_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, UNICODE_ENGLISH);
		StringDescrTbl [STRING_ID_0].size = partlen;
		StringDescrTbl [STRING_ID_0].data = alldescbuffer + score;
		score += partlen;

		// All string IDs, except serial number
		unsigned i;
		for (i = 0; i < ARRAY_SIZE(strtemplates); ++ i)
		{
			const uint_fast8_t id = strtemplates [i].id;
			ASSERT(id < ARRAY_SIZE(StringDescrTbl));

			score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
			partlen = fill_string_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, strtemplates [i].str);
			StringDescrTbl [id].size = partlen;
			StringDescrTbl [id].data = alldescbuffer + score;
			score += partlen;
		}
	}

#if WITHUSBHID
	{
		unsigned partlen;
		const uint_fast8_t id = 0;
		ASSERT(id < ARRAY_SIZE(HIDReportDescrTbl));

		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_pattern_descriptor(1, alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, HIDREPORTDESC, sizeof HIDREPORTDESC);
		HIDReportDescrTbl [id].size = partlen;
		HIDReportDescrTbl [id].data = alldescbuffer + score;
		score += partlen;
	}
#endif /* WITHUSBHID */
	
#if WITHUSBCDCECM
	{
		unsigned partlen;
		// Формирование MAC адреса данного устройства
		// TODO: При модификации не забыть про достоверность значений
		const uint_fast8_t id = STRING_ID_MACADDRESS;
		char b [64];
		local_snprintf_P(b, ARRAY_SIZE(b), PSTR("3089846A96AB"));
		// Unic serial number
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_string_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, b);
		StringDescrTbl [id].size = partlen;
		StringDescrTbl [id].data = alldescbuffer + score;
		score += partlen;
	}
#endif /* WITHUSBCDCECM */

	{
		unsigned partlen;
		const uint_fast8_t id = STRING_ID_3;
		char b [64];
		local_snprintf_P(b, ARRAY_SIZE(b), PSTR("SN:19640302_%lu"), (unsigned long) BUILD_ID);
		// Unic serial number
		score += fill_align4(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score);
		partlen = fill_string_descriptor(alldescbuffer + score, ARRAY_SIZE(alldescbuffer) - score, b);
		StringDescrTbl [id].size = partlen;
		StringDescrTbl [id].data = alldescbuffer + score;
		score += partlen;
	}

	arm_hardware_flush_invalidate((uintptr_t) alldescbuffer, score);
	PRINTF(PSTR("usbd_descriptors_initialize: total length=%u\r\n"), score);
}


#endif /* WITHUSBHW */

// +++ адаптация
#if CPUSTYLE_ARM_CM7

// Сейчас в эту память будем читать по DMA
// Убрать копию этой области из кэша
// Используется только в startup
void arm_hardware_invalidate(uintptr_t base, size_t size)
{
	//ASSERT((base % 32) == 0);		// при работе с BACKUP SRAM невыровненно
	SCB_InvalidateDCache_by_Addr((void *) base, size);	// DCIMVAC register used.
}

// Сейчас эта память будет записываться по DMA куда-то
// Записать содержимое кэша данных в память
void arm_hardware_flush(uintptr_t base, size_t size)
{
	//ASSERT((base % 32) == 0);		// при работе с BACKUP SRAM невыровненно
	SCB_CleanDCache_by_Addr((void *) base, size);	// DCCMVAC register used.
}

// Записать содержимое кэша данных в память
// применяетмся после начальной инициализации среды выполнния
void arm_hardware_flush_all(void)
{
	SCB_CleanDCache();	// DCCMVAC register used.
}

// Сейчас эта память будет записываться по DMA куда-то. Потом содержимое не требуется
// Записать содержимое кэша данных в память
// Убрать копию этой области из кэша
void arm_hardware_flush_invalidate(uintptr_t base, size_t size)
{
	//ASSERT((base % 32) == 0);		// при работе с BACKUP SRAM невыровненно
	SCB_CleanInvalidateDCache_by_Addr((void *) base, size);	// DCCIMVAC register used.
}

#elif CPUSTYLE_ARM_CA9

static unsigned long DCACHEROWSIZE; // 32
static unsigned long ICACHEROWSIZE; // 32

#define MK_MVA(addr) ((uintptr_t) (addr) & ~ (uintptr_t) (DCACHEROWSIZE - 1))

// Сейчас в эту память будем читать по DMA
// Используется только в startup
void arm_hardware_invalidate(uintptr_t base, size_t size)
{
	unsigned long len = (size + (DCACHEROWSIZE - 1)) / DCACHEROWSIZE + (((unsigned long) base & (DCACHEROWSIZE - 1)) != 0);
	while (len --)
	{
		uintptr_t mva = MK_MVA(base);
		L1C_InvalidateDCacheMVA((void *) mva);	// очистить кэш
		base += DCACHEROWSIZE;
	}
}


/* считать конфигурационные параметры data cache */
static void ca9_cache_setup(void)
{
	uint32_t ccsidr0 [8];	// data cache parameters
	uint32_t ccsidr1 [8];	// instruction cache parameters

	uint_fast32_t leveli;
	for (leveli = 0; leveli <= ARM_CA9_CACHELEVELMAX; ++ leveli)
	{
		__set_CSSELR(leveli * 2 + 0);	// data cache select
		ccsidr0 [leveli] = __get_CCSIDR();

		//const uint32_t assoc0 = (ccsidr0 >> 3) & 0x3FF;
		//const int passoc0 = ilog2(assoc0);
		//const uint32_t maxsets0 = (ccsidr0 >> 13) & 0x7FFF;

		__set_CSSELR(leveli * 2 + 1);	// instruction cache select
		ccsidr1 [leveli] = __get_CCSIDR();

		//const uint32_t assoc1 = (ccsidr1 >> 3) & 0x3FF;
		//const int passoc1 = ilog2(assoc1);
		//const uint32_t maxsets1 = (ccsidr1 >> 13) & 0x7FFF;
	}

	// Установка размера строки кэша
	DCACHEROWSIZE = 4uL << (((ccsidr0 [0] >> 0) & 0x07) + 2);
	ICACHEROWSIZE = 4uL << (((ccsidr1 [0] >> 0) & 0x07) + 2);
}

// используется в startup
static void
arm_hardware_invalidate_all(void)
{
	L1C_InvalidateDCacheAll();
	L1C_InvalidateICacheAll();
	L1C_InvalidateBTAC();
}

// Сейчас эта память будет записываться по DMA куда-то
void arm_hardware_flush(uintptr_t base, size_t size)
{
	unsigned long len = (size + (DCACHEROWSIZE - 1)) / DCACHEROWSIZE + (((unsigned long) base & (DCACHEROWSIZE - 1)) != 0);
	while (len --)
	{
		uintptr_t mva = MK_MVA(base);
		L1C_CleanDCacheMVA((void *) mva);		// записать буфер, кэш продолжает хранить
		base += DCACHEROWSIZE;
	}
}


// Записать содержимое кэша данных в память
// применяетмся после начальной инициализации среды выполнния
void arm_hardware_flush_all(void)
{
	L1C_CleanDCacheAll();
}

// Сейчас эта память будет записываться по DMA куда-то. Потом содержимое не требуется
void arm_hardware_flush_invalidate(uintptr_t base, size_t size)
{
	unsigned long len = (size + (DCACHEROWSIZE - 1)) / DCACHEROWSIZE + (((unsigned long) base & (DCACHEROWSIZE - 1)) != 0);
	while (len --)
	{
		uintptr_t mva = MK_MVA(base);
		L1C_CleanInvalidateDCacheMVA((void *) mva);	// записать буфер, очистить кэш
		base += DCACHEROWSIZE;
	}
}

#else

// Заглушки
// Сейчас в эту память будем читать по DMA
// Используется только в startup
void arm_hardware_invalidate(uintptr_t base, size_t size)
{
}

// Сейчас эта память будет записываться по DMA куда-то
void arm_hardware_flush(uintptr_t base, size_t size)
{
}

// Записать содержимое кэша данных в память
// применяетмся после начальной инициализации среды выполнния
void arm_hardware_flush_all(void)
{
}

// Сейчас эта память будет записываться по DMA куда-то. Потом содержимое не требуется
void arm_hardware_flush_invalidate(uintptr_t base, size_t size)
{
}

#endif /* CPUSTYLE_ARM_CM7 */

#define FORMATFROMLIBRARY 1
// Для архитектуры ATMega определена только эта функция -
// с расположением форматной строкии в памяти программ.
uint_fast8_t local_snprintf_P( char *buffer, uint_fast8_t count, const FLASHMEM char *format, ... )
{
	va_list	ap;
	int n;

	#if FORMATFROMLIBRARY
		va_start(ap, format);
		n = vsnprintf(buffer, count, format, ap);
		va_end(ap);
	#else /* FORMATFROMLIBRARY */

		struct fmt_param pr;

		pr.buffer = buffer;
		pr.pos = 0;
		pr.count = count;

		va_start(ap, format);
		// использование самописной функции
		n = local_format(& pr, vsputchar, format, ap);		// никогда не возвращается ошибка
		va_end(ap);
		vsputchar(& pr, '\0');
	#endif /* FORMATFROMLIBRARY */
	return n == -1 ? count - 1 : n;	// изменено от стандартного поведения = всегда длинну возвращаем.
}

// --- адаптация