LUFA - HID with 3 endpoints

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, 

 

Is it possible to have a one HID device with 3 endpoints using LUFA?

What I'm actually trying to do is:

 

1. Joystick at endpoint 1.

2. IN on endpoint 2.

3. Out on endpoint 3.

 

The MCU is at90usb1287.

 

I read in the LUFA code somewhere that currently it doesn't support OUT endpoints for the HID and it's using control endpoint instead. I guess this could work but I would have to check (I'm not the one who designed the host software for the communication). What I don't know is if it's possible to have a Joystick with 2 IN endpoints. One for the actual joystick and one for sending some custom data.

 

Cheers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess that's impossible hence the silence..

I'm still on it. I managed to enumerate as a single device with 3 endpoints, but with following issues:

 

- Don't know how to make the 3rd endpoint be of type OUT.

- The 2nd IN endpoint seems to be crashing things in some way when creating report.

 

Here is my Descriptors.h:

 

/*
             LUFA Library
     Copyright (C) Dean Camera, 2017.

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

/*
  Copyright 2017  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
 *
 *  Header file for Descriptors.c.
 */

#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_

	/* Includes: */
		#include <avr/pgmspace.h>

		#include <LUFA/Drivers/USB/USB.h>

		#include "Config/AppConfig.h"

	#ifdef CDC_DEBUG
		/* Macros: */
		/** Endpoint address of the CDC device-to-host notification IN endpoint. */
		#define CDC_NOTIFICATION_EPADDR        (ENDPOINT_DIR_IN  | 4)

		/** Endpoint address of the CDC device-to-host data IN endpoint. */
		#define CDC_TX_EPADDR                  (ENDPOINT_DIR_IN  | 5)

		/** Endpoint address of the CDC host-to-device data OUT endpoint. */
		#define CDC_RX_EPADDR                  (ENDPOINT_DIR_OUT | 6)

		/** Size in bytes of the CDC device-to-host notification IN endpoint. */
		#define CDC_NOTIFICATION_EPSIZE        8

		/** Size in bytes of the CDC data IN and OUT endpoints. */
		#define CDC_TXRX_EPSIZE                16
	#endif

	/* Type Defines: */
		/** Type define for the device configuration descriptor structure. This must be defined in the
		 *  application code, as the configuration descriptor contains several sub-descriptors which
		 *  vary between devices, and which describe the device's usage to the host.
		 */
		typedef struct
		{
			USB_Descriptor_Configuration_Header_t Config;

			USB_Descriptor_Interface_t            HID_JoystickInterface;
			USB_HID_Descriptor_HID_t              HID_JoystickDescriptor;
			USB_Descriptor_Endpoint_t             HID_JoystickReportINEndpoint;
			USB_Descriptor_Endpoint_t             HID_INEndpoint;
			USB_Descriptor_Endpoint_t             HID_OUTEndpoint;

		#ifdef CDC_DEBUG
			// CDC Control Interface
			USB_Descriptor_Interface_Association_t   CDC_IAD;
			USB_Descriptor_Interface_t               CDC_CCI_Interface;
			USB_CDC_Descriptor_FunctionalHeader_t    CDC_Functional_Header;
			USB_CDC_Descriptor_FunctionalACM_t       CDC_Functional_ACM;
			USB_CDC_Descriptor_FunctionalUnion_t     CDC_Functional_Union;
			USB_Descriptor_Endpoint_t                CDC_NotificationEndpoint;

			// CDC Data Interface
			USB_Descriptor_Interface_t               CDC_DCI_Interface;
			USB_Descriptor_Endpoint_t                CDC_DataOutEndpoint;
			USB_Descriptor_Endpoint_t                CDC_DataInEndpoint;
		#endif

		} USB_Descriptor_Configuration_t;

		/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
		 *  should have a unique ID index associated with it, which can be used to refer to the
		 *  interface from other descriptors.
		 */
		enum InterfaceDescriptors_t
		{
			INTERFACE_ID_Joystick		= 1, /**< Joystick interface descriptor ID */
		#ifdef CDC_DEBUG
			INTERFACE_ID_CDC_CCI		= 2, /**< CDC CCI interface descriptor ID */
			INTERFACE_ID_CDC_DCI		= 3, /**< CDC DCI interface descriptor ID */
		#endif
		};

		/** Enum for the device string descriptor IDs within the device. Each string descriptor should
		 *  have a unique ID index associated with it, which can be used to refer to the string from
		 *  other descriptors.
		 */
		enum StringDescriptors_t
		{
			STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
			STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
			STRING_ID_Product      = 2, /**< Product string ID */
			STRING_ID_Serial	   = 3, /**< Product serial ID */
		};

	/* Macros: */

		#define JOYSTICK_IN_EPNUM 1
		/** Endpoint address of the Joystick HID reporting IN endpoint. */
		#define JOYSTICK_EPADDR              (ENDPOINT_DIR_IN | JOYSTICK_IN_EPNUM)

		/** Size in bytes of the Joystick HID reporting IN endpoint. */
		#define JOYSTICK_EPSIZE              8

		#define HID_IN_EPNUM 2
		/** Endpoint address of the Generic HID reporting IN endpoint. */
		#define HID_IN_EPADDR         (ENDPOINT_DIR_IN | HID_IN_EPNUM)

		/** Size in bytes of the Generic HID reporting endpoint. */
		#define HID_IN_EPSIZE            64

		#define HID_OUT_EPNUM 3
		/** Endpoint address of the Generic HID reporting IN endpoint. */
		#define HID_OUT_EPADDR         (ENDPOINT_DIR_IN | HID_OUT_EPNUM)

		/** Size in bytes of the Generic HID reporting endpoint. */
		#define HID_OUT_EPSIZE            10

	/* Function Prototypes: */
		uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
		                                    const uint16_t wIndex,
		                                    const void** const DescriptorAddress)
		                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);

