/****************************************************************************** * Simple EDF scheduler for xen * * by Stephan Diestelhorst (C) 2004 Cambridge University * based on code by Mark Williamson (C) 2004 Intel Research Cambridge */ #include #include #include #include #include #include #include /*verbosity settings*/ #define SEDFLEVEL 0 #define PRINT(_f, _a...) \ do { \ if ( (_f) <= SEDFLEVEL ) \ printk(_a ); \ } while ( 0 ) #ifndef NDEBUG #define SEDF_STATS #define CHECK(_p) \ do { \ if ( !(_p) ) \ printk("Check '%s' failed, line %d, file %s\n", \ #_p , __LINE__, __FILE__); \ } while ( 0 ) #else #define CHECK(_p) ((void)0) #endif #define EXTRA_NONE (0) #define EXTRA_AWARE (1) #define EXTRA_RUN_PEN (2) #define EXTRA_RUN_UTIL (4) #define EXTRA_WANT_PEN_Q (8) #define EXTRA_PEN_Q (0) #define EXTRA_UTIL_Q (1) #define SEDF_ASLEEP (16) #define EXTRA_QUANTUM (MICROSECS(500)) #define WEIGHT_PERIOD (MILLISECS(100)) #define WEIGHT_SAFETY (MILLISECS(5)) #define PERIOD_MAX MILLISECS(10000) /* 10s */ #define PERIOD_MIN (MICROSECS(10)) /* 10us */ #define SLICE_MIN (MICROSECS(5)) /* 5us */ #define IMPLY(a, b) (!(a) || (b)) #define EQ(a, b) ((!!(a)) == (!!(b))) struct sedf_dom_info { struct domain *domain; }; struct sedf_vcpu_info { struct vcpu *vcpu; struct list_head list; struct list_head extralist[2]; /*Parameters for EDF*/ s_time_t period; /*=(relative deadline)*/ s_time_t slice; /*=worst case execution time*/ /*Advaced Parameters*/ /*Latency Scaling*/ s_time_t period_orig; s_time_t slice_orig; s_time_t latency; /*status of domain*/ int status; /*weights for "Scheduling for beginners/ lazy/ etc." ;)*/ short weight; short extraweight; /*Bookkeeping*/ s_time_t deadl_abs; s_time_t sched_start_abs; s_time_t cputime; /* times the domain un-/blocked */ s_time_t block_abs; s_time_t unblock_abs; /*scores for {util, block penalty}-weighted extratime distribution*/ int score[2]; s_time_t short_block_lost_tot; /*Statistics*/ s_time_t extra_time_tot; #ifdef SEDF_STATS s_time_t block_time_tot; s_time_t penalty_time_tot; int block_tot; int short_block_tot; int long_block_tot; int short_cont; int pen_extra_blocks; int pen_extra_slices; #endif }; struct sedf_cpu_info { struct list_head runnableq; struct list_head waitq; struct list_head extraq[2]; s_time_t current_slice_expires; }; #define EDOM_INFO(d) ((struct sedf_vcpu_info *)((d)->sched_priv)) #define CPU_INFO(cpu) \ ((struct sedf_cpu_info *)per_cpu(schedule_data, cpu).sched_priv) #define LIST(d) (&EDOM_INFO(d)->list) #define EXTRALIST(d,i) (&(EDOM_INFO(d)->extralist[i])) #define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) #define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) #define EXTRAQ(cpu,i) (&(CPU_INFO(cpu)->extraq[i])) #define IDLETASK(cpu) ((struct vcpu *)per_cpu(schedule_data, cpu).idle) #define PERIOD_BEGIN(inf) ((inf)->deadl_abs - (inf)->period) #define MIN(x,y) (((x)<(y))?(x):(y)) #define DIV_UP(x,y) (((x) + (y) - 1) / y) #define extra_runs(inf) ((inf->status) & 6) #define extra_get_cur_q(inf) (((inf->status & 6) >> 1)-1) #define sedf_runnable(edom) (!(EDOM_INFO(edom)->status & SEDF_ASLEEP)) static void sedf_dump_cpu_state(int i); static inline int extraq_on(struct vcpu *d, int i) { return ((EXTRALIST(d,i)->next != NULL) && (EXTRALIST(d,i)->next != EXTRALIST(d,i))); } static inline void extraq_add_head(struct vcpu *d, int i) { list_add(EXTRALIST(d,i), EXTRAQ(d->processor,i)); ASSERT(extraq_on(d, i)); } static inline void extraq_add_tail(struct vcpu *d, int i) { list_add_tail(EXTRALIST(d,i), EXTRAQ(d->processor,i)); ASSERT(extraq_on(d, i)); } static inline void extraq_del(struct vcpu *d, int i) { struct list_head *list = EXTRALIST(d,i); ASSERT(extraq_on(d,i)); PRINT(3, "Removing domain %i.%i from L%i extraq\n", d->domain->domain_id, d->vcpu_id, i); list_del(list); list->next = NULL; ASSERT(!extraq_on(d, i)); } /* adds a domain to the queue of processes which are aware of extra time. List is sorted by score, where a lower score means higher priority for an extra slice. It also updates the score, by simply subtracting a fixed value from each entry, in order to avoid overflow. The algorithm works by simply charging each domain that recieved extratime with an inverse of its weight. */ static inline void extraq_add_sort_update(struct vcpu *d, int i, int sub) { struct list_head *cur; struct sedf_vcpu_info *curinf; ASSERT(!extraq_on(d,i)); PRINT(3, "Adding domain %i.%i (score= %i, short_pen= %"PRIi64")" " to L%i extraq\n", d->domain->domain_id, d->vcpu_id, EDOM_INFO(d)->score[i], EDOM_INFO(d)->short_block_lost_tot, i); /* * Iterate through all elements to find our "hole" and on our way * update all the other scores. */ list_for_each ( cur, EXTRAQ(d->processor, i) ) { curinf = list_entry(cur,struct sedf_vcpu_info,extralist[i]); curinf->score[i] -= sub; if ( EDOM_INFO(d)->score[i] < curinf->score[i] ) break; PRINT(4,"\tbehind domain %i.%i (score= %i)\n", curinf->vcpu->domain->domain_id, curinf->vcpu->vcpu_id, curinf->score[i]); } /* cur now contains the element, before which we'll enqueue. */ PRINT(3, "\tlist_add to %p\n", cur->prev); list_add(EXTRALIST(d,i),cur->prev); /* Continue updating the extraq. */ if ( (cur != EXTRAQ(d->processor,i)) && sub ) { for ( cur = cur->next; cur != EXTRAQ(d->processor,i); cur = cur->next ) { curinf = list_entry(cur,struct sedf_vcpu_info, extralist[i]); curinf->score[i] -= sub; PRINT(4, "\tupdating domain %i.%i (score= %u)\n", curinf->vcpu->domain->domain_id, curinf->vcpu->vcpu_id, curinf->score[i]); } } ASSERT(extraq_on(d,i)); } static inline void extraq_check(struct vcpu *d) { if ( extraq_on(d, EXTRA_UTIL_Q) ) { PRINT(2,"Dom %i.%i is on L1 extraQ\n", d->domain->domain_id, d->vcpu_id); if ( !(EDOM_INFO(d)->status & EXTRA_AWARE) && !extra_runs(EDOM_INFO(d)) ) { extraq_del(d, EXTRA_UTIL_Q); PRINT(2,"Removed dom %i.%i from L1 extraQ\n", d->domain->domain_id, d->vcpu_id); } } else { PRINT(2, "Dom %i.%i is NOT on L1 extraQ\n", d->domain->domain_id, d->vcpu_id); if ( (EDOM_INFO(d)->status & EXTRA_AWARE) && sedf_runnable(d) ) { extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); PRINT(2,"Added dom %i.%i to L1 extraQ\n", d->domain->domain_id, d->vcpu_id); } } } static inline void extraq_check_add_unblocked(struct vcpu *d, int priority) { struct sedf_vcpu_info *inf = EDOM_INFO(d); if ( inf->status & EXTRA_AWARE ) /* Put on the weighted extraq without updating any scores. */ extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); } static inline int __task_on_queue(struct vcpu *d) { return (((LIST(d))->next != NULL) && (LIST(d)->next != LIST(d))); } static inline void __del_from_queue(struct vcpu *d) { struct list_head *list = LIST(d); ASSERT(__task_on_queue(d)); PRINT(3,"Removing domain %i.%i (bop= %"PRIu64") from runq/waitq\n", d->domain->domain_id, d->vcpu_id, PERIOD_BEGIN(EDOM_INFO(d))); list_del(list); list->next = NULL; ASSERT(!__task_on_queue(d)); } typedef int(*list_comparer)(struct list_head* el1, struct list_head* el2); static inline void list_insert_sort( struct list_head *list, struct list_head *element, list_comparer comp) { struct list_head *cur; /* Iterate through all elements to find our "hole". */ list_for_each( cur, list ) if ( comp(element, cur) < 0 ) break; /* cur now contains the element, before which we'll enqueue. */ PRINT(3,"\tlist_add to %p\n",cur->prev); list_add(element, cur->prev); } #define DOMAIN_COMPARER(name, field, comp1, comp2) \ int name##_comp(struct list_head* el1, struct list_head* el2) \ { \ struct sedf_vcpu_info *d1, *d2; \ d1 = list_entry(el1,struct sedf_vcpu_info, field); \ d2 = list_entry(el2,struct sedf_vcpu_info, field); \ if ( (comp1) == (comp2) ) \ return 0; \ if ( (comp1) < (comp2) ) \ return -1; \ else \ return 1; \ } /* adds a domain to the queue of processes which wait for the beginning of the next period; this list is therefore sortet by this time, which is simply absol. deadline - period */ DOMAIN_COMPARER(waitq, list, PERIOD_BEGIN(d1), PERIOD_BEGIN(d2)); static inline void __add_to_waitqueue_sort(struct vcpu *v) { ASSERT(!__task_on_queue(v)); PRINT(3,"Adding domain %i.%i (bop= %"PRIu64") to waitq\n", v->domain->domain_id, v->vcpu_id, PERIOD_BEGIN(EDOM_INFO(v))); list_insert_sort(WAITQ(v->processor), LIST(v), waitq_comp); ASSERT(__task_on_queue(v)); } /* adds a domain to the queue of processes which have started their current period and are runnable (i.e. not blocked, dieing,...). The first element on this list is running on the processor, if the list is empty the idle task will run. As we are implementing EDF, this list is sorted by deadlines. */ DOMAIN_COMPARER(runq, list, d1->deadl_abs, d2->deadl_abs); static inline void __add_to_runqueue_sort(struct vcpu *v) { PRINT(3,"Adding domain %i.%i (deadl= %"PRIu64") to runq\n", v->domain->domain_id, v->vcpu_id, EDOM_INFO(v)->deadl_abs); list_insert_sort(RUNQ(v->processor), LIST(v), runq_comp); } static int sedf_init_vcpu(struct vcpu *v) { struct sedf_vcpu_info *inf; if ( (v->sched_priv = xmalloc(struct sedf_vcpu_info)) == NULL ) return -1; memset(v->sched_priv, 0, sizeof(struct sedf_vcpu_info)); inf = EDOM_INFO(v); inf->vcpu = v; /* Allocate per-CPU context if this is the first domain to be added. */ if ( unlikely(per_cpu(schedule_data, v->processor).sched_priv == NULL) ) { per_cpu(schedule_data, v->processor).sched_priv = xmalloc(struct sedf_cpu_info); BUG_ON(per_cpu(schedule_data, v->processor).sched_priv == NULL); memset(CPU_INFO(v->processor), 0, sizeof(*CPU_INFO(v->processor))); INIT_LIST_HEAD(WAITQ(v->processor)); INIT_LIST_HEAD(RUNQ(v->processor)); INIT_LIST_HEAD(EXTRAQ(v->processor,EXTRA_PEN_Q)); INIT_LIST_HEAD(EXTRAQ(v->processor,EXTRA_UTIL_Q)); } /* Every VCPU gets an equal share of extratime by default. */ inf->deadl_abs = 0; inf->latency = 0; inf->status = EXTRA_AWARE | SEDF_ASLEEP; inf->extraweight = 1; if ( v->domain->domain_id == 0 ) { /* Domain0 gets 75% guaranteed (15ms every 20ms). */ inf->period = MILLISECS(20); inf->slice = MILLISECS(15); } else { /* Best-effort extratime only. */ inf->period = WEIGHT_PERIOD; inf->slice = 0; } inf->period_orig = inf->period; inf->slice_orig = inf->slice; INIT_LIST_HEAD(&(inf->list)); INIT_LIST_HEAD(&(inf->extralist[EXTRA_PEN_Q])); INIT_LIST_HEAD(&(inf->extralist[EXTRA_UTIL_Q])); if ( !is_idle_vcpu(v) ) { extraq_check(v); } else { EDOM_INFO(v)->deadl_abs = 0; EDOM_INFO(v)->status &= ~SEDF_ASLEEP; } return 0; } static void sedf_destroy_vcpu(struct vcpu *v) { xfree(v->sched_priv); } static int sedf_init_domain(struct domain *d) { d->sched_priv = xmalloc(struct sedf_dom_info); if ( d->sched_priv == NULL ) return -ENOMEM; memset(d->sched_priv, 0, sizeof(struct sedf_dom_info)); return 0; } static void sedf_destroy_domain(struct domain *d) { xfree(d->sched_priv); } static int sedf_pick_cpu(struct vcpu *v) { cpumask_t online_affinity; cpus_and(online_affinity, v->cpu_affinity, cpu_online_map); return first_cpu(online_affinity); } /* * Handles the rescheduling & bookkeeping of domains running in their * guaranteed timeslice. */ static void desched_edf_dom(s_time_t now, struct vcpu* d) { struct sedf_vcpu_info* inf = EDOM_INFO(d); /* Current domain is running in real time mode. */ ASSERT(__task_on_queue(d)); /* Update the domain's cputime. */ inf->cputime += now - inf->sched_start_abs; /* * Scheduling decisions which don't remove the running domain from the * runq. */ if ( (inf->cputime < inf->slice) && sedf_runnable(d) ) return; __del_from_queue(d); /* * Manage bookkeeping (i.e. calculate next deadline, memorise * overrun-time of slice) of finished domains. */ if ( inf->cputime >= inf->slice ) { inf->cputime -= inf->slice; if ( inf->period < inf->period_orig ) { /* This domain runs in latency scaling or burst mode. */ inf->period *= 2; inf->slice *= 2; if ( (inf->period > inf->period_orig) || (inf->slice > inf->slice_orig) ) { /* Reset slice and period. */ inf->period = inf->period_orig; inf->slice = inf->slice_orig; } } /* Set next deadline. */ inf->deadl_abs += inf->period
/*
             LUFA Library
     Copyright (C) Dean Camera, 2013.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/

/*
  Copyright 2013  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaims all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

/** \file
 *
 *  USB Device Descriptors, for library use when in USB device mode. Descriptors are special
 *  computer-readable structures which the host requests upon device enumeration, to determine
 *  the device's capabilities and functions.
 */

