From c59242383f9494ffb5859315286d3eb7e6997cd2 Mon Sep 17 00:00:00 2001 From: Raphael Assenat Date: Sat, 13 Jun 2015 23:44:21 -0400 Subject: [PATCH] WIP --- .gitignore | 5 + Makefile | 44 +++ gamepad.h | 25 ++ gcn64_protocol.c | 492 ++++++++++++++++++++++++++++++ gcn64_protocol.h | 168 +++++++++++ main.c | 436 +++++++++++++++++++++++++++ n64.c | 332 +++++++++++++++++++++ n64.h | 4 + reportdesc.c | 652 ++++++++++++++++++++++++++++++++++++++++ usart1.c | 39 +++ usart1.h | 7 + usb.c | 725 +++++++++++++++++++++++++++++++++++++++++++++ usb.h | 200 +++++++++++++ wait_then_flash.sh | 27 ++ 14 files changed, 3156 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 gamepad.h create mode 100644 gcn64_protocol.c create mode 100644 gcn64_protocol.h create mode 100644 main.c create mode 100644 n64.c create mode 100644 n64.h create mode 100644 reportdesc.c create mode 100644 usart1.c create mode 100644 usart1.h create mode 100644 usb.c create mode 100644 usb.h create mode 100755 wait_then_flash.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb36de1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.elf +*.hex +*.map +*.swp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fed3cae --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +CC=avr-gcc +AS=$(CC) +LD=$(CC) + +PROGNAME=n64 +OBJDIR=objs-$(PROGNAME) +CPU=atmega32u2 +CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT +LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map +HEXFILE=$(PROGNAME).hex + +OBJS=main.o n64.o gcn64_protocol.o usart1.o usb.o + +all: $(HEXFILE) + +clean: + rm -f $(PROGNAME).elf $(PROGNAME).hex $(PROGNAME).map $(OBJS) + +%.o: %.S + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.c %.h + $(CC) $(CFLAGS) -c $< -o $@ + +$(PROGNAME).elf: $(OBJS) + $(LD) $(OBJS) $(LDFLAGS) -o $(PROGNAME).elf + +$(PROGNAME).hex: $(PROGNAME).elf + avr-objcopy -j .data -j .text -O ihex $(PROGNAME).elf $(PROGNAME).hex + avr-size $(PROGNAME).elf + +fuse: + +flash: $(HEXFILE) + ./wait_then_flash.sh $(CPU) $(HEXFILE) + +chip_erase: + dfu-programmer atmega32u2 erase + +reset: + dfu-programmer atmega32u2 reset diff --git a/gamepad.h b/gamepad.h new file mode 100644 index 0000000..40242ec --- /dev/null +++ b/gamepad.h @@ -0,0 +1,25 @@ +#ifndef _gamepad_h__ +#define _gamepad_h__ + +typedef struct { + int num_reports; + + int reportDescriptorSize; + void *reportDescriptor; // must be in flash + + int deviceDescriptorSize; // if 0, use default + void *deviceDescriptor; // must be in flash + + void (*init)(void); + char (*update)(void); + char (*changed)(int id); + int (*buildReport)(unsigned char *buf, int id); + void (*setVibration)(int value); + + /* Check for the controller */ + char (*probe)(void); /* return true if found */ +} Gamepad; + +#endif // _gamepad_h__ + + diff --git a/gcn64_protocol.c b/gcn64_protocol.c new file mode 100644 index 0000000..b479c01 --- /dev/null +++ b/gcn64_protocol.c @@ -0,0 +1,492 @@ +/* gc_n64_usb : Gamecube or N64 controller to USB firmware + Copyright (C) 2007-2015 Raphael Assenat + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include + +#include "gcn64_protocol.h" + +#undef FORCE_KEYBOARD + +#define GCN64_BUF_SIZE 300 +static volatile unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; + +/******** IO port definitions and options **************/ +#define GCN64_DATA_PORT PORTD +#define GCN64_DATA_DDR DDRD +#define GCN64_DATA_PIN PIND +#define GCN64_DATA_BIT (1<<0) +#define GCN64_BIT_NUM_S "0" // for asm +#define FREQ_IS_16MHZ +#define DISABLE_INTS_DURING_COMM + +/* + * \brief Explode bytes to bits + * \param bytes The input byte array + * \param num_bytes The number of input bytes + * \param workbuf_bit_offset The offset to start writing at + * \return number of bits (i.e. written output bytes) + * + * 1 input byte = 8 output bytes, where each output byte is zero or non-zero depending + * on the input byte bits, starting with the most significant one. + */ +static int bitsToWorkbufBytes(unsigned char *bytes, int num_bytes, int workbuf_bit_offset) +{ + int i, bit; + unsigned char p; + + if (num_bytes * 8 > GCN64_BUF_SIZE) + return 0; + + for (i=0,bit=0; i>=1) { + gcn64_workbuf[bit+workbuf_bit_offset] = bytes[i] & p; + bit++; + } + } + + return bit; +} + +/* Read a byte from the buffer (where 1 byte is 1 bit). + * MSb first. + */ +unsigned char gcn64_protocol_getByte(int offset) +{ + unsigned char val, b; + unsigned char volatile *addr = gcn64_workbuf + offset; + + for (b=0x80, val=0; b; b>>=1) + { + if (*addr) + val |= b; + addr++; + } + return val; +} + +void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf) +{ + int i; + + for (i=0; i 127 (approx 50uS timeout) + " sbic %2, "GCN64_BIT_NUM_S" \n" + " rjmp waitlow_lp \n" + + " inc %0 \n" // count this timed low level + " breq overflow \n" // > 255 + " st z+,r16 \n" + +"waithigh:\n" + " ldi r16, %4 \n" +"waithigh_lp:\n" + " inc r16 \n" + " brmi timeout \n" // > 127 + " sbis %2, "GCN64_BIT_NUM_S" \n" + " rjmp waithigh_lp \n" + + " inc %0 \n" // count this timed high level + " breq overflow \n" // > 255 + " st z+,r16 \n" + + " rjmp waitlow \n" + +"overflow: \n" +"timeout: \n" +" pop r31 \n" // restore z +" pop r30 \n" // restore z + + : "=&r" (count) // %0 + : "z" ((unsigned char volatile *)gcn64_workbuf), // %1 + "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)), // %2 + "I" (_SFR_IO_ADDR(PORTB)), // %3 + "M" (TIMING_OFFSET) // %4 + : "r16" + ); + + return count; +} + +static void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes) +{ + unsigned int bits; + + if (n_bytes == 0) + return; + + // Explode the data to one byte per bit for very easy transmission in assembly. + // This trades memory for ease of implementation. + bits = bitsToWorkbufBytes(data, n_bytes, 0); + + // the value of the gpio is pre-configured to low. We simulate + // an open drain output by toggling the direction. +#define PULL_DATA " sbi %0, "GCN64_BIT_NUM_S"\n" +#define RELEASE_DATA " cbi %0, "GCN64_BIT_NUM_S"\n" + +#ifdef FREQ_IS_16MHZ + // busy looping delays based on busy loop and nop tuning. + // valid for 16Mhz clock. (Tuned to 1us/3us using a scope) +#define DLY_SHORT_1ST "ldi r17, 2\n nop\nrcall sb_dly%=\n " +#define DLY_LARGE_1ST "ldi r17, 13\n rcall sb_dly%=\n" +#define DLY_SHORT_2ND "nop\nnop\nnop\nnop\n" +#define DLY_LARGE_2ND "ldi r17, 9\n rcall sb_dly%=\nnop\nnop\n" + +#else + // busy looping delays based on busy loop and nop tuning. + // valid for 12Mhz clock. +#define DLY_SHORT_1ST "ldi r17, 1\n rcall sb_dly%=\n " +#define DLY_LARGE_1ST "ldi r17, 9\n rcall sb_dly%=\n" +#define DLY_SHORT_2ND "\n" +#define DLY_LARGE_2ND "ldi r17, 5\n rcall sb_dly%=\n nop\nnop\n" +#endif + + asm volatile( + // Save the modified input operands + " push r28 \n" // y + " push r29 \n" + " push r30 \n" // z + " push r31 \n" + + "sb_loop%=: \n" + " ld r16, z+ \n" + " tst r16 \n" + " breq sb_send0%= \n" + " brne sb_send1%= \n" + + " rjmp sb_end%= \n" // not reached + + + "sb_send0%=: \n" + " nop \n" + PULL_DATA + DLY_LARGE_1ST + RELEASE_DATA + DLY_SHORT_2ND + " sbiw %1, 1 \n" + " brne sb_loop%= \n" + " rjmp sb_end%= \n" + + "sb_send1%=: \n" + PULL_DATA + DLY_SHORT_1ST + RELEASE_DATA + DLY_LARGE_2ND + " sbiw %1, 1 \n" + " brne sb_loop%= \n" + " rjmp sb_end%= \n" + +// delay sub (arg r17) + "sb_dly%=: \n" + " dec r17 \n" + " brne sb_dly%= \n" + " ret \n" + + + "sb_end%=:\n" + // going here is fast so we need to extend the last + // delay by 500nS + " nop\n " +#ifdef FREQ_IS_16MHZ + " nop\n" +#endif + " pop r31 \n" + " pop r30 \n" + PULL_DATA + " pop r29 \n" + " pop r28 \n" + //DLY_SHORT_1ST + "nop\nnop\nnop\nnop\n" + RELEASE_DATA + + // Now, we need to loop until the wire is high to + // prevent the reception code from thinking this is + // the beginning of the first reply bit. + + " ldi r16, 0xff \n" // setup a timeout + "sb_waitHigh%=: \n" + " dec r16 \n" // decrement timeout + " breq sb_wait_high_done%= \n" // handle timeout condition + " sbis %3, "GCN64_BIT_NUM_S" \n" // Read the port + " rjmp sb_waitHigh%= \n" +"sb_wait_high_done%=:\n" + : + : "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0 + "w" (bits), // %1 + "z" ((unsigned char volatile *)gcn64_workbuf), // %2 + "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)) // %3 + : "r16", "r17"); +} + + +/* \brief Decode the received length of low/high states to byte-per-bit format + * + * The result is in workbuf. + * + **/ +static void gcn64_decodeWorkbuf(unsigned char count) +{ + unsigned char i; + volatile unsigned char *output = gcn64_workbuf; + volatile unsigned char *input = gcn64_workbuf; + unsigned char t; + + // + // ________ + // ________/ + // + // [i*2] [i*2+1] + // + // ________________ + // 0 : ____/ + // ____ + // 1 : ________________/ + // + // The timings on a real N64 are + // + // 0 : 1 us low, 3 us high + // 1 : 3 us low, 1 us high + // + // However, HORI pads use something similar to + // + // 0 : 1.5 us low, 4.5 us high + // 1 : 4.5 us low, 1.5 us high + // + // + // No64 us = microseconds + + // This operation takes approximately 100uS on 64bit gamecube messages + for (i=0; i> 8) { + case 0x05: + return CONTROLLER_IS_N64; + + case 0x09: // normal controllers + case 0x0b: // Never saw this one, but it is mentionned above. + return CONTROLLER_IS_GC; + + case 0x08: + if (id == 0x0820) { + // Ascii keyboard + return CONTROLLER_IS_GC_KEYBOARD; + } + // wavebird, controller off. + return CONTROLLER_IS_GC; + + default: + return CONTROLLER_IS_UNKNOWN; + } + + return 0; +} + + diff --git a/gcn64_protocol.h b/gcn64_protocol.h new file mode 100644 index 0000000..06be7a2 --- /dev/null +++ b/gcn64_protocol.h @@ -0,0 +1,168 @@ +#ifndef _gcn64_protocol_h__ +#define _gcn64_protocol_h__ + +#define CONTROLLER_IS_ABSENT 0 +#define CONTROLLER_IS_N64 1 +#define CONTROLLER_IS_GC 2 +#define CONTROLLER_IS_GC_KEYBOARD 3 +#define CONTROLLER_IS_UNKNOWN 4 + + +/* Return many unknown bits, but two are about the expansion port. */ +#define N64_GET_CAPABILITIES 0x00 +#define N64_CAPS_REPLY_LENGTH 24 + +#define OFFSET_EXT_REMOVED 22 +#define OFFSET_EXT_PRESENT 23 + +/* Returns button states and axis values */ +#define N64_GET_STATUS 0x01 +#define N64_GET_STATUS_REPLY_LENGTH 32 + +/* Read from the expansion bus. */ +#define N64_EXPANSION_READ 0x02 + +/* Write to the expansion bus. */ +#define N64_EXPANSION_WRITE 0x03 + +/* Return information about controller. */ +#define GC_GETID 0x00 +#define GC_GETID_REPLY_LENGTH 24 + +/* 3-byte get status command. Returns axis and buttons. Also + * controls motor. */ +#define GC_GETSTATUS1 0x40 +#define GC_GETSTATUS2 0x03 +#define GC_GETSTATUS3(rumbling) ((rumbling) ? 0x01 : 0x00) +#define GC_GETSTATUS_REPLY_LENGTH 64 + +/* 3-byte poll keyboard command. + * Source: http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.3 + * */ +#define GC_POLL_KB1 0x54 +#define GC_POLL_KB2 0x00 +#define GC_POLL_KB3 0x00 + +/* Gamecube keycodes are from table 9.3.2: + * http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.2 + * + * But the table appears to have been built using a PC keyboard + * converted (Tototek). + * + * I was working with an ASCII keyboard so I made a few adjustments + * to reflect the /real/ key functions. For instance: + * + * LWIN, RWIN and MENU are in fact the Henkan, muhenkan and katakana/hiragana keys. + * They are equivalemnt to the HID keys International 4, International 5 and International 2. + * The - key (code 0x3F) is also HID international 1. + * The PrntScrn key (code 0x36) is in fact the Yen key (International 3). + * The bracket/brace [{ and }] keys are at different places on this Japanese keyboard, + * but following standard PC practice, some keycodes are named after the US usage. Hence, + * the aforementioned keys are the 2 keys right of P, even if they are not labeled. + */ +#define GC_KEY_RESERVED 0x00 // No key + +#define GC_KEY_HOME 0x06 +#define GC_KEY_END 0x07 +#define GC_KEY_PGUP 0x08 +#define GC_KEY_PGDN 0x09 +#define GC_KEY_SCROLL_LOCK 0x0A + +#define GC_KEY_A 0x10 +#define GC_KEY_B 0x11 +#define GC_KEY_C 0x12 +#define GC_KEY_D 0x13 +#define GC_KEY_E 0x14 +#define GC_KEY_F 0x15 +#define GC_KEY_G 0x16 +#define GC_KEY_H 0x17 +#define GC_KEY_I 0x18 +#define GC_KEY_J 0x19 +#define GC_KEY_K 0x1A +#define GC_KEY_L 0x1B +#define GC_KEY_M 0x1C +#define GC_KEY_N 0x1D +#define GC_KEY_O 0x1E +#define GC_KEY_P 0x1F +#define GC_KEY_Q 0x20 +#define GC_KEY_R 0x21 +#define GC_KEY_S 0x22 +#define GC_KEY_T 0x23 +#define GC_KEY_U 0x24 +#define GC_KEY_V 0x25 +#define GC_KEY_W 0x26 +#define GC_KEY_X 0x27 +#define GC_KEY_Y 0x28 +#define GC_KEY_Z 0x29 +#define GC_KEY_1 0x2A +#define GC_KEY_2 0x2B +#define GC_KEY_3 0x2C +#define GC_KEY_4 0x2D +#define GC_KEY_5 0x2E +#define GC_KEY_6 0x2F +#define GC_KEY_7 0x30 +#define GC_KEY_8 0x31 +#define GC_KEY_9 0x32 +#define GC_KEY_0 0x33 +#define GC_KEY_DASH_UNDERSCORE 0x34 // Key next to 0 +#define GC_KEY_PLUS_EQUAL 0x35 +#define GC_KEY_YEN 0x36 // Yen on ascii keyboard. HID International 3. +#define GC_KEY_OPEN_BRKT_BRACE 0x37 // 1st key right of P +#define GC_KEY_CLOSE_BRKT_BRACE 0x38 // 2nd key right of P +#define GC_KEY_SEMI_COLON_COLON 0x39 // ;: +#define GC_KEY_QUOTES 0x3A // '" + +// The 3rd key after 'L'. HID Keyboard non-us # and ~ +#define GC_KEY_BRACKET_MU 0x3B +#define GC_KEY_COMMA_ST 0x3C // ,< +#define GC_KEY_PERIOD_GT 0x3D // .> +#define GC_KEY_SLASH_QUESTION 0x3E // /? + +// (The extra key before right-shift on japanese keyboards. +// HID code International 1 [HID usage tables Footnote 15-20]). +#define GC_KEY_INTERNATIONAL1 0x3F +#define GC_KEY_F1 0x40 +#define GC_KEY_F2 0x41 +#define GC_KEY_F3 0x42 +#define GC_KEY_F4 0x43 +#define GC_KEY_F5 0x44 +#define GC_KEY_F6 0x45 +#define GC_KEY_F7 0x46 +#define GC_KEY_F8 0x47 +#define GC_KEY_F9 0x48 +#define GC_KEY_F10 0x49 +#define GC_KEY_F11 0x4A +#define GC_KEY_F12 0x4B +#define GC_KEY_ESC 0x4C +#define GC_KEY_INSERT 0x4D +#define GC_KEY_DELETE 0x4E + +// (Hankaku/zenkaku/kanji button). Also known as `~ +#define GC_KEY_HANKAKU 0x4F +#define GC_KEY_BACKSPACE 0x50 +#define GC_KEY_TAB 0x51 + +#define GC_KEY_CAPS_LOCK 0x53 +#define GC_KEY_LEFT_SHIFT 0x54 +#define GC_KEY_RIGHT_SHIFT 0x55 +#define GC_KEY_LEFT_CTRL 0x56 +#define GC_KEY_LEFT_ALT 0x57 +#define GC_KEY_MUHENKAN 0x58 // HID international 5 +#define GC_KEY_SPACE 0x59 +#define GC_KEY_HENKAN 0x5A // HID international 4 +#define GC_KEY_KANA 0x5B // HID international 2 +#define GC_KEY_LEFT 0x5C +#define GC_KEY_DOWN 0x5D +#define GC_KEY_UP 0x5E +#define GC_KEY_RIGHT 0x5F + +#define GC_KEY_ENTER 0x61 + +void gcn64protocol_hwinit(void); +int gcn64_detectController(void); +int gcn64_transaction(unsigned char *data_out, int data_out_len); + +unsigned char gcn64_protocol_getByte(int offset); +void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf); + +#endif // _gcn64_protocol_h__ diff --git a/main.c b/main.c new file mode 100644 index 0000000..0a0e2cc --- /dev/null +++ b/main.c @@ -0,0 +1,436 @@ +#include +#include +#include + +#include +#include + +#include + +#include "usart1.h" +#include "usb.h" +#include "gamepad.h" +#include "gcn64_protocol.h" +#include "n64.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +#ifndef LPSTR +#define LPSTR(s) ((const PROGMEM wchar_t*)(s)) +#endif + +uint16_t hid_get_report(struct usb_request *rq, const uint8_t **dat); +uint8_t hid_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len); + +#include "reportdesc.c" + +const wchar_t *const g_usb_strings[] = { + [0] = L"raphnet technologies", // 1 : Vendor + [1] = L"GC/N64 to USB v3.0", // 2: Product + [2] = L"123456", // 3 : Serial +}; + +struct cfg0 { + struct usb_configuration_descriptor configdesc; + struct usb_interface_descriptor interface; + struct usb_hid_descriptor hid; + struct usb_endpoint_descriptor ep1_in; +}; + +static const struct cfg0 cfg0 PROGMEM = { + .configdesc = { + .bLength = sizeof(struct usb_configuration_descriptor), + .bDescriptorType = CONFIGURATION_DESCRIPTOR, + .wTotalLength = sizeof(cfg0), // includes all descriptors returned together + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed. + .bMaxPower = 25, // for 50mA + }, + .interface = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = INTERFACE_DESCRIPTOR, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_DEVICE_CLASS_HID, + .bInterfaceSubClass = HID_SUBCLASS_NONE, + .bInterfaceProtocol = HID_PROTOCOL_NONE, + }, + .hid = { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = HID_DESCRIPTOR, + .bcdHid = 0x0101, + .bCountryCode = HID_COUNTRY_NOT_SUPPORTED, + .bNumDescriptors = 1, // Only a report descriptor + .bClassDescriptorType = REPORT_DESCRIPTOR, + .wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), + }, + .ep1_in = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = ENDPOINT_DESCRIPTOR, + .bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81 + .bmAttributes = TRANSFER_TYPE_INT, + .wMaxPacketsize = 64, + .bInterval = LS_FS_INTERVAL_MS(1), + }, +}; + +const struct usb_device_descriptor device_descriptor PROGMEM = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = DEVICE_DESCRIPTOR, + .bcdUSB = 0x0101, + .bDeviceClass = 0, // set at interface + .bDeviceSubClass = 0, // set at interface + .bDeviceProtocol = 0, + .bMaxPacketSize = 64, + .idVendor = 0x289B, + .idProduct = 0x0017, + .bcdDevice = 0x0300, // 1.0.0 + .bNumConfigurations = 1, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, +}; + +static struct usb_parameters usb_params = { + .flags = USB_PARAM_FLAG_CONFDESC_PROGMEM | + USB_PARAM_FLAG_DEVDESC_PROGMEM | + USB_PARAM_FLAG_REPORTDESC_PROGMEM, + .devdesc = (PGM_VOID_P)&device_descriptor, + .configdesc = (PGM_VOID_P)&cfg0, + .configdesc_ttllen = sizeof(cfg0), + .num_strings = ARRAY_SIZE(g_usb_strings), + .strings = g_usb_strings, + .reportdesc = gcn64_usbHidReportDescriptor, + .reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), + .getReport = hid_get_report, + .setReport = hid_set_report, +}; + +void hwinit(void) +{ + /* PORTB + * + * 7: NC Output low + * 6: NC Output low + * 5: NC Output low + * 4: NC Output low + * 3: MISO Output low + * 2: MOSI Output low + * 1: SCK Output low + * 0: NC Output low + */ + PORTB = 0x00; + DDRB = 0xFF; + + /* PORTC + * + * 7: NC Output low + * 6: NC Output low + * 5: NC Output low + * 4: NC Output low + * 3: (no such pin) + * 2: NC Output low + * 1: RESET (N/A: Reset input per fuses) + * 0: XTAL2 (N/A: Crystal oscillator) + */ + PORTB = 0x00; + DDRB = 0xff; + + /* PORTD + * + * 7: HWB Input (external pull-up) + * 6: NC Output low + * 5: NC Output low + * 4: NC Output low + * 3: IO3_MCU Input + * 2: IO2_MCU Input + * 1: IO1_MCU Input + * 0: IO0_MCU Input + */ + PORTD = 0x00; + DDRD = 0x70; + + // System clock. External crystal is 16 Mhz and we want + // to run at max. speed. + CLKPR = 0x80; + CLKPR = 0x0; // Division factor of 1 + PRR0 = 0; + PRR1 = 0; +} + +static unsigned char hid_report_data[32]; +static unsigned char gamepad_report0[32]; + +// Output Report IDs for various functions +#define REPORT_SET_EFFECT 0x01 +#define REPORT_SET_PERIODIC 0x04 +#define REPORT_SET_CONSTANT_FORCE 0x05 +#define REPORT_EFFECT_OPERATION 0x0A +#define REPORT_DISABLE_ACTUATORS 0x0C +#define REPORT_PID_POOL 0x0D + +// Feature reports +#define REPORT_CREATE_EFFECT 0x09 + +// For the 'Usage Effect Operation' report +#define EFFECT_OP_START 1 +#define EFFECT_OP_START_SOLO 2 +#define EFFECT_OP_STOP 3 + +// Feature report +#define PID_SIMULTANEOUS_MAX 3 +#define PID_BLOCK_LOAD_REPORT 2 + +static volatile unsigned char gamepad_vibrate = 0; // output + +static unsigned char vibration_on = 0; +static unsigned char constant_force = 0; +static unsigned char magnitude = 0; + +static unsigned char _FFB_effect_index; +#define LOOP_MAX 0xFFFF +static unsigned int _loop_count; + +static void effect_loop() +{ + if (_loop_count) { + if (_loop_count != LOOP_MAX) { + _loop_count--; + } + } +} + +static void decideVibration(void) +{ + if (!_loop_count) + vibration_on = 0; + + if (!vibration_on) { + gamepad_vibrate = 0; + } else { + if (constant_force > 0x7f) { + gamepad_vibrate = 1; + } + if (magnitude > 0x7f) { + gamepad_vibrate = 1; + } + } +} + +uint16_t hid_get_report(struct usb_request *rq, const uint8_t **dat) +{ + uint8_t report_id = (rq->wValue & 0xff); + + // USB HID 1.11 section 7.2.1 Get_Report + // wValue high byte : report type + // wValue low byte : report id + switch (rq->wValue >> 8) + { + case HID_REPORT_TYPE_INPUT: + { + if (report_id == 1) { // Joystick + // report_id = rq->wValue & 0xff + // interface = rq->wIndex + *dat = gamepad_report0; + printf("Get joy report\r\n"); + return 9; + } else if (report_id == 2) { // 2 : ES playing + hid_report_data[0] = report_id; + hid_report_data[1] = 0; + hid_report_data[2] = _FFB_effect_index; + printf("ES playing\r\n"); + *dat = hid_report_data; + return 3; + } else { + printf("Get input report %d ??\r\n", rq->wValue & 0xff); + } + } + break; + + case HID_REPORT_TYPE_FEATURE: + if (report_id == PID_BLOCK_LOAD_REPORT) { + hid_report_data[0] = report_id; + hid_report_data[1] = 0x1; + hid_report_data[2] = 0x1; + hid_report_data[3] = 10; + hid_report_data[4] = 10; + printf("block load\r\n"); + *dat = hid_report_data; + return 5; + } + else if (report_id == PID_SIMULTANEOUS_MAX) { + hid_report_data[0] = report_id; + // ROM Effect Block count + hid_report_data[1] = 0x1; + hid_report_data[2] = 0x1; + // PID pool move report? + hid_report_data[3] = 0xff; + hid_report_data[4] = 1; + printf("simultaneous max\r\n"); + *dat = hid_report_data; + return 5; + } + else if (report_id == REPORT_CREATE_EFFECT) { + hid_report_data[0] = report_id; + hid_report_data[1] = 1; + printf("create effect\r\n"); + *dat = hid_report_data; + return 2; + } else { + printf("Unknown feature %d\r\n", rq->wValue & 0xff); + } + break; + } + + printf("Unhandled hid get report type=0x%02x, rq=0x%02x, wVal=0x%04x, wLen=0x%04x\r\n", rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength); + return 0; +} + +uint8_t hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len) +{ + if (len < 1) { + printf("shrt\n"); + return -1; + } + + if ((rq->wValue >> 8) == HID_REPORT_TYPE_OUTPUT) { + + switch(data[0]) + { + case REPORT_DISABLE_ACTUATORS: + printf("disable actuators\r\n"); + break; + case REPORT_PID_POOL: + printf("pid pool\r\n"); + break; + case REPORT_SET_EFFECT: + _FFB_effect_index = data[1]; + printf("set effect %d\n", data[1]); + break; + case REPORT_SET_PERIODIC: + magnitude = data[2]; + decideVibration(); + printf("periodic mag: %d", data[2]); + break; + case REPORT_SET_CONSTANT_FORCE: + if (data[1] == 1) { + constant_force = data[2]; + decideVibration(); + printf("Constant force"); + } + break; + case REPORT_EFFECT_OPERATION: + if (len != 4) + return -1; + printf("EFFECT OP\n"); + /* Byte 0 : report ID + * Byte 1 : bit 7=rom flag, bits 6-0=effect block index + * Byte 2 : Effect operation + * Byte 3 : Loop count */ + _loop_count = data[3]<<3; + + switch(data[1] & 0x7F) // Effect block index + { + case 1: // constant force + case 3: // square + case 4: // sine + switch (data[2]) // effect operation + { + case EFFECT_OP_START: + vibration_on = 1; + decideVibration(); + break; + + case EFFECT_OP_START_SOLO: + vibration_on = 1; + decideVibration(); + break; + + case EFFECT_OP_STOP: + vibration_on = 0; + decideVibration(); + break; + } + break; + + // TODO : should probably drop these from the descriptor since they are + + case 2: // ramp + case 5: // triangle + case 6: // sawtooth up + case 7: // sawtooth down + case 8: // spring + case 9: // damper + case 10: // inertia + case 11: // friction + case 12: // custom force data + printf("Ununsed effect %d\n", data[1] & 0x7F); + break; + } + break; + default: + printf("Set output report 0x%02x\r\n", data[0]); + } + } + else if ((rq->wValue >> 8) == HID_REPORT_TYPE_FEATURE) { + switch(data[0]) + { + case REPORT_CREATE_EFFECT: + _FFB_effect_index = data[1]; + printf("create effect %d\n", data[1]); + break; + + default: + printf("What?\n"); + } + } + else { + printf("impossible\n"); + } + return 0; +} + +int main(void) +{ + Gamepad *pad = NULL; + + hwinit(); + usart1_init(); + gcn64protocol_hwinit(); + + pad = n64GetGamepad(); + + sei(); + usb_init(&usb_params); + + while (1) + { + static char last_v = 0; + + usb_doTasks(); + _delay_ms(5); + effect_loop(); + decideVibration(); + + if (last_v != gamepad_vibrate) { + if (pad->setVibration) { + pad->setVibration(gamepad_vibrate); + } + last_v = gamepad_vibrate; + } + + pad->update(); + if (pad->changed(1)) { + int report_size; + + report_size = pad->buildReport(gamepad_report0, 1); + usb_interruptSend(gamepad_report0, report_size); + } + } + + return 0; +} diff --git a/n64.c b/n64.c new file mode 100644 index 0000000..cedc1f4 --- /dev/null +++ b/n64.c @@ -0,0 +1,332 @@ +/* gc_n64_usb : Gamecube or N64 controller to USB firmware + Copyright (C) 2007-2014 Raphael Assenat + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "gamepad.h" +#include "n64.h" +#include "gcn64_protocol.h" + +#define GCN64_REPORT_SIZE 9 + +#undef BUTTON_A_RUMBLE_TEST + +/*********** prototypes *************/ +static void n64Init(void); +static char n64Update(void); +static char n64Changed(int id); +static int n64BuildReport(unsigned char *reportBuffer, int id); +static void n64SetVibration(int value); + +static char must_rumble = 0; +#ifdef BUTTON_A_RUMBLE_TEST +static char force_rumble = 0; +#endif + +/* What was most recently read from the controller */ +static unsigned char last_built_report[GCN64_REPORT_SIZE]; + +/* What was most recently sent to the host */ +static unsigned char last_sent_report[GCN64_REPORT_SIZE]; + +static void n64Init(void) +{ + n64Update(); +} + +#define RSTATE_INIT 0 +#define RSTATE_OFF 1 +#define RSTATE_TURNON 2 +#define RSTATE_ON 3 +#define RSTATE_TURNOFF 4 +#define RSTATE_UNAVAILABLE 5 +static unsigned char n64_rumble_state = RSTATE_UNAVAILABLE; +unsigned char tmpdata[40]; + +static char initRumble(void) +{ + int count; + + tmpdata[0] = N64_EXPANSION_WRITE; + tmpdata[1] = 0x80; + tmpdata[2] = 0x01; + memset(tmpdata+3, 0x80, 32); + + /* Note: The old test (count > 0) was not reliable. */ + count = gcn64_transaction(tmpdata, 35); + if (count == 8) + return 0; + + return -1; +} + +static char controlRumble(char enable) +{ + int count; + + tmpdata[0] = N64_EXPANSION_WRITE; + tmpdata[1] = 0xc0; + tmpdata[2] = 0x1b; + memset(tmpdata+3, enable ? 0x01 : 0x00, 32); + count = gcn64_transaction(tmpdata, 35); + if (count == 8) + return 0; + + return -1; +} + +static char n64Update(void) +{ + int i; + unsigned char count; + unsigned char x,y; + unsigned char btns1, btns2; + unsigned char rb1, rb2; + unsigned char caps[3]; + + /* Pad answer to N64_GET_CAPABILITIES + * + * 0x050000 : 0000 0101 0000 0000 0000 0000 : No expansion pack + * 0x050001 : 0000 0101 0000 0000 0000 0001 : With expansion pack + * 0x050002 : 0000 0101 0000 0000 0000 0010 : Expansion pack removed + * + * Bit 0 tells us if there is something connected to the expansion port. + * Bit 1 tells is if there was something connected that has been removed. + */ + tmpdata[0] = N64_GET_CAPABILITIES; + count = gcn64_transaction(tmpdata, 1); + if (count != N64_CAPS_REPLY_LENGTH) { + // a failed read could mean the pack or controller was gone. Init + // will be necessary next time we detect a pack is present. + n64_rumble_state = RSTATE_INIT; + return -1; + } + + caps[0] = gcn64_protocol_getByte(0); + caps[1] = gcn64_protocol_getByte(8); + caps[2] = gcn64_protocol_getByte(16); + + /* Detect when a pack becomes present and schedule initialisation when it happens. */ + if ((caps[2] & 0x01) && (n64_rumble_state == RSTATE_UNAVAILABLE)) { + n64_rumble_state = RSTATE_INIT; + } + + /* Detect when a pack is removed. */ + if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) { + n64_rumble_state = RSTATE_UNAVAILABLE; + } +#ifdef BUTTON_A_RUMBLE_TEST + must_rumble = force_rumble; +#endif + + switch (n64_rumble_state) + { + case RSTATE_INIT: + /* Retry until the controller answers with a full byte. */ + if (initRumble() != 0) { + if (initRumble() != 0) { + n64_rumble_state = RSTATE_UNAVAILABLE; + } + break; + } + + if (must_rumble) { + controlRumble(1); + n64_rumble_state = RSTATE_ON; + } else { + controlRumble(0); + n64_rumble_state = RSTATE_OFF; + } + break; + + case RSTATE_TURNON: + if (0 == controlRumble(1)) { + n64_rumble_state = RSTATE_ON; + } + break; + + case RSTATE_TURNOFF: + if (0 == controlRumble(0)) { + n64_rumble_state = RSTATE_OFF; + } + break; + + case RSTATE_ON: + if (!must_rumble) { + controlRumble(0); + n64_rumble_state = RSTATE_OFF; + } + break; + + case RSTATE_OFF: + if (must_rumble) { + controlRumble(1); + n64_rumble_state = RSTATE_ON; + } + break; + } + + tmpdata[0] = N64_GET_STATUS; + count = gcn64_transaction(tmpdata, 1); + if (count != N64_GET_STATUS_REPLY_LENGTH) { + return -1; + } + +/* + Bit Function + 0 A + 1 B + 2 Z + 3 Start + 4 Directional Up + 5 Directional Down + 6 Directional Left + 7 Directional Right + 8 unknown (always 0) + 9 unknown (always 0) + 10 L + 11 R + 12 C Up + 13 C Down + 14 C Left + 15 C Right + 16-23: analog X axis + 24-31: analog Y axis + */ + + btns1 = gcn64_protocol_getByte(0); + btns2 = gcn64_protocol_getByte(8); + x = gcn64_protocol_getByte(16); // X axis + y = gcn64_protocol_getByte(24); // Y axis + +#ifdef BUTTON_A_RUMBLE_TEST + if (btns1 & 0x80) { + force_rumble = 1; + } else { + force_rumble = 0; + } +#endif + + // Remap buttons as they always were by this + // adapter. Might change in v3 when a N64 + // specific report descriptor will be used. + // + rb1 = rb2 = 0; + for (i=0; i<4; i++) // A B Z START + rb1 |= (btns1 & (0x80 >> i)) ? (0x01<> i) ? (0x10<> i) ? (0x01<> i) ? (0x04< + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * The original file gamepad.c contained this descriptor which was + * adapted from my original reportdesc.c by Sean Green for force + * feedback support. Big thanks to him from sharing back! + * + * The descriptor is intact, except for minor changes such as + * axis types and button quantity. + */ + +const uint8_t gcn64_usbHidReportDescriptor[] PROGMEM = { +///// gampad +0x05,0x01, // Usage Page Generic Desktop +0x09,0x05, // Usage Joystick +0xA1,0x01, // Collection Application +//axis + 0x85,0x01, // Report ID 1 + // not sure how this is to work? +// 0x05, 0x02, //USAGE_PAGE (Simulation Controls) + //0x09, 0xBB, //USAGE (Throttle) + 0x09, 0x01, // usage pointer + 0xA1, 0x00, // COLLECTION (phys) + 0x05, 0x01, // USAGE_PAGE (Generic desktop) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x06, // REPORT_COUNT (6) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x00, // Physical Minimum (255) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x33, // USAGE (Rx) + 0x09, 0x34, // USAGE (Ry) + 0x09, 0x35, // USAGE (Rz) + 0x09, 0x36, // USAGE (Slider) + 0x81, 0x02, // INPUT + +#define NUM_BUTTONS 16 + +//buttons + 0x05, 0x09, // USAGE_PAGE (Button) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, NUM_BUTTONS, // REPORT_COUNT (14) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, NUM_BUTTONS, // USAGE_MAXIMUM (Button 14) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, NUM_BUTTONS, // REPORT_COUNT (16) + 0x81, 0x02, // INPUT + + 0xc0, // END COLLECTION +#if 0 +//??? + 0x06,0x01,0xFF, // Usage Page Generic Desktop + 0x09,0x49, // Usage Undefined + 0x75,0x01, // Report Size 1 + 0x95,0x01, // Report Count 1 + 0x81,0x02, // Input (Variable) + 0x75,0x07, // Report Size 7 + 0x81,0x03, // Input (Constant, Variable) + #endif + + + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x92, // Usage ES Playing + 0xA1,0x02, // Collection Datalink + 0x85,0x02, // Report ID 2 + 0x09,0x9F, // Usage DS Device is Reset + 0x09,0xA0, // Usage DS Device is Pause + 0x09,0xA4, // Usage Actuator Power + 0x09,0xA5, // Usage Undefined + 0x09,0xA6, // Usage Undefined + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x35,0x00, // Physical Minimum 0 + 0x45,0x01, // Physical Maximum 1 + 0x75,0x01, // Report Size 1 + 0x95,0x05, // Report Count 5 + 0x81,0x02, // Input (Variable) + 0x95,0x03, // Report Count 3 + 0x75,0x01, // Report Size 1 + 0x81,0x03, // Input (Constant, Variable) + 0x09,0x94, // Usage PID Device Control + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x35,0x00, // Physical Minimum 0 + 0x45,0x01, // Physical Maximum 1 + 0x75,0x01, // Report Size 1 + 0x95,0x01, // Report Count 1 + 0x81,0x02, // Input (Variable) + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x07, // Report Size 7 + 0x95,0x01, // Report Count 1 + 0x81,0x02, // Input (Variable) + 0xC0 , // End Collection + + + 0x09,0x21, // Usage Set Effect Report + 0xA1,0x02, // Collection Datalink + 0x85,0x01, // Report ID 1 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x25, // Usage Effect Type + 0xA1,0x02, // Collection Datalink + 0x09,0x26, // Usage ET Constant Force + 0x09,0x27, // Usage ET Ramp + 0x09,0x30, // Usage ET Square + 0x09,0x31, // Usage ET Sine + 0x09,0x32, // Usage ET Triangle + 0x09,0x33, // Usage ET Sawtooth Up + 0x09,0x34, // Usage ET Sawtooth Down + 0x09,0x40, // Usage ET Spring + 0x09,0x41, // Usage ET Damper + 0x09,0x42, // Usage ET Inertia + 0x09,0x43, // Usage ET Friction + 0x09,0x28, // Usage ET Custom Force Data + 0x25,0x0C, // Logical Maximum Ch (12d) + 0x15,0x01, // Logical Minimum 1 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x0C, // Physical Maximum Ch (12d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x00, // Output + 0xC0 , // End Collection + + 0x09,0x50, // Usage Duration + 0x09,0x54, // Usage Trigger Repeat Interval + 0x09,0x51, // Usage Sample Period + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) + 0x66,0x03,0x10, // Unit 1003h (4099d) + 0x55,0xFD, // Unit Exponent FDh (253d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x03, // Report Count 3 + 0x91,0x02, // Output (Variable) + 0x55,0x00, // Unit Exponent 0 + 0x66,0x00,0x00, // Unit 0 + 0x09,0x52, // Usage Gain + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x53, // Usage Trigger Button + 0x15,0x01, // Logical Minimum 1 + 0x25,0x08, // Logical Maximum 8 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x08, // Physical Maximum 8 + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x55, // Usage Axes Enable + 0xA1,0x02, // Collection Datalink + 0x05,0x01, // Usage Page Generic Desktop + 0x09,0x30, // Usage X + 0x09,0x31, // Usage Y + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x75,0x01, // Report Size 1 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x56, // Usage Direction Enable + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x95,0x05, // Report Count 5 + 0x91,0x03, // Output (Constant, Variable) + 0x09,0x57, // Usage Direction + 0xA1,0x02, // Collection Datalink + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 + 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x66,0x14,0x00, // Unit 14h (20d) + 0x55,0xFE, // Unit Exponent FEh (254d) + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d) + 0x66,0x00,0x00, // Unit 0 + 0x75,0x08, // Report Size 8 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x55,0x00, // Unit Exponent 0 + 0x66,0x00,0x00, // Unit 0 + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0xA7, // Usage Undefined + 0x66,0x03,0x10, // Unit 1003h (4099d) + 0x55,0xFD, // Unit Exponent FDh (253d) + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x66,0x00,0x00, // Unit 0 + 0x55,0x00, // Unit Exponent 0 + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x5A, // Usage Set Envelope Report + 0xA1,0x02, // Collection Datalink + 0x85,0x02, // Report ID 2 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x5B, // Usage Attack Level + 0x09,0x5D, // Usage Fade Level + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x09,0x5C, // Usage Attack Time + 0x09,0x5E, // Usage Fade Time + 0x66,0x03,0x10, // Unit 1003h (4099d) + 0x55,0xFD, // Unit Exponent FDh (253d) + 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) + 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) + 0x75,0x10, // Report Size 10h (16d) + 0x91,0x02, // Output (Variable) + 0x45,0x00, // Physical Maximum 0 + 0x66,0x00,0x00, // Unit 0 + 0x55,0x00, // Unit Exponent 0 + 0xC0 , // End Collection + 0x09,0x5F, // Usage Set Condition Report + 0xA1,0x02, // Collection Datalink + 0x85,0x03, // Report ID 3 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x23, // Usage Parameter Block Offset + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x35,0x00, // Physical Minimum 0 + 0x45,0x01, // Physical Maximum 1 + 0x75,0x04, // Report Size 4 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x58, // Usage Type Specific Block Off... + 0xA1,0x02, // Collection Datalink + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 + 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x75,0x02, // Report Size 2 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x15,0x80, // Logical Minimum 80h (-128d) + 0x25,0x7F, // Logical Maximum 7Fh (127d) + 0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d) + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x09,0x60, // Usage CP Offset + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d) + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x09,0x61, // Usage Positive Coefficient + 0x09,0x62, // Usage Negative Coefficient + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x09,0x63, // Usage Positive Saturation + 0x09,0x64, // Usage Negative Saturation + 0x75,0x08, // Report Size 8 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x09,0x65, // Usage Dead Band + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x6E, // Usage Set Periodic Report + 0xA1,0x02, // Collection Datalink + 0x85,0x04, // Report ID 4 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x70, // Usage Magnitude + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x6F, // Usage Offset + 0x15,0x80, // Logical Minimum 80h (-128d) + 0x25,0x7F, // Logical Maximum 7Fh (127d) + 0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d) + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x71, // Usage Phase + 0x66,0x14,0x00, // Unit 14h (20d) + 0x55,0xFE, // Unit Exponent FEh (254d) + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d) + 0x91,0x02, // Output (Variable) + 0x09,0x72, // Usage Period + 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) + 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) + 0x66,0x03,0x10, // Unit 1003h (4099d) + 0x55,0xFD, // Unit Exponent FDh (253d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x66,0x00,0x00, // Unit 0 + 0x55,0x00, // Unit Exponent 0 + 0xC0 , // End Collection + 0x09,0x73, // Usage Set Constant Force Rep... + 0xA1,0x02, // Collection Datalink + 0x85,0x05, // Report ID 5 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x70, // Usage Magnitude + 0x16,0x01,0xFF, // Logical Minimum FF01h (-255d) + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d) + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x74, // Usage Set Ramp Force Report + 0xA1,0x02, // Collection Datalink + 0x85,0x06, // Report ID 6 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x75, // Usage Ramp Start + 0x09,0x76, // Usage Ramp End + 0x15,0x80, // Logical Minimum 80h (-128d) + 0x25,0x7F, // Logical Maximum 7Fh (127d) + 0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d) + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x08, // Report Size 8 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x68, // Usage Custom Force Data Rep... + 0xA1,0x02, // Collection Datalink + 0x85,0x07, // Report ID 7 + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x6C, // Usage Custom Force Data Offset + 0x15,0x00, // Logical Minimum 0 + 0x26,0x10,0x27, // Logical Maximum 2710h (10000d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x69, // Usage Custom Force Data + 0x15,0x81, // Logical Minimum 81h (-127d) + 0x25,0x7F, // Logical Maximum 7Fh (127d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x00, // Physical Maximum FFh (255d) + 0x75,0x08, // Report Size 8 + 0x95,0x0C, // Report Count Ch (12d) + 0x92,0x02,0x01, // Output (Variable, Buffered) + 0xC0 , // End Collection + 0x09,0x66, // Usage Download Force Sample + 0xA1,0x02, // Collection Datalink + 0x85,0x08, // Report ID 8 + 0x05,0x01, // Usage Page Generic Desktop + 0x09,0x30, // Usage X + 0x09,0x31, // Usage Y + 0x15,0x81, // Logical Minimum 81h (-127d) + 0x25,0x7F, // Logical Maximum 7Fh (127d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x00, // Physical Maximum FFh (255d) + 0x75,0x08, // Report Size 8 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x77, // Usage Effect Operation Report + 0xA1,0x02, // Collection Datalink + 0x85,0x0A, // Report ID Ah (10d) + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x78, // Usage Operation + 0xA1,0x02, // Collection Datalink + 0x09,0x79, // Usage Op Effect Start + 0x09,0x7A, // Usage Op Effect Start Solo + 0x09,0x7B, // Usage Op Effect Stop + 0x15,0x01, // Logical Minimum 1 + 0x25,0x03, // Logical Maximum 3 + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x00, // Output + 0xC0 , // End Collection + 0x09,0x7C, // Usage Loop Count + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x00, // Physical Maximum FFh (255d) + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x90, // Usage PID State Report + 0xA1,0x02, // Collection Datalink + 0x85,0x0B, // Report ID Bh (11d) + 0x09,0x22, // Usage Effect Block Index + 0x25,0x28, // Logical Maximum 28h (40d) + 0x15,0x01, // Logical Minimum 1 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x96, // Usage DC Disable Actuators + 0xA1,0x02, // Collection Datalink + 0x85,0x0C, // Report ID Ch (12d) + 0x09,0x97, // Usage DC Stop All Effects + 0x09,0x98, // Usage DC Device Reset + 0x09,0x99, // Usage DC Device Pause + 0x09,0x9A, // Usage DC Device Continue + 0x09,0x9B, // Usage PID Device State + 0x09,0x9C, // Usage DS Actuators Enabled + 0x15,0x01, // Logical Minimum 1 + 0x25,0x06, // Logical Maximum 6 + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x00, // Output + 0xC0 , // End Collection + 0x09,0x7D, // Usage PID Pool Report + 0xA1,0x02, // Collection Datalink + 0x85,0x0D, // Report ID Dh (13d) + 0x09,0x7E, // Usage RAM Pool Size + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x09,0x6B, // Usage Set Custom Force Report + 0xA1,0x02, // Collection Datalink + 0x85,0x0E, // Report ID Eh (14d) + 0x09,0x22, // Usage Effect Block Index + 0x15,0x01, // Logical Minimum 1 + 0x25,0x28, // Logical Maximum 28h (40d) + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x6D, // Usage Sample Count + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x00, // Physical Maximum FFh (255d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x09,0x51, // Usage Sample Period + 0x66,0x03,0x10, // Unit 1003h (4099d) + 0x55,0xFD, // Unit Exponent FDh (253d) + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x55,0x00, // Unit Exponent 0 + 0x66,0x00,0x00, // Unit 0 + 0xC0 , // End Collection + 0x09,0xAB, // Usage Undefined << Create New Effect Report + 0xA1,0x02, // Collection Datalink + 0x85,0x09, // Report ID 9 + 0x09,0x25, // Usage Effect Type + 0xA1,0x02, // Collection Datalink + 0x09,0x26, // Usage ET Constant Force + 0x09,0x27, // Usage ET Ramp + 0x09,0x30, // Usage ET Square + 0x09,0x31, // Usage ET Sine + 0x09,0x32, // Usage ET Triangle + 0x09,0x33, // Usage ET Sawtooth Up + 0x09,0x34, // Usage ET Sawtooth Down + 0x09,0x40, // Usage ET Spring + 0x09,0x41, // Usage ET Damper + 0x09,0x42, // Usage ET Inertia + 0x09,0x43, // Usage ET Friction + 0x09,0x28, // Usage ET Custom Force Data + 0x25,0x0C, // Logical Maximum Ch (12d) + 0x15,0x01, // Logical Minimum 1 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x0C, // Physical Maximum Ch (12d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0xB1,0x00, // Feature + 0xC0 , // End Collection + 0x05,0x01, // Usage Page Generic Desktop + 0x09,0x3B, // Usage Byte Count + 0x15,0x00, // Logical Minimum 0 + 0x26,0xFF,0x01, // Logical Maximum 1FFh (511d) + 0x35,0x00, // Physical Minimum 0 + 0x46,0xFF,0x01, // Physical Maximum 1FFh (511d) + 0x75,0x0A, // Report Size Ah (10d) + 0x95,0x01, // Report Count 1 + 0xB1,0x02, // Feature (Variable) + 0x75,0x06, // Report Size 6 + 0xB1,0x01, // Feature (Constant) +0xC0 , // End Collection +0x05,0x0F, // Usage Page Physical Interface +0x09,0x89, // Usage Block Load Status +0xA1,0x02, // Collection Datalink + 0x85,0x02, // Report ID 2 + 0x09,0x22, // Usage Effect Block Index + 0x25,0x28, // Logical Maximum 28h (40d) + 0x15,0x01, // Logical Minimum 1 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x28, // Physical Maximum 28h (40d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0xB1,0x02, // Feature (Variable) + 0x09,0x8B, // Usage Block Load Full + 0xA1,0x02, // Collection Datalink + 0x09,0x8C, // Usage Block Load Error + 0x09,0x8D, // Usage Block Handle + 0x09,0x8E, // Usage PID Block Free Report + 0x25,0x03, // Logical Maximum 3 + 0x15,0x01, // Logical Minimum 1 + 0x35,0x01, // Physical Minimum 1 + 0x45,0x03, // Physical Maximum 3 + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0xB1,0x00, // Feature + 0xC0 , // End Collection + 0x09,0xAC, // Usage Undefined + 0x15,0x00, // Logical Minimum 0 + 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) + 0x35,0x00, // Physical Minimum 0 + 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0xB1,0x00, // Feature +0xC0 , // End Collection +0x09,0x7F, // Usage ROM Pool Size +0xA1,0x02, // Collection Datalink + 0x85,0x03, // Report ID 3 + 0x09,0x80, // Usage ROM Effect Block Count + 0x75,0x10, // Report Size 10h (16d) + 0x95,0x01, // Report Count 1 + 0x15,0x00, // Logical Minimum 0 + 0x35,0x00, // Physical Minimum 0 + 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) + 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) + 0xB1,0x02, // Feature (Variable) + 0x09,0x83, // Usage PID Pool Move Report + 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x46,0xFF,0x00, // Physical Maximum FFh (255d) + 0x75,0x08, // Report Size 8 + 0x95,0x01, // Report Count 1 + 0xB1,0x02, // Feature (Variable) + 0x09,0xA9, // Usage Undefined + 0x09,0xAA, // Usage Undefined + 0x75,0x01, // Report Size 1 + 0x95,0x02, // Report Count 2 + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x35,0x00, // Physical Minimum 0 + 0x45,0x01, // Physical Maximum 1 + 0xB1,0x02, // Feature (Variable) + 0x75,0x06, // Report Size 6 + 0x95,0x01, // Report Count 1 + 0xB1,0x03, // Feature (Constant, Variable) + 0xC0, // End Collection +0xC0, // End Collection + + +}; + diff --git a/usart1.c b/usart1.c new file mode 100644 index 0000000..a1cb315 --- /dev/null +++ b/usart1.c @@ -0,0 +1,39 @@ +#include +#include +#include "usart1.h" + +#ifdef UART1_STDOUT +static int uart1_putchar(char c, FILE *stream) +{ + usart1_send(&c, 1); + return 0; +} + +static FILE mystdout = FDEV_SETUP_STREAM(uart1_putchar, NULL, + _FDEV_SETUP_WRITE); +#endif + +void usart1_send(void *data, int len) +{ + while (!(UCSR1A & (1< +#include +#include + +#include +#include +#include + +#include "usb.h" + +#define STATE_POWERED 0 +#define STATE_DEFAULT 1 +#define STATE_ADDRESS 2 +#define STATE_CONFIGURED 3 + +static volatile uint8_t g_usb_suspend; +static uint8_t g_ep0_buf[64]; +static uint8_t g_device_state = STATE_DEFAULT; +static uint8_t g_current_config; +static void *interrupt_data; +static volatile int interrupt_data_len = -1; + +#define CONTROL_WRITE_BUFSIZE 64 +static struct usb_request control_write_rq; +static volatile uint16_t control_write_len; +static volatile uint8_t control_write_in_progress; +static uint8_t control_write_buf[CONTROL_WRITE_BUFSIZE]; + +static const struct usb_parameters *g_params; + +static void initControlWrite(const struct usb_request *rq) +{ + memcpy(&control_write_rq, rq, sizeof(struct usb_request)); + control_write_len = 0; + control_write_in_progress = 1; +// printf("Init cw\r\n"); +} + +static int wcslen(const wchar_t *str) +{ + int i=0; + while (*str) { + str++; + i++; + } + return i; +} + +static void setupEndpoints(void) +{ + /*** EP0 ***/ + + // Order from figure 23-2 + UENUM = 0x00; // select endpoint +// UERST |= 0x01; // reset endpoint + UECONX = 1< max_len) { + len = max_len; + } + + if (progmem) { + const unsigned char *s = src; + for (i=0; ibmRequestType, rq->bRequest, rq->wValue, rq->wLength); + + if (USB_RQT_IS_HOST_TO_DEVICE(rq->bmRequestType)) + { + switch (rq->bmRequestType & USB_RQT_RECIPIENT_MASK) + { + case USB_RQT_RECIPIENT_DEVICE: + switch (rq->bRequest) + { + case USB_RQ_SET_ADDRESS: + UDADDR = rq->wValue; + while (!(UEINTX & (1<wValue); + if (!rq->wValue) { + g_device_state = STATE_DEFAULT; + } else { + g_device_state = STATE_ADDRESS; + } + break; + + case USB_RQ_SET_CONFIGURATION: + g_current_config = rq->wValue; + if (!g_current_config) { + g_device_state = STATE_ADDRESS; + } else { + g_device_state = STATE_CONFIGURED; + } + while (!(UEINTX & (1<bmRequestType & (USB_RQT_TYPE_MASK)) + { + case USB_RQT_CLASS: + switch(rq->bRequest) + { + case HID_CLSRQ_SET_IDLE: + while (!(UEINTX & (1<bRequest); + unhandled = 1; + } + break; + default: + unhandled = 1; + } + + break; + + case USB_RQT_RECIPIENT_ENDPOINT: + case USB_RQT_RECIPIENT_OTHER: + default: + break; + } + } + + // Request where we send data to the host. Handlers + // simply load the endpoint buffer and transmission + // is handled automatically. + if (USB_RQT_IS_DEVICE_TO_HOST(rq->bmRequestType)) + { + switch (rq->bmRequestType & USB_RQT_RECIPIENT_MASK) + { + case USB_RQT_RECIPIENT_DEVICE: + switch (rq->bRequest) + { + case USB_RQ_GET_STATUS: + { + unsigned char status[2] = { 0x00, 0x00 }; + // status[0] & 0x01 : Self powered + // status[1] & 0x02 : Remote wakeup + buf2EP(0, status, 2, rq->wLength, 0); + } + break; + + case USB_RQ_GET_CONFIGURATION: + { + if (g_device_state != STATE_CONFIGURED) { + unsigned char zero = 0; + buf2EP(0, &zero, 1, rq->wLength, 0); + } else { + buf2EP(0, &g_current_config, 1, rq->wLength, 0); + } + } + break; + + case USB_RQ_GET_DESCRIPTOR: + switch (rq->wValue >> 8) + { + case DEVICE_DESCRIPTOR: + buf2EP(0, (unsigned char*)g_params->devdesc, + sizeof(struct usb_device_descriptor), rq->wLength, + g_params->flags & USB_PARAM_FLAG_DEVDESC_PROGMEM); + break; + case CONFIGURATION_DESCRIPTOR: + // Check index if more than 1 config + buf2EP(0, (unsigned char*)g_params->configdesc, g_params->configdesc_ttllen, + rq->wLength, g_params->flags & USB_PARAM_FLAG_CONFDESC_PROGMEM); + break; + case STRING_DESCRIPTOR: + { + int id, len, slen; + struct usb_string_descriptor_header hdr; + + id = (rq->wValue & 0xff); + if (id > 0 && id <= g_params->num_strings) + { + id -= 1; // Our string table is zero-based + + len = rq->wLength; + slen = wcslen(g_params->strings[id]) << 1; + + hdr.bLength = sizeof(hdr) + slen; + hdr.bDescriptorType = STRING_DESCRIPTOR; + + buf2EP(0, (unsigned char*)&hdr, 2, len, 0); + len -= 2; + buf2EP(0, (unsigned char*)g_params->strings[id], slen, len, 0); + } + else if (id == 0) // Table of supported languages (string id 0) + { + unsigned char languages[4] = { + 4, STRING_DESCRIPTOR, 0x09, 0x10 // English (Canadian) + }; + buf2EP(0, languages, 4, rq->wLength, 0); + } + else + { + printf_P(PSTR("Unknown string id\r\n")); + } + } + break; + + case DEVICE_QUALIFIER_DESCRIPTOR: + // Full speed devices must respond with a request error. + unhandled = 1; + break; + + + default: +// printf("Unhandled descriptor 0x%02x\n", rq->wValue>>8); + unhandled = 1; + } + break; + + default: + unhandled = 1; + } + break; + + case USB_RQT_RECIPIENT_INTERFACE: + switch(rq->bmRequestType & (USB_RQT_TYPE_MASK)) + { + case USB_RQT_STANDARD: + switch (rq->bRequest) + { + case USB_RQ_GET_STATUS: + { // 9.4.5 Get Status, Figure 9-5. Reserved (0) + unsigned char status[2] = { 0x00, 0x00 }; + buf2EP(0, status, 2, rq->wLength, 0); + } + break; + + case USB_RQ_GET_DESCRIPTOR: + switch (rq->wValue >> 8) + { + case REPORT_DESCRIPTOR: + { + uint16_t rqlen = rq->wLength; + if (rqlen > g_params->reportdesc_len) { + rqlen = g_params->reportdesc_len; + }; + uint16_t todo = rqlen; + uint16_t pos = 0; + printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x, l: %d\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength); + + while(1) + { + printf("pos %d todo %d\r\n", pos, todo); + if (todo > 64) { + buf2EP(0, ((unsigned char*)g_params->reportdesc)+pos, 64, + 64, + g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM); + UEINTX &= ~(1<reportdesc)+pos, todo, + todo, + g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM); + UEINTX &= ~(1<bRequest) + { + case HID_CLSRQ_GET_REPORT: + { + if (g_params->getReport) { + const unsigned char *data; + uint16_t len; + len = g_params->getReport(rq, &data); + if (len) { + buf2EP(0, data, len, rq->wLength, 0); + } + } else { + // Treat as not-supported (i.e. STALL endpoint) + unhandled = 1; + } + } + break; + default: + unhandled = 1; + } + break; + + default: + unhandled = 1; + } + break; + + case USB_RQT_RECIPIENT_ENDPOINT: + switch (rq->bRequest) + { + case USB_RQ_GET_STATUS: + { // 9.4.5 Get Status, Figure 0-6 + unsigned char status[2] = { 0x00, 0x00 }; + // status[0] & 0x01 : Halt + buf2EP(0, status, 2, rq->wLength, 0); + } + break; + default: + unhandled = 1; + } + break; + + case USB_RQT_RECIPIENT_OTHER: + default: + unhandled = 1; + } + + if (!unhandled) + { + // Handle transmission now + UEINTX &= ~(1<bmRequestType, rq->bRequest, rq->wValue); + UECONX |= (1<bmRequestType & (USB_RQT_TYPE_MASK)) == USB_RQT_CLASS) { + if (g_params->setReport) { + if (g_params->setReport(rq, dat, len)) { + UECONX |= (1< +#include +#include + +/**** Standard USB defines ****/ + +#define DEVICE_DESCRIPTOR 0x01 +#define CONFIGURATION_DESCRIPTOR 0x02 +#define STRING_DESCRIPTOR 0x03 +#define INTERFACE_DESCRIPTOR 0x04 +#define ENDPOINT_DESCRIPTOR 0x05 +#define DEVICE_QUALIFIER_DESCRIPTOR 0x06 + +// USB HID 1.11 section 7.1.1 +#define HID_DESCRIPTOR 0x21 +#define REPORT_DESCRIPTOR 0x22 +#define PHYSICAL_DESCRIPTOR 0x23 + +#define USB_RQT_IS_HOST_TO_DEVICE(r) (((r) & 0x80) == 0) +#define USB_RQT_IS_DEVICE_TO_HOST(r) (((r) & 0x80) == 0x80) +#define USB_RQT_HOST_TO_DEVICE 0x00 +#define USB_RQT_DEVICE_TO_HOST 0x80 +#define USB_RQT_STANDARD (0x00 << 5) +#define USB_RQT_CLASS (0x01 << 5) +#define USB_RQT_VENDOR (0x02 << 5) +#define USB_RQT_TYPE_MASK (0x03 << 5) +#define USB_RQT_RECIPIENT_DEVICE 0x00 +#define USB_RQT_RECIPIENT_INTERFACE 0x01 +#define USB_RQT_RECIPIENT_ENDPOINT 0x02 +#define USB_RQT_RECIPIENT_OTHER 0x03 +#define USB_RQT_RECIPIENT_MASK 0x1F + +#define USB_RQ_GET_STATUS 0x00 +#define USB_RQ_CLEAR_FEATURE 0x01 +#define USB_RQ_SET_FEATURE 0x03 +#define USB_RQ_SET_ADDRESS 0x05 +#define USB_RQ_GET_DESCRIPTOR 0x06 +#define USB_RQ_SET_DESCRIPTOR 0x07 +#define USB_RQ_GET_CONFIGURATION 0x08 +#define USB_RQ_SET_CONFIGURATION 0x09 +#define USB_RQ_GET_INTERFACE 0x0A +#define USB_RQ_SET_INTERFACE 0x0B + +/* If the one you need is missing, add it. For values, + * see: http://www.usb.org/developers/defined_class + */ +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_CDC 0x02 +#define USB_DEVICE_CLASS_HID 0x03 +#define USB_DEVICE_CLASS_MASS_STORAGE 0x04 +#define USB_DEVICE_CLASS_HUB 0x05 +#define USB_DEVICE_CLASS_VENDOR 0xFF + +struct usb_request { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; // DEVICE_DESCRIPTOR + uint16_t bcdUSB; + uint8_t bDeviceClass; // class code + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize; // Max packet size for endpoint zero + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +}; + +#define CFG_DESC_ATTR_RESERVED 0x80 +#define CFG_DESC_ATTR_SELF_POWERED 0x40 +#define CFG_DESC_ATTR_REMOTE_WAKEUP 0x20 +struct usb_configuration_descriptor { + uint8_t bLength; // sizeof(struct usb_configuration_descriptor) + uint8_t bDescriptorType; // CONFIGURATION_DESCRIPTOR + uint16_t wTotalLength; // for all descriptors in this configuration. (Cfg, interface, endpoint, class) + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; // for SET_CONFIGURATION argument(must be >= 1) + uint8_t iConfiguration; // String descriptor index + uint8_t bmAttributes; // D7 (set to one), D6 Selft-powred, D5 remote wakeup + uint8_t bMaxPower; // in 2mA units +}; + +struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; // INTERFACE_DESCRIPTOR + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; // String descriptor index +}; + +#define TRANSFER_TYPE_CONTROL 0x0 +#define TRANSFER_TYPE_ISOCHRONOUS 0x1 +#define TRANSFER_TYPE_BULK 0x2 +#define TRANSFER_TYPE_INT 0x3 +#define LS_FS_INTERVAL_MS(v) ((v)) // At low/full speed, 1 ms units +#define HS_INTERVAL_US(v) ((v)/125) // At high speed, .125us units +struct usb_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketsize; + uint8_t bInterval; +}; + +struct usb_string_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* HID 1.11 section 4.2 */ +#define HID_SUBCLASS_NONE 0 +#define HUD_SUBCLASS_BOOT 1 + +/* HID 1.11 section 4.3 */ +#define HID_PROTOCOL_NONE 0 +#define HID_PROTOCOL_KEYBOARD 1 +#define HID_PROTOCOL_MOUSE 2 + +/* Other values defined at HID 1.11, 6.2.1 */ +#define HID_COUNTRY_NOT_SUPPORTED 0x00 + +struct usb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHid; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + + uint8_t bClassDescriptorType; + uint16_t wClassDescriptorLength; + // Note: At least one descriptor (REPORT) is + // required. Additional descriptors + // are declared as above (type, length) and + // appended to the HID descriptor. +}; + +#define HID_CLSRQ_GET_REPORT 0x01 +#define HID_CLSRQ_GET_IDLE 0x02 +#define HID_CLSRQ_GET_PROTOCOL 0x03 +#define HID_CLSRQ_SET_REPORT 0x09 +#define HID_CLSRQ_SET_IDLE 0x0A +#define HID_CLSRQ_SET_PROTOCOL 0x0B + +#define HID_REPORT_TYPE_INPUT 0x01 +#define HID_REPORT_TYPE_OUTPUT 0x02 +#define HID_REPORT_TYPE_FEATURE 0x03 + +/**** API ****/ + +#define USB_PARAM_FLAG_DEVDESC_PROGMEM 1 +#define USB_PARAM_FLAG_CONFDESC_PROGMEM 2 +#define USB_PARAM_FLAG_REPORTDESC_PROGMEM 4 + +struct usb_parameters { + uint8_t flags; + + const struct usb_device_descriptor *devdesc; + + const void *configdesc; + // length including all descriptors returned together + // for getdescriptor(config). (i.e. Config descriptor + + // interface descritor + hid and endpoints) + uint16_t configdesc_ttllen; + + uint8_t num_strings; + const wchar_t *const *strings; + + uint16_t reportdesc_len; + const unsigned char *reportdesc; + + // Warning: Called from interrupt handler. Implement accordingly. + uint16_t (*getReport)(struct usb_request *rq, const uint8_t **dat); + uint8_t (*setReport)(const struct usb_request *rq, const uint8_t *dat, uint16_t len); +}; + +void usb_interruptSend(void *data, int len); // EP1 +void usb_init(const struct usb_parameters *params); +void usb_doTasks(void); +void usb_shutdown(void); + + +#endif // _usb_h__ diff --git a/wait_then_flash.sh b/wait_then_flash.sh new file mode 100755 index 0000000..f6eebd6 --- /dev/null +++ b/wait_then_flash.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ $# -ne 2 ]; then + echo "Syntax: ./wait_then_flash.sh CPU HEXFILE" + exit 1; +fi + +CPU=$1 +HEXFILE=$2 + +echo "Will program $HEXFILE on $CPU target" +echo "Polling for chip..." +while true; do + dfu-programmer $1 erase + if [ $? -eq 0 ]; then + echo "Chip found. Erased." + break; + fi + + sleep 1 +done + +echo "Writing flash..." +dfu-programmer $CPU flash $HEXFILE + +echo "Starting program..." +dfu-programmer $CPU start