#endif

And Descriptors.c:

/*
             LUFA Library
     Copyright (C) Dean Camera, 2017.

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

/*
  Copyright 2017  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"
#include "EEPROMDefines.h"
#include "DeviceType.h"
#include "Version.h"
#include <avr/eeprom.h>

/** HID class report descriptor. This is a special descriptor constructed with values from the
 *  USBIF HID class specification to describe the reports and capabilities of the HID device. This
 *  descriptor is parsed by the host and its contents used to determine what data (and in what encoding)
 *  the device will send, and what it may be sent back from the host. Refer to the HID specification for
 *  more details on HID report descriptors.
 */
const USB_Descriptor_HIDReport_Datatype_t PROGMEM GenericReport[] =
{
	/* Use the HID class driver's standard Vendor HID report.
	 *  Vendor Usage Page: 0
	 *  Vendor Collection Usage: 1
	 *  Vendor Report IN Usage: 2
	 *  Vendor Report OUT Usage: 3
	 *  Vendor Report Size: GENERIC_REPORT_SIZE
	 */
	HID_DESCRIPTOR_VENDOR(0x00, 0x01, 0x02, 0x03, GENERIC_REPORT_SIZE)
};

/** HID class report descriptor. This is a special descriptor constructed with values from the
 *  USBIF HID class specification to describe the reports and capabilities of the HID device. This
 *  descriptor is parsed by the host and its contents used to determine what data (and in what encoding)
 *  the device will send, and what it may be sent back from the host. Refer to the HID specification for
 *  more details on HID report descriptors.
 */
const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] =
{
	/* Use the HID class driver's standard Joystick report.
	 *   Min X/Y/Z Axis values: -100
	 *   Max X/Y/Z Axis values:  100
	 *   Min physical X/Y/Z Axis values (used to determine resolution): -1
	 *   Max physical X/Y/Z Axis values (used to determine resolution):  1
	 *   Buttons: 2
	 */
	HID_DESCRIPTOR_JOYSTICK(-100, 100, -1, 1, 2)
};