#include "Descriptors.h"

/** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall
 *  device characteristics, including the supported USB version, control endpoint size and the
 *  number of device configurations. The descriptor is read out by the USB host when the enumeration
 *  process begins.
 */
const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
{
	.Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},

	.USBSpecification       = VERSION_BCD(01.10),
	.Class                  = USB_CSCP_NoDeviceClass,
	.SubClass               = USB_CSCP_NoDeviceSubclass,
	.Protocol               = USB_CSCP_NoDeviceProtocol,

	.Endpoint0Size          = FIXED_CONTROL_ENDPOINT_SIZE,

	.VendorID               = 0x03EB,
	.ProductID              = 0x2048,
	.ReleaseNumber          = VERSION_BCD(00.01),

	.ManufacturerStrIndex   = 0x01,
	.ProductStrIndex        = 0x02,
	.SerialNumStrIndex      = NO_DESCRIPTOR,

	.NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
};

/** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage
 *  of the device in one of its supported configurations, including information about any device interfaces
 *  and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting
 *  a configuration so that the host may correctly communicate with the USB device.
 */
const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor =
{
	.Config =
		{
			.Header                   = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration},

			.TotalConfigurationSize   = sizeof(USB_Descriptor_Configuration_t),
			.TotalInterfaces          = 2,

			.ConfigurationNumber      = 1,
			.ConfigurationStrIndex    = NO_DESCRIPTOR,

			.ConfigAttributes         = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED),

			.MaxPowerConsumption      = USB_CONFIG_POWER_MA(100)
		},

	.Audio_ControlInterface =
		{
			.Header                   = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},

			.InterfaceNumber          = 0,
			.AlternateSetting         = 0,

			.TotalEndpoints           = 0,

			.Class                    = AUDIO_CSCP_AudioClass,
			.SubClass                 = AUDIO_CSCP_ControlSubclass,
			.Protocol                 = AUDIO_CSCP_ControlProtocol,

			.InterfaceStrIndex        = NO_DESCRIPTOR
		},

	.Audio_ControlInterface_SPC =
		{
			.Header                   = {.Size = sizeof(USB_Audio_Descriptor_Interface_AC_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_Header,

			.ACSpecification          = VERSION_BCD(01.00),
			.TotalLength              = sizeof(USB_Audio_Descriptor_Interface_AC_t),

			.InCollection             = 1,
			.InterfaceNumber          = 1,
		},

	.Audio_StreamInterface =
		{
			.Header                   = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},

			.InterfaceNumber          = 1,
			.AlternateSetting         = 0,

			.TotalEndpoints           = 2,

			.Class                    = AUDIO_CSCP_AudioClass,
			.SubClass                 = AUDIO_CSCP_MIDIStreamingSubclass,
			.Protocol                 = AUDIO_CSCP_StreamingProtocol,

			.InterfaceStrIndex        = NO_DESCRIPTOR
		},

	.Audio_StreamInterface_SPC =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_AudioInterface_AS_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_General,

			.AudioSpecification       = VERSION_BCD(01.00),

			.TotalLength              = (sizeof(USB_Descriptor_Configuration_t) -
			                             offsetof(USB_Descriptor_Configuration_t, Audio_StreamInterface_SPC))
		},

	.MIDI_In_Jack_Emb =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_InputJack_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_InputTerminal,

			.JackType                 = MIDI_JACKTYPE_Embedded,
			.JackID                   = 0x01,

			.JackStrIndex             = NO_DESCRIPTOR
		},

	.MIDI_In_Jack_Ext =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_InputJack_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_InputTerminal,

			.JackType                 = MIDI_JACKTYPE_External,
			.JackID                   = 0x02,

			.JackStrIndex             = NO_DESCRIPTOR
		},

	.MIDI_Out_Jack_Emb =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_OutputJack_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,

			.JackType                 = MIDI_JACKTYPE_Embedded,
			.JackID                   = 0x03,

			.NumberOfPins             = 1,
			.SourceJackID             = {0x02},
			.SourcePinID              = {0x01},

			.JackStrIndex             = NO_DESCRIPTOR
		},

	.MIDI_Out_Jack_Ext =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_OutputJack_t), .Type = DTYPE_CSInterface},
			.Subtype                  = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,

			.JackType                 = MIDI_JACKTYPE_External,
			.JackID                   = 0x04,

			.NumberOfPins             = 1,
			.SourceJackID             = {0x01},
			.SourcePinID              = {0x01},

			.JackStrIndex             = NO_DESCRIPTOR
		},

	.MIDI_In_Jack_Endpoint =
		{
			.Endpoint =
				{
					.Header              = {.Size = sizeof(USB_Audio_Descriptor_StreamEndpoint_Std_t), .Type = DTYPE_Endpoint},

					.EndpointAddress     = MIDI_STREAM_OUT_EPADDR,
					.Attributes          = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
					.EndpointSize        = MIDI_STREAM_EPSIZE,
					.PollingIntervalMS   = 0x05
				},

			.Refresh                  = 0,
			.SyncEndpointNumber       = 0
		},

	.MIDI_In_Jack_Endpoint_SPC =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_Jack_Endpoint_t), .Type = DTYPE_CSEndpoint},
			.Subtype                  = AUDIO_DSUBTYPE_CSEndpoint_General,

			.TotalEmbeddedJacks       = 0x01,
			.AssociatedJackID         = {0x01}
		},

	.MIDI_Out_Jack_Endpoint =
		{
			.Endpoint =
				{
					.Header              = {.Size = sizeof(USB_Audio_Descriptor_StreamEndpoint_Std_t), .Type = DTYPE_Endpoint},

					.EndpointAddress     = MIDI_STREAM_IN_EPADDR,
					.Attributes          = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
					.EndpointSize        = MIDI_STREAM_EPSIZE,
					.PollingIntervalMS   = 0x05
				},

			.Refresh                  = 0,
			.SyncEndpointNumber       = 0
		},

	.MIDI_Out_Jack_Endpoint_SPC =
		{
			.Header                   = {.Size = sizeof(USB_MIDI_Descriptor_Jack_Endpoint_t), .Type = DTYPE_CSEndpoint},
			.Subtype                  = AUDIO_DSUBTYPE_CSEndpoint_General,

			.TotalEmbeddedJacks       = 0x01,
			.AssociatedJackID         = {0x03}
		}
};

