From d4bb1157eb11945b1c05a9eecf5e48cbcdc48b8d Mon Sep 17 00:00:00 2001 From: Francesco Valla Date: Sun, 12 Jun 2016 20:08:08 +0200 Subject: [PATCH] hid_keyboard: Add HID keyboard example In this example, the device is seen as an HID keyboard. When a pin (in this case RA3) is sensed low, a CTRL+SHIFT+1 keypress is sent to the OS. Signed-off-by: Francesco Valla --- apps/hid_keyboard/.gitignore | 8 + apps/hid_keyboard/MPLAB.X/Makefile | 108 +++ .../MPLAB.X/nbproject/configurations.xml | 851 ++++++++++++++++++ .../MPLAB.X/nbproject/project.xml | 17 + apps/hid_keyboard/main.c | 310 +++++++ apps/hid_keyboard/usb_config.h | 101 +++ apps/hid_keyboard/usb_descriptors.c | 300 ++++++ 7 files changed, 1695 insertions(+) create mode 100644 apps/hid_keyboard/.gitignore create mode 100644 apps/hid_keyboard/MPLAB.X/Makefile create mode 100644 apps/hid_keyboard/MPLAB.X/nbproject/configurations.xml create mode 100644 apps/hid_keyboard/MPLAB.X/nbproject/project.xml create mode 100644 apps/hid_keyboard/main.c create mode 100644 apps/hid_keyboard/usb_config.h create mode 100644 apps/hid_keyboard/usb_descriptors.c diff --git a/apps/hid_keyboard/.gitignore b/apps/hid_keyboard/.gitignore new file mode 100644 index 0000000..e0622e2 --- /dev/null +++ b/apps/hid_keyboard/.gitignore @@ -0,0 +1,8 @@ +MPLAB.X/nbproject/Makefile-genesis.properties +MPLAB.X/nbproject/Makefile-*.mk +MPLAB.X/nbproject/Package-*.bash +MPLAB.X/nbproject/private/ +MPLAB.X/build/ +MPLAB.X/dist +MPLAB.X/funclist +MPLAB.X/disassembly/ diff --git a/apps/hid_keyboard/MPLAB.X/Makefile b/apps/hid_keyboard/MPLAB.X/Makefile new file mode 100644 index 0000000..05a3fb1 --- /dev/null +++ b/apps/hid_keyboard/MPLAB.X/Makefile @@ -0,0 +1,108 @@ +# +# There exist several targets which are by default empty and which can be +# used for execution of your targets. These targets are usually executed +# before and after some main targets. They are: +# +# .build-pre: called before 'build' target +# .build-post: called after 'build' target +# .clean-pre: called before 'clean' target +# .clean-post: called after 'clean' target +# .clobber-pre: called before 'clobber' target +# .clobber-post: called after 'clobber' target +# .all-pre: called before 'all' target +# .all-post: called after 'all' target +# .help-pre: called before 'help' target +# .help-post: called after 'help' target +# +# Targets beginning with '.' are not intended to be called on their own. +# +# Main targets can be executed directly, and they are: +# +# build build a specific configuration +# clean remove built files from a configuration +# clobber remove all built files +# all build all configurations +# help print help mesage +# +# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and +# .help-impl are implemented in nbproject/makefile-impl.mk. +# +# Available make variables: +# +# CND_BASEDIR base directory for relative paths +# CND_DISTDIR default top distribution directory (build artifacts) +# CND_BUILDDIR default top build directory (object files, ...) +# CONF name of current configuration +# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) +# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) +# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) +# CND_PACKAGE_DIR_${CONF} directory of package (current configuration) +# CND_PACKAGE_NAME_${CONF} name of package (current configuration) +# CND_PACKAGE_PATH_${CONF} path to package (current configuration) +# +# NOCDDL + + +# Environment +MKDIR=mkdir +CP=cp +CCADMIN=CCadmin +RANLIB=ranlib + + +# build +build: .build-post + +.build-pre: +# Add your pre 'build' code here... + +.build-post: .build-impl +# Add your post 'build' code here... + + +# clean +clean: .clean-post + +.clean-pre: +# Add your pre 'clean' code here... + +.clean-post: .clean-impl +# Add your post 'clean' code here... + + +# clobber +clobber: .clobber-post + +.clobber-pre: +# Add your pre 'clobber' code here... + +.clobber-post: .clobber-impl +# Add your post 'clobber' code here... + + +# all +all: .all-post + +.all-pre: +# Add your pre 'all' code here... + +.all-post: .all-impl +# Add your post 'all' code here... + + +# help +help: .help-post + +.help-pre: +# Add your pre 'help' code here... + +.help-post: .help-impl +# Add your post 'help' code here... + + + +# include project implementation makefile +include nbproject/Makefile-impl.mk + +# include project make variables +include nbproject/Makefile-variables.mk diff --git a/apps/hid_keyboard/MPLAB.X/nbproject/configurations.xml b/apps/hid_keyboard/MPLAB.X/nbproject/configurations.xml new file mode 100644 index 0000000..bf06841 --- /dev/null +++ b/apps/hid_keyboard/MPLAB.X/nbproject/configurations.xml @@ -0,0 +1,851 @@ + + + + + + + ../../bootloader/firmware/gld/pic24fj64gb002-bootloader.gld + ../../bootloader/firmware/gld/pic24fj256da206-bootloader.gld + + + + ../../../usb/src/usb.c + ../../../usb/include/usb.h + ../../../usb/src/usb_hal.h + ../../../usb/include/usb_ch9.h + ../../../usb/src/usb_hid.c + ../../../usb/include/usb_hid.h + + ../usb_descriptors.c + ../main.c + ../usb_config.h + + + Makefile + + + + ../../bootloader/firmware/gld + ../../../usb/src + ../../../usb/include + + Makefile + + + + localhost + PIC24FJ64GB002 + + + ICD3PlatformTool + XC16 + 1.11 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC24FJ64GB002 + + + ICD3PlatformTool + XC16 + 1.11 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC24FJ256DA206 + + + ICD3PlatformTool + XC16 + 1.11 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC24FJ256DA206 + + + ICD3PlatformTool + XC16 + 1.11 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC18F46J50 + + + SKDEPIC18FJPlatformTool + XC8 + 1.12 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC16F1459 + + + PICkit3PlatformTool + XC8 + 1.12 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + PIC32MX460F512L + + + SKDEPIC32PlatformTool + XC32 + 1.21 + 2 + + + + + + + + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/hid_keyboard/MPLAB.X/nbproject/project.xml b/apps/hid_keyboard/MPLAB.X/nbproject/project.xml new file mode 100644 index 0000000..9610953 --- /dev/null +++ b/apps/hid_keyboard/MPLAB.X/nbproject/project.xml @@ -0,0 +1,17 @@ + + + com.microchip.mplab.nbide.embedded.makeproject + + + usb_stack_hid_keyboard_demo + d62999b6-148b-4401-bb2b-d1fe2348676c + 0 + c + + h + ISO-8859-1 + + + + + diff --git a/apps/hid_keyboard/main.c b/apps/hid_keyboard/main.c new file mode 100644 index 0000000..d0efb76 --- /dev/null +++ b/apps/hid_keyboard/main.c @@ -0,0 +1,310 @@ +/* + * USB HID Keyboard + * + * This file may be used by anyone for any purpose and may be used as a + * starting point making your own application using M-Stack. + * + * It is worth noting that M-Stack itself is not under the same license as + * this file. + * + * M-Stack is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. For details, see sections 7, 8, and 9 + * of the Apache License, version 2.0 which apply to this file. If you have + * purchased a commercial license for this software from Signal 11 Software, + * your commerical license superceeds the information in this header. + * + * Alan Ott + * Signal 11 Software + * 2013-08-13 + */ + +#include "usb.h" +#include +#include +#include "usb_config.h" +#include "usb_ch9.h" +#include "usb_hid.h" + +#ifdef __PIC32MX__ + #include +#endif + +#ifdef __PIC24FJ64GB002__ +_CONFIG1(WDTPS_PS16 & FWPSA_PR32 & WINDIS_OFF & FWDTEN_OFF & ICS_PGx1 & GWRP_OFF & GCP_OFF & JTAGEN_OFF) +_CONFIG2(POSCMOD_NONE & I2C1SEL_PRI & IOL1WAY_OFF & OSCIOFNC_OFF & FCKSM_CSDCMD & FNOSC_FRCPLL & PLL96MHZ_ON & PLLDIV_NODIV & IESO_OFF) +_CONFIG3(WPFP_WPFP0 & SOSCSEL_IO & WUTSEL_LEG & WPDIS_WPDIS & WPCFG_WPCFGDIS & WPEND_WPENDMEM) +_CONFIG4(DSWDTPS_DSWDTPS3 & DSWDTOSC_SOSC & RTCOSC_SOSC & DSBOREN_OFF & DSWDTEN_OFF) + +#elif __PIC24FJ256DA206__ +_CONFIG1(WDTPS_PS32768 & FWPSA_PR128 & WINDIS_OFF & FWDTEN_OFF & ICS_PGx2 & GWRP_OFF & GCP_OFF & JTAGEN_OFF) +_CONFIG2(POSCMOD_NONE & IOL1WAY_OFF & OSCIOFNC_ON & FCKSM_CSECMD & FNOSC_FRCPLL & PLL96MHZ_ON & PLLDIV_NODIV & IESO_OFF) +_CONFIG3(WPFP_WPFP255 & SOSCSEL_SOSC & WUTSEL_LEG & ALTPMP_ALPMPDIS & WPDIS_WPDIS & WPCFG_WPCFGDIS & WPEND_WPENDMEM) + +#elif _18F46J50 +#pragma config PLLDIV = 3 /* 3 = Divide by 3. 12MHz crystal => 4MHz */ +#pragma config XINST = OFF +#pragma config WDTEN = OFF +#pragma config CPUDIV = OSC1 +#pragma config IESO = OFF +#pragma config FCMEN = OFF +#pragma config LPT1OSC = OFF +#pragma config T1DIG = ON +#pragma config OSC = ECPLL +#pragma config DSWDTEN = OFF +#pragma config IOL1WAY = OFF +#pragma config WPDIS = OFF /* This pragma seems backwards */ + +#elif _16F1459 +#pragma config FOSC = INTOSC +#pragma config WDTE = OFF +#pragma config PWRTE = OFF +#pragma config MCLRE = ON +#pragma config CP = OFF +#pragma config BOREN = ON +#pragma config CLKOUTEN = OFF +#pragma config IESO = OFF +#pragma config FCMEN = OFF +#pragma config WRT = OFF +#pragma config CPUDIV = NOCLKDIV +#pragma config USBLSCLK = 48MHz +#pragma config PLLMULT = 3x +#pragma config PLLEN = ENABLED +#pragma config STVREN = ON +#pragma config BORV = LO +#pragma config LPBOR = ON +#pragma config LVP = OFF + +#elif __32MX460F512L__ +#pragma config DEBUG = OFF, ICESEL = ICS_PGx2, PWP = OFF, BWP = OFF, CP = OFF +#pragma config FNOSC = PRIPLL, FSOSCEN = OFF, IESO = OFF, POSCMOD = HS, \ + OSCIOFNC = OFF, FPBDIV = DIV_1, FCKSM = CSDCMD, WDTPS = PS1, \ + FWDTEN = OFF +#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_15, UPLLIDIV = DIV_2, \ + UPLLEN = ON, FPLLODIV = DIV_1 + +#else + #error "Config flags for your device not defined" + +#endif + +#ifdef MULTI_CLASS_DEVICE +static uint8_t hid_interfaces[] = { 0 }; +#endif + +int main(void) +{ +#if defined(__PIC24FJ64GB002__) || defined(__PIC24FJ256DA206__) + unsigned int pll_startup_counter = 600; + CLKDIVbits.PLLEN = 1; + while(pll_startup_counter--); +#elif _18F46J50 + unsigned int pll_startup = 600; + OSCTUNEbits.PLLEN = 1; + while (pll_startup--) + ; +#elif _16F1459 + OSCCONbits.IRCF = 0b1111; /* 0b1111 = 16MHz HFINTOSC postscalar */ + + /* Enable Active clock-tuning from the USB */ + ACTCONbits.ACTSRC = 1; /* 1=USB */ + ACTCONbits.ACTEN = 1; +#elif __32MX460F512L__ + SYSTEMConfigPerformance(80000000); +#endif + + +/* Configure interrupts, per architecture */ +#ifdef USB_USE_INTERRUPTS + #if defined (_PIC18) || defined(_PIC14E) + INTCONbits.PEIE = 1; + INTCONbits.GIE = 1; + #elif __PIC32MX__ + INTCONbits.MVEC = 1; /* Multi-vector interrupts */ + IPC11bits.USBIP = 4; /* Interrupt priority, must set to != 0. */ + __asm volatile("ei"); + #endif +#endif + +#ifdef MULTI_CLASS_DEVICE + hid_set_interface_list(hid_interfaces, sizeof(hid_interfaces)); +#endif + usb_init(); + + /* Setup keyboard keypress movement. This implementation sends back data + * for every IN packet. + */ + uint8_t button = 0; + + while (1) { + if (usb_is_configured() && + !usb_in_endpoint_halted(1) && + !usb_in_endpoint_busy(1)) { + + unsigned char *buf = usb_get_in_buffer(1); + + if(PORTAbits.RA3 != button){ + button = PORTAbits.RA3; + buf[0] = (!button?0x03:0x00); // modifier (shift+ctrl) + buf[1] = 0x00; // reserved + buf[2] = (!button?0x1e:0x00); // key (1) + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + buf[7] = 0x00; + } + usb_send_in_buffer(1, 8); + } + + #ifndef USB_USE_INTERRUPTS + usb_service(); + #endif + } + + return 0; +} + +/* Callbacks. These function names are set in usb_config.h. */ +void app_set_configuration_callback(uint8_t configuration) +{ + +} + +uint16_t app_get_device_status_callback() +{ + return 0x0000; +} + +void app_endpoint_halt_callback(uint8_t endpoint, bool halted) +{ + +} + +int8_t app_set_interface_callback(uint8_t interface, uint8_t alt_setting) +{ + return 0; +} + +int8_t app_get_interface_callback(uint8_t interface) +{ + return 0; +} + +void app_out_transaction_callback(uint8_t endpoint) +{ + +} + +void app_in_transaction_complete_callback(uint8_t endpoint) +{ + +} + +int8_t app_unknown_setup_request_callback(const struct setup_packet *setup) +{ + /* To use the HID device class, have a handler for unknown setup + * requests and call process_hid_setup_request() (as shown here), + * which will check if the setup request is HID-related, and will + * call the HID application callbacks defined in usb_hid.h. For + * composite devices containing other device classes, make sure + * MULTI_CLASS_DEVICE is defined in usb_config.h and call all + * appropriate device class setup request functions here. + */ + return process_hid_setup_request(setup); +} + +int16_t app_unknown_get_descriptor_callback(const struct setup_packet *pkt, const void **descriptor) +{ + return -1; +} + +void app_start_of_frame_callback(void) +{ + +} + +void app_usb_reset_callback(void) +{ + +} + +/* HID Callbacks. See usb_hid.h for documentation. */ + +static uint8_t report_buf[3]; + +static void get_report_callback(bool transfer_ok, void *context) +{ + /* Nothing to do here really. It either succeeded or failed. If it + * failed, the host will ask for it again. It's nice to be on the + * device side in USB. */ +} + +int16_t app_get_report_callback(uint8_t interface, uint8_t report_type, + uint8_t report_id, const void **report, + usb_ep0_data_stage_callback *callback, + void **context) +{ + /* This isn't a composite device, so there's no need to check the + * interface here. Also, we know that there's only one report for + * this device, so there's no need to check report_type or report_id. + * + * Set report, callback, and context; and the USB stack will send + * the report, calling our callback (get_report_callback()) when + * it has finished. + */ + *report = report_buf; + *callback = get_report_callback; + *context = NULL; + return sizeof(report_buf); +} + +int8_t app_set_report_callback(uint8_t interface, uint8_t report_type, + uint8_t report_id) +{ + /* To handle Set_Report, call usb_start_receive_ep0_data_stage() + * here. See the documentation for HID_SET_REPORT_CALLBACK() in + * usb_hid.h. For this device though, there are no output or + * feature reports. */ + return -1; +} + +uint8_t app_get_idle_callback(uint8_t interface, uint8_t report_id) +{ + return 0; +} + +int8_t app_set_idle_callback(uint8_t interface, uint8_t report_id, + uint8_t idle_rate) +{ + return -1; +} + +int8_t app_get_protocol_callback(uint8_t interface) +{ + return 1; +} + +int8_t app_set_protocol_callback(uint8_t interface, uint8_t report_id) +{ + return -1; +} + + +#ifdef _PIC14E +void interrupt isr() +{ + usb_service(); +} +#elif _PIC18 + +#ifdef __XC8 +void interrupt high_priority isr() +{ + usb_service(); +} +#elif _PICC18 +#error need to make ISR +#endif + +#endif diff --git a/apps/hid_keyboard/usb_config.h b/apps/hid_keyboard/usb_config.h new file mode 100644 index 0000000..528acb2 --- /dev/null +++ b/apps/hid_keyboard/usb_config.h @@ -0,0 +1,101 @@ +/* + * USB HID Keyboard Configuration + * + * This file may be used by anyone for any purpose and may be used as a + * starting point making your own application using M-Stack. + * + * It is worth noting that M-Stack itself is not under the same license + * as this file. + * + * M-Stack is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. For details, see sections 7, 8, and 9 + * of the Apache License, version 2.0 which apply to this file. If you have + * purchased a commercial license for this software from Signal 11 Software, + * your commerical license superceeds the information in this header. + * + * Alan Ott + * Signal 11 Software + * 2013-04-08 + */ + +#ifndef USB_CONFIG_H__ +#define USB_CONFIG_H__ + +/* Number of endpoint numbers besides endpoint zero. It's worth noting that + and endpoint NUMBER does not completely describe an endpoint, but the + along with the DIRECTION does (eg: EP 1 IN). The #define below turns on + BOTH IN and OUT endpoints for endpoint numbers (besides zero) up to the + value specified. For example, setting NUM_ENDPOINT_NUMBERS to 2 will + activate endpoints EP 1 IN, EP 1 OUT, EP 2 IN, EP 2 OUT. */ +#define NUM_ENDPOINT_NUMBERS 1 + +/* Only 8, 16, 32 and 64 are supported for endpoint zero length. */ +#define EP_0_LEN 8 + +#define EP_1_OUT_LEN 8 +#define EP_1_IN_LEN 8 + +#define NUMBER_OF_CONFIGURATIONS 1 + +/* Ping-pong buffering mode. Valid values are: + PPB_NONE - Do not ping-pong any endpoints + PPB_EPO_OUT_ONLY - Ping-pong only endpoint 0 OUT + PPB_ALL - Ping-pong all endpoints + PPB_EPN_ONLY - Ping-pong all endpoints except 0 +*/ +#ifdef __PIC32MX__ + /* PIC32MX only supports PPB_ALL */ + #define PPB_MODE PPB_ALL +#else + #define PPB_MODE PPB_NONE +#endif + +/* Comment the following line to use polling USB operation. When using polling, + You are responsible for calling usb_service() periodically from your + application. */ +#define USB_USE_INTERRUPTS + +/* Uncomment if you have a composite device which has multiple different types + * of device classes. For example a device which has HID+CDC or + * HID+VendorDefined, but not a device which has multiple of the same class + * (such as HID+HID). Device class implementations have additional requirements + * for multi-class devices. See the documentation for each device class for + * details. */ +//#define MULTI_CLASS_DEVICE + + +/* Objects from usb_descriptors.c */ +#define USB_DEVICE_DESCRIPTOR this_device_descriptor +#define USB_CONFIG_DESCRIPTOR_MAP usb_application_config_descs +#define USB_STRING_DESCRIPTOR_FUNC usb_application_get_string + +/* Optional callbacks from usb.c. Leave them commented if you don't want to + use them. For the prototypes and documentation for each one, see usb.h. */ + +#define SET_CONFIGURATION_CALLBACK app_set_configuration_callback +#define GET_DEVICE_STATUS_CALLBACK app_get_device_status_callback +#define ENDPOINT_HALT_CALLBACK app_endpoint_halt_callback +#define SET_INTERFACE_CALLBACK app_set_interface_callback +#define GET_INTERFACE_CALLBACK app_get_interface_callback +#define OUT_TRANSACTION_CALLBACK app_out_transaction_callback +#define IN_TRANSACTION_COMPLETE_CALLBACK app_in_transaction_complete_callback +#define UNKNOWN_SETUP_REQUEST_CALLBACK app_unknown_setup_request_callback +#define UNKNOWN_GET_DESCRIPTOR_CALLBACK app_unknown_get_descriptor_callback +#define START_OF_FRAME_CALLBACK app_start_of_frame_callback +#define USB_RESET_CALLBACK app_usb_reset_callback + +/* HID Configuration functions. See usb_hid.h for documentation. */ +#define USB_HID_DESCRIPTOR_FUNC usb_application_get_hid_descriptor +#define USB_HID_REPORT_DESCRIPTOR_FUNC usb_application_get_hid_report_descriptor +//#define USB_HID_PHYSICAL_DESCRIPTOR_FUNC usb_application_get_hid_physical_descriptor + +/* HID Callbacks. See usb_hid.h for documentation. */ +#define HID_GET_REPORT_CALLBACK app_get_report_callback +#define HID_SET_REPORT_CALLBACK app_set_report_callback +#define HID_GET_IDLE_CALLBACK app_get_idle_callback +#define HID_SET_IDLE_CALLBACK app_set_idle_callback +#define HID_GET_PROTOCOL_CALLBACK app_get_protocol_callback +#define HID_SET_PROTOCOL_CALLBACK app_set_protocol_callback + +#endif /* USB_CONFIG_H__ */ diff --git a/apps/hid_keyboard/usb_descriptors.c b/apps/hid_keyboard/usb_descriptors.c new file mode 100644 index 0000000..b11ad1a --- /dev/null +++ b/apps/hid_keyboard/usb_descriptors.c @@ -0,0 +1,300 @@ +/* + * USB Descriptors file + * + * This file may be used by anyone for any purpose and may be used as a + * starting point making your own application using M-Stack. + * + * It is worth noting that M-Stack itself is not under the same license as + * this file. + * + * M-Stack is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. For details, see sections 7, 8, and 9 + * of the Apache License, version 2.0 which apply to this file. If you have + * purchased a commercial license for this software from Signal 11 Software, + * your commerical license superceeds the information in this header. + * + * Alan Ott + * Signal 11 Software + */ + +#include "usb_config.h" +#include "usb.h" +#include "usb_ch9.h" +#include "usb_hid.h" + +#ifdef __C18 +#define ROMPTR rom +#else +#define ROMPTR +#endif + +/* Configuration Packet + * + * This packet contains a configuration descriptor, one or more interface + * descriptors, class descriptors(optional), and endpoint descriptors for a + * single configuration of the device. This struct is specific to the + * device, so the application will need to add any interfaces, classes and + * endpoints it intends to use. It is sent to the host in response to a + * GET_DESCRIPTOR[CONFIGURATION] request. + * + * While Most devices will only have one configuration, a device can have as + * many configurations as it needs. To have more than one, simply make as + * many of these structs as are required, one for each configuration. + * + * An instance of each configuration packet must be put in the + * usb_application_config_descs[] array below (which is #defined in + * usb_config.h) so that the USB stack can find it. + * + * See Chapter 9 of the USB specification from usb.org for details. + * + * It's worth noting that adding endpoints here does not automatically + * enable them in the USB stack. To use an endpoint, it must be declared + * here and also in usb_config.h. + * + * The configuration packet below is for the keyboard demo application. + * Yours will of course vary. + */ +struct configuration_1_packet { + struct configuration_descriptor config; + struct interface_descriptor interface; + struct hid_descriptor hid; + struct endpoint_descriptor ep; + struct endpoint_descriptor ep1_out; +}; + + +/* Device Descriptor + * + * Each device has a single device descriptor describing the device. The + * format is described in Chapter 9 of the USB specification from usb.org. + * USB_DEVICE_DESCRIPTOR needs to be defined to the name of this object in + * usb_config.h. For more information, see USB_DEVICE_DESCRIPTOR in usb.h. + */ +const ROMPTR struct device_descriptor this_device_descriptor = +{ + sizeof(struct device_descriptor), // bLength + DESC_DEVICE, // bDescriptorType + 0x0200, // 0x0200 = USB 2.0, 0x0110 = USB 1.1 + 0x00, // Device class + 0x00, // Device Subclass + 0x00, // Protocol. + EP_0_LEN, // bMaxPacketSize0 + 0xA0A0, // Vendor + 0x0003, // Product + 0x0001, // device release (1.0) + 1, // Manufacturer + 2, // Product + 0, // Serial + NUMBER_OF_CONFIGURATIONS // NumConfigurations +}; + +/* HID Report descriptor. See the HID specification for more deatils. This + * is the keyboard example from the "HID Descriptor Tool" which can be downloaded + * from USB.org. */ +static const ROMPTR uint8_t keyboard_report_descriptor[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x05, // USAGE_MAXIMUM (Kana) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x65, // LOGICAL_MAXIMUM (101) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +/* Configuration Packet Instance + * + * This is an instance of the configuration_packet struct containing all the + * data describing a single configuration of this device. It is wise to use + * as much C here as possible, such as sizeof() operators, and #defines from + * usb_config.h. When stuff is wrong here, it can be difficult to track + * down exactly why, so it's good to get the compiler to do as much of it + * for you as it can. + */ +static const ROMPTR struct configuration_1_packet configuration_1 = +{ + { + // Members from struct configuration_descriptor + sizeof(struct configuration_descriptor), + DESC_CONFIGURATION, + sizeof(configuration_1), // wTotalLength (length of the whole packet) + 1, // bNumInterfaces + 1, // bConfigurationValue + 2, // iConfiguration (index of string descriptor) + 0b10000000, + 100/2, // 100/2 indicates 100mA + }, + + { + // Members from struct interface_descriptor + sizeof(struct interface_descriptor), // bLength; + DESC_INTERFACE, + 0x0, // InterfaceNumber + 0x0, // AlternateSetting + 0x2, // bNumEndpoints (num besides endpoint 0) + HID_INTERFACE_CLASS, // bInterfaceClass 3=HID, 0xFF=VendorDefined + 0x00, // bInterfaceSubclass (0=NoBootInterface for HID) + 0x00, // bInterfaceProtocol + 0x02, // iInterface (index of string describing interface) + }, + + { + // Members from struct hid_descriptor + sizeof(struct hid_descriptor), + DESC_HID, + 0x0101, // bcdHID + 0x0, // bCountryCode + 1, // bNumDescriptors + DESC_REPORT, // bDescriptorType2 + sizeof(keyboard_report_descriptor), // wDescriptorLength + }, + + { + // Members of the Endpoint Descriptor (EP1 IN) + sizeof(struct endpoint_descriptor), + DESC_ENDPOINT, + 0x01 | 0x80, // endpoint #1 0x80=IN + EP_INTERRUPT, // bmAttributes + EP_1_IN_LEN, // wMaxPacketSize + 1, // bInterval in ms. + }, + + { + // Members of the Endpoint Descriptor (EP1 OUT) + sizeof(struct endpoint_descriptor), + DESC_ENDPOINT, + 0x01 /*| 0x00*/, // endpoint #1 0x00=OUT + EP_INTERRUPT, // bmAttributes + EP_1_OUT_LEN, // wMaxPacketSize + 1, // bInterval in ms. + }, +}; + +/* String Descriptors + * + * String descriptors are optional. If strings are used, string #0 is + * required, and must contain the language ID of the other strings. See + * Chapter 9 of the USB specification from usb.org for more info. + * + * Strings are UTF-16 Unicode, and are not NULL-terminated, hence the + * unusual syntax. + */ + +/* String index 0, only has one character in it, which is to be set to the + language ID of the language which the other strings are in. */ +static const ROMPTR struct {uint8_t bLength;uint8_t bDescriptorType; uint16_t lang; } str00 = { + sizeof(str00), + DESC_STRING, + 0x0409 // US English +}; + +static const ROMPTR struct {uint8_t bLength;uint8_t bDescriptorType; uint16_t chars[23]; } vendor_string = { + sizeof(vendor_string), + DESC_STRING, + {'S','i','g','n','a','l',' ','1','1',' ','S','o','f','t','w','a','r','e',' ','L','L','C','.'} +}; + +static const ROMPTR struct {uint8_t bLength;uint8_t bDescriptorType; uint16_t chars[17]; } product_string = { + sizeof(product_string), + DESC_STRING, + {'U','S','B',' ','K','e','y','b','o','a','r','d',' ','D','e','m','o'} +}; + +static const ROMPTR struct {uint8_t bLength;uint8_t bDescriptorType; uint16_t chars[11]; } interface_string = { + sizeof(interface_string), + DESC_STRING, + {'I','n','t','e','r','f','a','c','e',' ','1'} +}; + +/* Get String function + * + * This function is called by the USB stack to get a pointer to a string + * descriptor. If using strings, USB_STRING_DESCRIPTOR_FUNC must be defined + * to the name of this function in usb_config.h. See + * USB_STRING_DESCRIPTOR_FUNC in usb.h for information about this function. + * This is a function, and not simply a list or map, because it is useful, + * and advisable, to have a serial number string which may be read from + * EEPROM or somewhere that's not part of static program memory. + */ +int16_t usb_application_get_string(uint8_t string_number, const void **ptr) +{ + if (string_number == 0) { + *ptr = &str00; + return sizeof(str00); + } + else if (string_number == 1) { + *ptr = &vendor_string; + return sizeof(vendor_string); + } + else if (string_number == 2) { + *ptr = &product_string; + return sizeof(product_string); + } + else if (string_number == 3) { + /* This is where you might have code to do something like read + a serial number out of EEPROM and return it. */ + return -1; + } + + return -1; +} + +/* Configuration Descriptor List + * + * This is the list of pointters to the device's configuration descriptors. + * The USB stack will read this array looking for descriptors which are + * requsted from the host. USB_CONFIG_DESCRIPTOR_MAP must be defined to the + * name of this array in usb_config.h. See USB_CONFIG_DESCRIPTOR_MAP in + * usb.h for information about this array. The order of the descriptors is + * not important, as the USB stack reads bConfigurationValue for each + * descriptor to know its index. Make sure NUMBER_OF_CONFIGURATIONS in + * usb_config.h matches the number of descriptors in this array. + */ +const struct configuration_descriptor *usb_application_config_descs[] = +{ + (struct configuration_descriptor*) &configuration_1, +}; +STATIC_SIZE_CHECK_EQUAL(USB_ARRAYLEN(USB_CONFIG_DESCRIPTOR_MAP), NUMBER_OF_CONFIGURATIONS); +STATIC_SIZE_CHECK_EQUAL(sizeof(USB_DEVICE_DESCRIPTOR), 18); + +/* HID Descriptor Function */ +int16_t usb_application_get_hid_descriptor(uint8_t interface, const void **ptr) +{ + /* Only one interface in this demo. The two-step assignment avoids an + * incorrect error in XC8 on PIC16. */ + const void *p = &configuration_1.hid; + *ptr = p; + return sizeof(configuration_1.hid); +} + +/** HID Report Descriptor Function */ +int16_t usb_application_get_hid_report_descriptor(uint8_t interface, const void **ptr) +{ + *ptr = keyboard_report_descriptor; + return sizeof(keyboard_report_descriptor); +}