/** 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(1,1,0),

#ifdef CDC_DEBUG
	.Class                  = USB_CSCP_IADDeviceClass,
	.SubClass               = USB_CSCP_IADDeviceSubclass,
	.Protocol               = USB_CSCP_IADDeviceProtocol,
#else
	.Class                  = USB_CSCP_NoDeviceClass,
	.SubClass               = USB_CSCP_NoDeviceSubclass,
	.Protocol               = USB_CSCP_NoDeviceProtocol,
#endif

	.Endpoint0Size          = FIXED_CONTROL_ENDPOINT_SIZE,

	.VendorID               = USB_VID,
	.ProductID              = USB_PID,
	.ReleaseNumber          = VERSION_BCD(AVIATEK_VERSION_MAJOR,AVIATEK_VERSION_MINOR,AVIATEK_VERSION_PATCH),

	.ManufacturerStrIndex   = STRING_ID_Manufacturer,
	.ProductStrIndex        = STRING_ID_Product,
	.SerialNumStrIndex      = NO_DESCRIPTOR,//STRING_ID_Serial,

	.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),
		#ifdef CDC_DEBUG
			.TotalInterfaces        = 3,
		#else
			.TotalInterfaces		= 1,
		#endif
			.ConfigurationNumber    = 1,
			.ConfigurationStrIndex  = NO_DESCRIPTOR,

			.ConfigAttributes       = (USB_CONFIG_ATTR_RESERVED),

			.MaxPowerConsumption    = USB_CONFIG_POWER_MA(450)
		},

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

			.InterfaceNumber        = INTERFACE_ID_Joystick,
			.AlternateSetting       = 0x00,

			.TotalEndpoints         = 3,

			.Class                  = HID_CSCP_HIDClass,
			.SubClass               = HID_CSCP_NonBootSubclass,
			.Protocol               = HID_CSCP_NonBootProtocol,

			.InterfaceStrIndex      = NO_DESCRIPTOR
		},

	.HID_JoystickDescriptor =
		{
			.Header                 = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID},

			.HIDSpec                = VERSION_BCD(1,1,1),
			.CountryCode            = 0x00,
			.TotalReportDescriptors = 1,
			.HIDReportType          = HID_DTYPE_Report,
			.HIDReportLength        = sizeof(JoystickReport)
		},

	.HID_JoystickReportINEndpoint =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

			.EndpointAddress        = JOYSTICK_EPADDR,
			.Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
			.EndpointSize           = JOYSTICK_EPSIZE,
			.PollingIntervalMS      = 0x05
		},

	.HID_INEndpoint =
	{
		.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

		.EndpointAddress        = HID_IN_EPADDR,
		.Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
		.EndpointSize           = HID_IN_EPSIZE,
		.PollingIntervalMS      = 0x05
	},

	.HID_OUTEndpoint =
	{
		.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

		.EndpointAddress        = HID_OUT_EPADDR,
		.Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
		.EndpointSize           = HID_OUT_EPSIZE,
		.PollingIntervalMS      = 0x05
	},

	#ifdef CDC_DEBUG
	.CDC_IAD =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Interface_Association_t), .Type = DTYPE_InterfaceAssociation},

			.FirstInterfaceIndex    = INTERFACE_ID_CDC_CCI,
			.TotalInterfaces        = 2,

			.Class                  = CDC_CSCP_CDCClass,
			.SubClass               = CDC_CSCP_ACMSubclass,
			.Protocol               = CDC_CSCP_ATCommandProtocol,

			.IADStrIndex            = NO_DESCRIPTOR
		},

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

			.InterfaceNumber        = INTERFACE_ID_CDC_CCI,
			.AlternateSetting       = 0,

			.TotalEndpoints         = 1,

			.Class                  = CDC_CSCP_CDCClass,
			.SubClass               = CDC_CSCP_ACMSubclass,
			.Protocol               = CDC_CSCP_ATCommandProtocol,

			.InterfaceStrIndex      = NO_DESCRIPTOR
		},

	.CDC_Functional_Header =
		{
			.Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalHeader_t), .Type = DTYPE_CSInterface},
			.Subtype                = CDC_DSUBTYPE_CSInterface_Header,

			.CDCSpecification       = VERSION_BCD(1,1,0),
		},

	.CDC_Functional_ACM =
		{
			.Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalACM_t), .Type = DTYPE_CSInterface},
			.Subtype                = CDC_DSUBTYPE_CSInterface_ACM,

			.Capabilities           = 0x06,
		},

	.CDC_Functional_Union =
		{
			.Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalUnion_t), .Type = DTYPE_CSInterface},
			.Subtype                = CDC_DSUBTYPE_CSInterface_Union,

			.MasterInterfaceNumber  = INTERFACE_ID_CDC_CCI,
			.SlaveInterfaceNumber   = INTERFACE_ID_CDC_DCI,
		},

	.CDC_NotificationEndpoint =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

			.EndpointAddress        = CDC_NOTIFICATION_EPADDR,
			.Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
			.EndpointSize           = CDC_NOTIFICATION_EPSIZE,
			.PollingIntervalMS      = 0xFF
		},

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

			.InterfaceNumber        = INTERFACE_ID_CDC_DCI,
			.AlternateSetting       = 0,

			.TotalEndpoints         = 2,

			.Class                  = CDC_CSCP_CDCDataClass,
			.SubClass               = CDC_CSCP_NoDataSubclass,
			.Protocol               = CDC_CSCP_NoDataProtocol,

			.InterfaceStrIndex      = NO_DESCRIPTOR
		},

	.CDC_DataOutEndpoint =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

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

	.CDC_DataInEndpoint =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

			.EndpointAddress        = CDC_TX_EPADDR,
			.Attributes             = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
			.EndpointSize           = CDC_TXRX_EPSIZE,
			.PollingIntervalMS      = 0x05
		},
	#endif
};

/** 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 = USB_STRING_DESCRIPTOR_ARRAY(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 = USB_STRING_DESCRIPTOR(L"Test");

/** 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 = USB_STRING_DESCRIPTOR(L"Test Device");

/** Product serial number string.
 */