/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests
 *  the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate
 *  via the language ID table available at USB.org what languages the device supports for its string descriptors.
 */
const USB_Descriptor_String_t PROGMEM LanguageString =
{
	.Header                 = {.Size = USB_STRING_LEN(1), .Type = DTYPE_String},

	.UnicodeString          = {LANGUAGE_ID_ENG}
};

/** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable
 *  form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ManufacturerString =
{
	.Header                 = {.Size = USB_STRING_LEN(11), .Type = DTYPE_String},

	.UnicodeString          = L"Dean Camera"
};

/** Product descriptor string. This is a Unicode string containing the product's details in human readable form,
 *  and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ProductString =
{
	.Header                 = {.Size = USB_STRING_LEN(14), .Type = DTYPE_String},

	.UnicodeString          = L"LUFA MIDI Demo"
};

/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
 *  documentation) by the application code so that the address and size of a requested descriptor can be given
 *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
 *  is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the
 *  USB host.
 */
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
                                    const uint8_t wIndex,
                                    const void** const DescriptorAddress)
{
	const uint8_t  DescriptorType   = (wValue >> 8);
	const uint8_t  DescriptorNumber = (wValue & 0xFF);

	const void* Address = NULL;
	uint16_t    Size    = NO_DESCRIPTOR;

	switch (DescriptorType)
	{
		case DTYPE_Device:
			Address = &DeviceDescriptor;
			Size    = sizeof(USB_Descriptor_Device_t);
			break;
		case DTYPE_Configuration:
			Address = &ConfigurationDescriptor;
			Size    = sizeof(USB_Descriptor_Configuration_t);
			break;
		case DTYPE_String:
			switch (DescriptorNumber)
			{
				case 0x00:
					Address = &LanguageString;
					Size    = pgm_read_byte(&LanguageString.Header.Size);
					break;
				case 0x01:
					Address = &ManufacturerString;
					Size    = pgm_read_byte(&ManufacturerString.Header.Size);
					break;
				case 0x02:
					Address = &ProductString;
					Size    = pgm_read_byte(&ProductString.Header.Size);
					break;
			}

			break;
	}

	*DescriptorAddress = Address;
	return Size;
}