const USB_Descriptor_String_t PROGMEM SerialString = USB_STRING_DESCRIPTOR(L"12345678901");

uint16_t serial[MEM_LEN_SERIAL];
bool serialRead = false;

void readSerial()
{
	for(unsigned int i = 0; i < (unsigned int)MEM_LEN_SERIAL; i++)
	{
		eeprom_read_block(&serial[i], MEM_OFFSET_SERIAL+(i*sizeof(char)), sizeof(char));
	}
}

void CALLBACK_USB_GetCustomSerial(uint16_t* const UnicodeString)
{
	if(!serialRead)
	{
		readSerial();
		serialRead = true;
	}

	for(int i=0; i < MEM_LEN_SERIAL; ++i)
	{
		UnicodeString[i] = serial[i];
	}
}

/** 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 uint16_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 STRING_ID_Language:
					Address = &LanguageString;
					Size    = pgm_read_byte(&LanguageString.Header.Size);
				break;
				case STRING_ID_Manufacturer:
					Address = &ManufacturerString;
					Size    = pgm_read_byte(&ManufacturerString.Header.Size);
				break;
				case STRING_ID_Product:
					Address = &ProductString;
					Size    = pgm_read_byte(&ProductString.Header.Size);
				break;
				case STRING_ID_Serial:
				{
					struct
					{
						USB_Descriptor_Header_t Header;
						uint16_t                UnicodeString[MEM_LEN_SERIAL];
					} SignatureDescriptor;

					SignatureDescriptor.Header.Type = DTYPE_String;
					SignatureDescriptor.Header.Size = USB_STRING_LEN(MEM_LEN_SERIAL);

					CALLBACK_USB_GetCustomSerial(SignatureDescriptor.UnicodeString);

					Endpoint_ClearSETUP();

					Endpoint_Write_Control_Stream_LE(&SignatureDescriptor, sizeof(SignatureDescriptor));
					Endpoint_ClearOUT();
				}
				break;
			}
			break;
		}
		case HID_DTYPE_HID:
		{
			switch(wIndex)
			{
				case INTERFACE_ID_Joystick:
					Address = &ConfigurationDescriptor.HID_JoystickDescriptor;
					Size    = sizeof(USB_HID_Descriptor_HID_t);
					break;
				/*case INTERFACE_ID_HID_IN:
					Address = &ConfigurationDescriptor.HID_IN_Descriptor;
					Size    = sizeof(USB_HID_Descriptor_HID_t);
				break;
				case INTERFACE_ID_HID_OUT:
					//Address = &ConfigurationDescriptor.HID_OUT_Descriptor;
					Size    = sizeof(USB_HID_Descriptor_HID_t);
				break;*/
			}
			break;
		}
		case HID_DTYPE_Report:
		{
			switch(wIndex)
			{
				case INTERFACE_ID_Joystick:
					Address = &JoystickReport;
					Size = sizeof(JoystickReport);
				break;
				/*case INTERFACE_ID_HID_IN:
					Address = &GenericReport;
					Size    = sizeof(GenericReport);
				break;
				case INTERFACE_ID_HID_OUT:
					Address = &GenericReport;
					Size    = sizeof(GenericReport);
				break;*/
			}
			break;
		}
		break;
	}

	*DescriptorAddress = Address;
	return Size;
}

main.h

#ifndef _AVIATEKCONNECTOR_H_
#define _AVIATEKCONNECTOR_H_

	extern "C" {
	/* Includes: */
		#include <avr/io.h>
		#include <avr/wdt.h>
		#include <avr/power.h>
		#include <avr/interrupt.h>
		#include <string.h>

		#include "Descriptors.h"
		#include "Config/AppConfig.h"

		#include <LUFA/Drivers/Board/LEDs.h>
		#include <LUFA/Drivers/USB/USB.h>
		#include <LUFA/Platform/Platform.h>
		}

			/* Macros: */
			/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
			#define LEDMASK_USB_NOTREADY      LEDS_LED1

			/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
			#define LEDMASK_USB_ENUMERATING  (LEDS_LED2 | LEDS_LED3)

			/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
			#define LEDMASK_USB_READY        (LEDS_LED2 | LEDS_LED4)

			/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
			#define LEDMASK_USB_ERROR        (LEDS_LED1 | LEDS_LED3)

			/** Type define for the joystick HID report structure, for creating and sending HID reports to the host PC.
		 *  This mirrors the layout described to the host in the HID report descriptor, in Descriptors.c.
		 */
		typedef struct
		{
			int8_t  X; /**< Current absolute joystick X position, as a signed 8-bit integer */
			int8_t  Y; /**< Current absolute joystick Y position, as a signed 8-bit integer */
			int8_t  Z; /**< Current absolute joystick Z position, as a signed 8-bit integer */
			uint8_t Button; /**< Bit mask of the currently pressed joystick buttons */
		} USB_JoystickReport_Data_t;

	/* Function Prototypes: */
		void SetupHardware(void);

		void EVENT_USB_Device_Connect(void);
		void EVENT_USB_Device_Disconnect(void);
		void EVENT_USB_Device_ConfigurationChanged(void);
		void EVENT_USB_Device_ControlRequest(void);
		void EVENT_USB_Device_StartOfFrame(void);

		bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
		                                         uint8_t* const ReportID,
		                                         const uint8_t ReportType,
		                                         void* ReportData,
		                                         uint16_t* const ReportSize);
		void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
		                                          const uint8_t ReportID,
		                                          const uint8_t ReportType,
		                                          const void* ReportData,
		                                          const uint16_t ReportSize);

#endif

And finally main.c

#include "main.h"

#ifdef CDC_DEBUG
#include <CDCDebug.h>
#endif

typedef struct testStruct
{
	unsigned char testOne;
	unsigned int testValue;
} HIDREPORT_TEST_DATA;

/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevHID_INReportBuffer[HID_IN_EPSIZE];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t HID_IN_Interface =
{
	.Config =
	{
		.InterfaceNumber              = INTERFACE_ID_Joystick,
		.ReportINEndpoint             =
		{
			.Address              = HID_IN_EPADDR,
			.Size                 = HID_IN_EPSIZE,
			.Type				  = 0,
			.Banks                = 1,
		},
		.PrevReportINBuffer           = PrevHID_INReportBuffer,
		.PrevReportINBufferSize       = sizeof(PrevHID_INReportBuffer),
	},
};

/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevHID_OUTReportBuffer[GENERIC_REPORT_SIZE];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t HID_OUT_Interface =
{
	.Config =
	{
		.InterfaceNumber              = INTERFACE_ID_Joystick,
		.ReportINEndpoint             =
		{
			.Address              = HID_OUT_EPADDR,
			.Size                 = HID_OUT_EPSIZE,
			.Type				  = 0,
			.Banks                = 1,
		},
		.PrevReportINBuffer           = PrevHID_OUTReportBuffer,
		.PrevReportINBufferSize       = sizeof(PrevHID_OUTReportBuffer),
	},
};

/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevJoystickHIDReportBuffer[sizeof(USB_JoystickReport_Data_t)];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t Joystick_HID_Interface =
	{
		.Config =
			{
				.InterfaceNumber              = INTERFACE_ID_Joystick,
				.ReportINEndpoint             =
					{
						.Address              = JOYSTICK_EPADDR,
						.Size                 = JOYSTICK_EPSIZE,
						.Type				  = 0,
						.Banks                = 1,
					},
				.PrevReportINBuffer           = PrevJoystickHIDReportBuffer,
				.PrevReportINBufferSize       = sizeof(PrevJoystickHIDReportBuffer),
			},
	};

#ifdef CDC_DEBUG
/** LUFA CDC Class driver interface configuration and state information. This structure is
 *  passed to all CDC Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
	{
		.Config =
			{
				.ControlInterfaceNumber         = INTERFACE_ID_CDC_CCI,
				.DataINEndpoint                 =
					{
						.Address                = CDC_TX_EPADDR,
						.Size                   = CDC_TXRX_EPSIZE,
						.Type					= 0,
						.Banks                  = 1,
					},
				.DataOUTEndpoint                =
					{
						.Address                = CDC_RX_EPADDR,
						.Size                   = CDC_TXRX_EPSIZE,
						.Type				    = 0,
						.Banks                  = 1,
					},
				.NotificationEndpoint           =
					{
						.Address                = CDC_NOTIFICATION_EPADDR,
						.Size                   = CDC_NOTIFICATION_EPSIZE,
						.Type				    = 0,
						.Banks                  = 1,
					},
			},
	};
#endif

uint32_t Boot_Key ATTR_NO_INIT;
#define MAGIC_BOOT_KEY				0xDC42ACCA
#if defined(__AVR_AT90USB1287__)
#define FLASH_SIZE_BYTES			128 * 1000
#define BOOTLOADER_SEC_SIZE_BYTES	8 * 1000
#elif defined(__AVR_ATmega32U4__)
#define FLASH_SIZE_BYTES			32 * 1000
#define BOOTLOADER_SEC_SIZE_BYTES	4 * 1000
#else
#error "MCU Not supported"
#endif
#define BOOTLOADER_START_ADDRESS  (FLASH_SIZE_BYTES - BOOTLOADER_SEC_SIZE_BYTES)

void Bootloader_Jump_Check(void) ATTR_INIT_SECTION(3);
void Bootloader_Jump_Check(void)
{
    // If the reset source was the bootloader and the key is correct, clear it and jump to the bootloader
    if ((MCUSR & (1 << WDRF)) && (Boot_Key == MAGIC_BOOT_KEY))
    {
        Boot_Key = 0;
        ((void (*)(void))BOOTLOADER_START_ADDRESS)();
    }
}

void Jump_To_Bootloader(void)
{
    // If USB is used, detach from the bus and reset it
    USB_Disable();

    // Disable all interrupts
    cli();

    // Wait two seconds for the USB detachment to register on the host
    Delay_MS(2000);

    // Set the bootloader key to the magic value and force a reset
    Boot_Key = MAGIC_BOOT_KEY;
    wdt_enable(WDTO_250MS);
    for (;;);
}

bool triggerInput = false;
int encPin = 39;

/** Main program entry point. This routine contains the overall program flow, including initial
 *  setup of all components and the main program loop.
 */
int main(void)
{
	SetupHardware();

#ifdef CDC_DEBUG
	/* Create a regular character stream for the interface so that it can be used with the stdio.h functions */
	FILE* stream = CDCDebug::getStream();
	CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, stream);
#endif	

	LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
	GlobalInterruptEnable();

	pinMode(encPin, INPUT_PULLUP);
	int encState = digitalRead(encPin);

	for (;;)
	{
		int temp = digitalRead(encPin);
		if(temp != encState)
		{
			encState = temp;
			triggerInput = triggerInput || temp == LOW;
		}

		HID_Device_USBTask(&Joystick_HID_Interface);
		HID_Device_USBTask(&HID_IN_Interface);
		HID_Device_USBTask(&HID_OUT_Interface);

	#ifdef CDC_DEBUG
		/* Must throw away unused bytes from the host, or it will lock up while waiting for the device */
		CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
		CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
	#endif

		USB_USBTask();
	}
}

/** Configures the board hardware and chip peripherals for the demo's functionality. */
void SetupHardware(void)
{
#if (ARCH == ARCH_AVR8)
	/* Disable watchdog if enabled by bootloader/fuses */
	MCUSR &= ~(1 << WDRF);
	wdt_disable();

	/* Disable clock division */
	clock_prescale_set(clock_div_1);

#elif (ARCH == ARCH_XMEGA)
	/* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */
	XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
	XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);

	/* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */
	XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
	XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);

	PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
#endif

	/* Hardware Initialization */
	LEDs_Init();
	USB_Init();
}

/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
	LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);
}

/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
	LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
}

/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
	bool ConfigSuccess = true;

	ConfigSuccess &= HID_Device_ConfigureEndpoints(&Joystick_HID_Interface);
	ConfigSuccess &= HID_Device_ConfigureEndpoints(&HID_IN_Interface);
	ConfigSuccess &= HID_Device_ConfigureEndpoints(&HID_OUT_Interface);

#ifdef CDC_DEBUG
	ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
#endif

	USB_Device_EnableSOFEvents();

	LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR);
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
	HID_Device_ProcessControlRequest(&Joystick_HID_Interface);
	HID_Device_ProcessControlRequest(&HID_IN_Interface);
	HID_Device_ProcessControlRequest(&HID_OUT_Interface);
#ifdef CDC_DEBUG
	CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
#endif
}

#ifdef CDC_DEBUG
/** CDC class driver callback function the processing of changes to the virtual
 *  control lines sent from the host..
 *
 *  \param[in] CDCInterfaceInfo  Pointer to the CDC class interface configuration structure being referenced
 */
void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo)
{
	/* You can get changes to the virtual CDC lines in this callback; a common
	   use-case is to use the Data Terminal Ready (DTR) flag to enable and
	   disable CDC communications in your application when set to avoid the
	   application blocking while waiting for a host to become ready and read
	   in the pending data from the USB endpoints.
	*/
	bool HostReady = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0;
}
#endif

/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
	HID_Device_MillisecondElapsed(&Joystick_HID_Interface);
	HID_Device_MillisecondElapsed(&HID_IN_Interface);
	HID_Device_MillisecondElapsed(&HID_OUT_Interface);
}

/** HID class driver callback function for the creation of HID reports to the host.
 *
 *  \param[in]     HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in,out] ReportID    Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
 *  \param[in]     ReportType  Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
 *  \param[out]    ReportData  Pointer to a buffer where the created report should be stored
 *  \param[out]    ReportSize  Number of bytes written in the report (or zero if no report is to be sent)
 *
 *  \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
 */
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
	if(HIDInterfaceInfo == &Joystick_HID_Interface)
	{
		USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;
		JoystickReport->Y = 100;
		JoystickReport->X = 100;
		JoystickReport->Button |= digitalRead(encPin) == LOW ? (1 << 1) : (1 << 0);

		*ReportSize = sizeof(USB_JoystickReport_Data_t);
	}
	else if(HIDInterfaceInfo == &HID_IN_Interface)
	{
		if(triggerInput)
		{

			HIDREPORT_TEST_DATA* report = (HIDREPORT_TEST_DATA*)ReportData;
			report->testOne = 1;
			report->testValue = 2;

			*ReportSize = sizeof(HIDREPORT_TEST_DATA);
			triggerInput = false;
		}
	}
	return false;
}

/** HID class driver callback function for the processing of HID reports from the host.
 *
 *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in] ReportID    Report ID of the received report from the host
 *  \param[in] ReportType  The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
 *  \param[in] ReportData  Pointer to a buffer where the received report has been stored
 *  \param[in] ReportSize  Size in bytes of the received HID report
 */
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                          const uint8_t ReportID,
                                          const uint8_t ReportType,
                                          const void* ReportData,
                                          const uint16_t ReportSize)
{
	// Never gets here..
}

 

The CDC_DEBUG is currently not defined. 

 

I'm lost! :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I probably should use a different words when saying "The 2nd IN endpoint seems to be crashing things in some way when creating report. ".

 

What happens is the following.

 

1. I press a hardware button that sets the triggerInput=true.

2. It enters the block in the CALLBACK_HID_Device_CreateHIDReport responsible for creating report the this endpoint:

else if(HIDInterfaceInfo == &HID_IN_Interface)
	{
		if(triggerInput)
		{

			HIDREPORT_TEST_DATA* report = (HIDREPORT_TEST_DATA*)ReportData;
			report->testOne = 1;
			report->testValue = 2;

			*ReportSize = sizeof(HIDREPORT_TEST_DATA);
			triggerInput = false;
		}
	}

3. If I press the button again to try it again, it never enters there because for some reason Endpoint_IsReadWriteAllowed() returns false for this endpoint inside the HID_Device_USBTask(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo) in the HIDClassDevice.c. So I guess this endpoint is not configured properly and things goes south after the first try..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd contact Dean. On twitter he is active as @abcminiuser