├── .gitattributes ├── usbdrvasm.asm ├── keywords.txt ├── oddebug.c ├── README.md ├── examples ├── CapsLockToggle │ └── CapsLockToggle.ino ├── SendMultipleKeys │ └── SendMultipleKeys.ino └── GettingStarted │ └── GettingStarted.ino ├── oddebug.h ├── USBKeyboard.h ├── usbportability.h ├── keycodes.h ├── USB-ID-FAQ.txt ├── USB-IDs-for-free.txt ├── USBKeyboard.cpp ├── asmcommon.inc ├── VUSB-Readme.txt ├── usbdrvasm.S ├── usbdrvasm16.inc ├── usbdrvasm20.inc ├── usbdrvasm12.inc ├── usbdrvasm165.inc ├── usbdrvasm15.inc ├── usbconfig.h └── usbdrv.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Let Linguist ignore V-USB files 5 | asmcommon.inc linguist-vendored 6 | oddebug.h linguist-vendored 7 | oddebug.c linguist-vendored 8 | usbconfig.h linguist-vendored 9 | usbdrv.h linguist-vendored 10 | usbdrv.c linguist-vendored 11 | usbdrvasm.S linguist-vendored 12 | usbdrvasm.asm linguist-vendored 13 | usbdrvasm12.inc linguist-vendored 14 | usbdrvasm128.inc linguist-vendored 15 | usbdrvasm15.inc linguist-vendored 16 | usbdrvasm16.inc linguist-vendored 17 | usbdrvasm165.inc linguist-vendored 18 | usbdrvasm18-crc.inc linguist-vendored 19 | usbdrvasm20.inc linguist-vendored 20 | usbportability.h linguist-vendored -------------------------------------------------------------------------------- /usbdrvasm.asm: -------------------------------------------------------------------------------- 1 | /* Name: usbdrvasm.asm 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2006-03-01 5 | * Tabsize: 4 6 | * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * This Revision: $Id$ 9 | */ 10 | 11 | /* 12 | General Description: 13 | The IAR compiler/assembler system prefers assembler files with file extension 14 | ".asm". We simply provide this file as an alias for usbdrvasm.S. 15 | 16 | Thanks to Oleg Semyonov for his help with the IAR tools port! 17 | */ 18 | 19 | #include "usbdrvasm.S" 20 | 21 | end 22 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for USBKeyboard 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | USBKeyboard KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | update KEYWORD2 16 | write KEYWORD2 17 | sendKey KEYWORD2 18 | sendKeys KEYWORD2 19 | asciiToKeycode KEYWORD2 20 | asciiToShiftState KEYWORD2 21 | isCapsLockActivated KEYWORD2 22 | getCapsLockToggleCount KEYWORD2 23 | resetCapsLockToggleCount KEYWORD2 24 | 25 | 26 | ####################################### 27 | # Constants (LITERAL1) 28 | ####################################### 29 | 30 | LAYOUT_US LITERAL1 31 | LAYOUT_DE LITERAL1 32 | CONTROL LITERAL1 33 | SHIFT LITERAL1 34 | ALT LITERAL1 35 | GUI LITERAL1 36 | -------------------------------------------------------------------------------- /oddebug.c: -------------------------------------------------------------------------------- 1 | /* Name: oddebug.c 2 | * Project: AVR library 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2005-01-16 5 | * Tabsize: 4 6 | * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * This Revision: $Id: oddebug.c 692 2008-11-07 15:07:40Z cs $ 9 | */ 10 | 11 | #include "oddebug.h" 12 | 13 | #if DEBUG_LEVEL > 0 14 | 15 | #warning "Never compile production devices with debugging enabled" 16 | 17 | static void uartPutc(char c) 18 | { 19 | while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ 20 | ODDBG_UDR = c; 21 | } 22 | 23 | static uchar hexAscii(uchar h) 24 | { 25 | h &= 0xf; 26 | if(h >= 10) 27 | h += 'a' - (uchar)10 - '0'; 28 | h += '0'; 29 | return h; 30 | } 31 | 32 | static void printHex(uchar c) 33 | { 34 | uartPutc(hexAscii(c >> 4)); 35 | uartPutc(hexAscii(c)); 36 | } 37 | 38 | void odDebug(uchar prefix, uchar *data, uchar len) 39 | { 40 | printHex(prefix); 41 | uartPutc(':'); 42 | while(len--){ 43 | uartPutc(' '); 44 | printHex(*data++); 45 | } 46 | uartPutc('\r'); 47 | uartPutc('\n'); 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USBKeyboard 2 | ## Introduction 3 | USBKeyboard is a library for the Arduino IDE. It adds HID USB keyboard functionality to your projects, allowing Arduinos and other AVRs to act as a HID USB Devices. 4 | Due to its HID protocol no drivers have to be installed, the USB keyboard connection will work with every PC and OS. 5 | 6 | ## Getting Started 7 | Connect your Arduino to the USB connector following the given schematic below and add a pushbutton betwen D7 and GND. Copy this repository into your `libraries` folder and (re)start the Arduino IDE. 8 | Open `File > Examples > USBKeyboard > USBKeyboard_GettingStarted` and upload it to your Arduino. 9 | Unplug your Arduino from your PC and plug in the USB connector. The keyboard should be recognized automatically by your PC, open a text editor of your choice and push the button afterwards, a message will be typed in. 10 | 11 | ## Schematic 12 | ![Schematic](https://petrockblog.files.wordpress.com/2012/05/wpid-photo-19-05-2012-22201.jpg "Schematic for the Arduino UNO") 13 | 14 | You just need the zener diodes if your Arduino/AVR is running above 3.6V 15 | (following the USB specification 3.6V is the maximum for D+ and D-). 16 | 17 | ## Reference 18 | This library is based on the [V-USB code](https://www.obdev.at/products/vusb/) by Objective Developement. 19 | 20 | ## License 21 | As the V-USB code, this library is licensed under GNU GPLv3, see [`LICENSE.txt`](LICENSE.txt) for further information. 22 | -------------------------------------------------------------------------------- /examples/CapsLockToggle/CapsLockToggle.ino: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * 3 | * File: CapsLockToggle.ino 4 | * Author: Julian Schuler (https://github.com/julianschuler) 5 | * License: GNU GPLv3, see LICENSE.txt 6 | * Description: This file is an example from the USBKeyboard library. 7 | * It types in a message after hitting Caps Lock three times. 8 | * 9 | *************************************************************************/ 10 | 11 | #include 12 | 13 | 14 | /* currently available layouts: LAYOUT_US, LAYOUT_DE */ 15 | #define LAYOUT LAYOUT_US 16 | 17 | 18 | /* create keyboard object mKeyboard */ 19 | USBKeyboard mKeyboard = USBKeyboard(); 20 | 21 | 22 | void setup() { 23 | /* USB timing has to be exact, therefore deactivate Timer0 interrupt */ 24 | TIMSK0 = 0; 25 | 26 | /* initialize the keyboard */ 27 | mKeyboard.begin() 28 | } 29 | 30 | 31 | void loop() { 32 | /* call this function at least every 20ms, otherwise an USB timeout will occur */ 33 | mKeyboard.update(); 34 | 35 | /* check if Caps Lock was toggled at least three times */ 36 | if (mKeyboard.getCapsLockToggleCount() >= 3) { 37 | /* reset Caps Lock toggle counter */ 38 | mKeyboard.resetCapsLockToggleCount(); 39 | 40 | /* type in a custom message */ 41 | mKeyboard.println("Hey, you have found Caps Lock on your keyboard!"); 42 | } 43 | 44 | /* due to the deactivation of the Timer0 interrupt, delay() 45 | * and millis() won't work, call delayMicroseconds() instead */ 46 | delayMicroseconds(20000); 47 | } 48 | -------------------------------------------------------------------------------- /examples/SendMultipleKeys/SendMultipleKeys.ino: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * 3 | * File: SendMultipleKeys.ino 4 | * Author: Julian Schuler (https://github.com/julianschuler) 5 | * License: GNU GPLv3, see LICENSE.txt 6 | * Description: This file is an example from the USBKeyboard library. 7 | * It sends "Win+D" and "Win+E" (for Windows: show Desktop 8 | * and open Explorer) after hitting Caps Lock three times. 9 | * 10 | *************************************************************************/ 11 | 12 | #include 13 | 14 | 15 | /* currently available layouts: LAYOUT_US, LAYOUT_DE */ 16 | #define LAYOUT LAYOUT_US 17 | 18 | 19 | /* create keyboard object mKeyboard */ 20 | USBKeyboard mKeyboard = USBKeyboard(); 21 | 22 | 23 | void setup() { 24 | /* USB timing has to be exact, therefore deactivate Timer0 interrupt */ 25 | TIMSK0 = 0; 26 | 27 | /* initialize the keyboard */ 28 | mKeyboard.begin() 29 | } 30 | 31 | 32 | void loop() { 33 | /* call this function at least every 20ms, otherwise an USB timeout will occur */ 34 | mKeyboard.update(); 35 | 36 | /* check if Caps Lock was toggled at least three times */ 37 | if (mKeyboard.getCapsLockToggleCount() >= 3) { 38 | /* reset Caps Lock toggle counter */ 39 | mKeyboard.resetCapsLockToggleCount(); 40 | 41 | /* send the E and D keys together with the GUI key (for Windows: Win key) 42 | * available modifiers: CONTROL, SHIFT, ALT, GUI */ 43 | mKeyboard.sendKeys(mKeyboard.asciiToKeycode('D'), mKeyboard.asciiToKeycode('E'), 0, 0, 0, 0, MODIFIER_GUI); 44 | } 45 | 46 | /* due to the deactivation of the Timer0 interrupt, delay() 47 | * and millis() won't work, call delayMicroseconds() instead */ 48 | delayMicroseconds(20000); 49 | } 50 | -------------------------------------------------------------------------------- /examples/GettingStarted/GettingStarted.ino: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * 3 | * File: GettingStarted.ino 4 | * Author: Julian Schuler (https://github.com/julianschuler) 5 | * License: GNU GPLv3, see LICENSE.txt 6 | * Description: This file is an example from the USBKeyboard library. 7 | * It types in a message after pressing a button and shows 8 | * the state of Caps Lock with the internal LED. 9 | * 10 | **************************************************************************/ 11 | 12 | #include 13 | 14 | /* connect a button to this pin and GND */ 15 | #define BUTTON_PIN 7 16 | 17 | /* currently available layouts: LAYOUT_US, LAYOUT_DE */ 18 | #define LAYOUT LAYOUT_US 19 | 20 | 21 | /* create keyboard object mKeyboard */ 22 | USBKeyboard mKeyboard = USBKeyboard(); 23 | 24 | bool lastButtonState = HIGH; 25 | bool lastCapsLockState; 26 | 27 | 28 | void setup() { 29 | /* USB timing has to be exact, therefore deactivate Timer0 interrupt */ 30 | TIMSK0 = 0; 31 | 32 | /* initialize the keyboard */ 33 | mKeyboard.begin() 34 | 35 | /* set the button pin as input and activate the internal pullup resistor */ 36 | pinMode(BUTTON_PIN, INPUT_PULLUP); 37 | 38 | /* set the internal LED pin (normally D13) as output */ 39 | pinMode(LED_BUILTIN, OUTPUT); 40 | 41 | /* set the internal LED corresponding to the Caps Lock state */ 42 | if (mKeyboard.isCapsLockActivated()) { 43 | digitalWrite(LED_BUILTIN, HIGH); 44 | lastCapsLockState = HIGH; 45 | } 46 | else { 47 | digitalWrite(LED_BUILTIN, LOW); 48 | lastCapsLockState = LOW; 49 | } 50 | } 51 | 52 | 53 | void loop() { 54 | /* call this function at least every 20ms, otherwise an USB timeout will occur */ 55 | mKeyboard.update(); 56 | 57 | /* check if button is pressed */ 58 | if (digitalRead(BUTTON_PIN) == LOW) { 59 | if (lastButtonState == HIGH) { 60 | /* print() and println() can be used to type in chars, Strings 61 | * and numbers, similar to Serial.print() and Serial.println() */ 62 | mKeyboard.println("Hello World!"); 63 | lastButtonState = LOW; 64 | } 65 | } 66 | else if (lastButtonState == LOW) { 67 | lastButtonState = HIGH; 68 | } 69 | 70 | /* check if the Caps Lock state has changed */ 71 | if (mKeyboard.isCapsLockActivated() != lastCapsLockState) { 72 | /* toggle lastCapsLockState */ 73 | lastCapsLockState = !lastCapsLockState; 74 | 75 | /* toggle LED state */ 76 | digitalWrite(LED_BUILTIN, lastCapsLockState); 77 | } 78 | 79 | /* due to the deactivation of the Timer0 interrupt, delay() 80 | * and millis() won't work, call delayMicroseconds() instead */ 81 | delayMicroseconds(20000); 82 | } 83 | -------------------------------------------------------------------------------- /oddebug.h: -------------------------------------------------------------------------------- 1 | /* Name: oddebug.h 2 | * Project: AVR library 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2005-01-16 5 | * Tabsize: 4 6 | * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * This Revision: $Id: oddebug.h 692 2008-11-07 15:07:40Z cs $ 9 | */ 10 | 11 | #ifndef __oddebug_h_included__ 12 | #define __oddebug_h_included__ 13 | 14 | /* 15 | General Description: 16 | This module implements a function for debug logs on the serial line of the 17 | AVR microcontroller. Debugging can be configured with the define 18 | 'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging 19 | calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is 20 | 2, DBG1 and DBG2 logs will be printed. 21 | 22 | A debug log consists of a label ('prefix') to indicate which debug log created 23 | the output and a memory block to dump in hex ('data' and 'len'). 24 | */ 25 | 26 | 27 | #ifndef F_CPU 28 | # define F_CPU 12000000 /* 12 MHz */ 29 | #endif 30 | 31 | /* make sure we have the UART defines: */ 32 | #include "usbportability.h" 33 | 34 | #ifndef uchar 35 | # define uchar unsigned char 36 | #endif 37 | 38 | #if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ 39 | # warning "Debugging disabled because device has no UART" 40 | # undef DEBUG_LEVEL 41 | #endif 42 | 43 | #ifndef DEBUG_LEVEL 44 | # define DEBUG_LEVEL 0 45 | #endif 46 | 47 | /* ------------------------------------------------------------------------- */ 48 | 49 | #if DEBUG_LEVEL > 0 50 | # define DBG1(prefix, data, len) odDebug(prefix, data, len) 51 | #else 52 | # define DBG1(prefix, data, len) 53 | #endif 54 | 55 | #if DEBUG_LEVEL > 1 56 | # define DBG2(prefix, data, len) odDebug(prefix, data, len) 57 | #else 58 | # define DBG2(prefix, data, len) 59 | #endif 60 | 61 | /* ------------------------------------------------------------------------- */ 62 | 63 | #if DEBUG_LEVEL > 0 64 | extern void odDebug(uchar prefix, uchar *data, uchar len); 65 | 66 | /* Try to find our control registers; ATMEL likes to rename these */ 67 | 68 | #if defined UBRR 69 | # define ODDBG_UBRR UBRR 70 | #elif defined UBRRL 71 | # define ODDBG_UBRR UBRRL 72 | #elif defined UBRR0 73 | # define ODDBG_UBRR UBRR0 74 | #elif defined UBRR0L 75 | # define ODDBG_UBRR UBRR0L 76 | #endif 77 | 78 | #if defined UCR 79 | # define ODDBG_UCR UCR 80 | #elif defined UCSRB 81 | # define ODDBG_UCR UCSRB 82 | #elif defined UCSR0B 83 | # define ODDBG_UCR UCSR0B 84 | #endif 85 | 86 | #if defined TXEN 87 | # define ODDBG_TXEN TXEN 88 | #else 89 | # define ODDBG_TXEN TXEN0 90 | #endif 91 | 92 | #if defined USR 93 | # define ODDBG_USR USR 94 | #elif defined UCSRA 95 | # define ODDBG_USR UCSRA 96 | #elif defined UCSR0A 97 | # define ODDBG_USR UCSR0A 98 | #endif 99 | 100 | #if defined UDRE 101 | # define ODDBG_UDRE UDRE 102 | #else 103 | # define ODDBG_UDRE UDRE0 104 | #endif 105 | 106 | #if defined UDR 107 | # define ODDBG_UDR UDR 108 | #elif defined UDR0 109 | # define ODDBG_UDR UDR0 110 | #endif 111 | 112 | static inline void odDebugInit(void) 113 | { 114 | ODDBG_UCR |= (1< 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /* constants */ 22 | #define MODIFIER_CONTROL (1<<0) 23 | #define MODIFIER_SHIFT (1<<1) 24 | #define MODIFIER_ALT (1<<2) 25 | #define MODIFIER_GUI (1<<3) 26 | 27 | #ifndef LAYOUT 28 | #define LAYOUT LAYOUT_US 29 | #endif 30 | 31 | /* makros */ 32 | #define conc(a, b) (a ## b) 33 | #define concat(a, b) conc(a, b) 34 | 35 | 36 | /* global variables */ 37 | static uint8_t idle_rate = 500 / 4; /* see HID1_11.pdf sect 7.2.4 */ 38 | static uint8_t protocol_version = 0; /* see HID1_11.pdf sect 7.2.6 */ 39 | volatile static uint8_t LED_states = 0; /* see HID1_11.pdf appendix B section 1 */ 40 | volatile static uint8_t toggle_counter = 0; /* keep track of how many times caps lock have toggled */ 41 | static uint8_t keyboard_layout = 0; /* keyboard layout, US layout by standard */ 42 | 43 | 44 | class USBKeyboard : public Print { 45 | public: /*#################### PUBLIC FUNCTIONS ####################*/ 46 | USBKeyboard() = default; 47 | 48 | /******************************************************* 49 | * call it when initializing the library 50 | ******************************************************/ 51 | void begin(uint8_t layout = 0); 52 | 53 | /******************************************************* 54 | * Call this function at least every 20ms, 55 | * otherwise an USB timeout will occur 56 | ******************************************************/ 57 | void update(); 58 | 59 | 60 | /******************************************************* 61 | * Type a single char to the USB host 62 | ******************************************************/ 63 | virtual size_t write(uint8_t ascii); 64 | 65 | 66 | /******************************************************* 67 | * Send a single key to the USB host 68 | ******************************************************/ 69 | void sendKey(uint8_t keycode) {sendKey(keycode, 0);} 70 | void sendKey(uint8_t keycode, uint8_t modifiers); 71 | 72 | 73 | /******************************************************* 74 | * Send up to 6 keys to the USB host 75 | ******************************************************/ 76 | void sendKeys(uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5, uint8_t keycode6) {sendKeys(keycode1, keycode2, keycode3, keycode4, keycode5, keycode6, 0);} 77 | void sendKeys(uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5, uint8_t keycode6, uint8_t modifiers); 78 | 79 | 80 | /******************************************************* 81 | * Translate ASCII char to keycode 82 | ******************************************************/ 83 | uint8_t asciiToKeycode(char ascii); 84 | 85 | 86 | /******************************************************* 87 | * Translate ASCII char to Shift state, taking into 88 | * consideration the status of Caps Lock 89 | ******************************************************/ 90 | uint8_t asciiToShiftState(char ascii); 91 | 92 | 93 | /******************************************************* 94 | * Get the state of Caps Lock 95 | ******************************************************/ 96 | bool isCapsLockActivated(); 97 | 98 | 99 | /******************************************************* 100 | * Get how often Caps Lock was toggled 101 | ******************************************************/ 102 | uint8_t getCapsLockToggleCount(); 103 | 104 | 105 | /******************************************************* 106 | * Reset the Caps Lock toggle counter 107 | ******************************************************/ 108 | void resetCapsLockToggleCount(); 109 | 110 | 111 | private: /*################### PRIVATE FUNCTIONS ###################*/ 112 | /******************************************************* 113 | * Send the keyboard report 114 | ******************************************************/ 115 | void sendReport(uint8_t modifiers, uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5, uint8_t keycode6); 116 | }; 117 | 118 | #endif /* __USBKeyboard_h__ */ 119 | -------------------------------------------------------------------------------- /usbportability.h: -------------------------------------------------------------------------------- 1 | /* Name: usbportability.h 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2008-06-17 5 | * Tabsize: 4 6 | * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * This Revision: $Id: usbportability.h 740 2009-04-13 18:23:31Z cs $ 9 | */ 10 | 11 | /* 12 | General Description: 13 | This header is intended to contain all (or at least most of) the compiler 14 | and library dependent stuff. The C code is written for avr-gcc and avr-libc. 15 | The API of other development environments is converted to gcc's and avr-libc's 16 | API by means of defines. 17 | 18 | This header also contains all system includes since they depend on the 19 | development environment. 20 | 21 | Thanks to Oleg Semyonov for his help with the IAR tools port! 22 | */ 23 | 24 | #ifndef __usbportability_h_INCLUDED__ 25 | #define __usbportability_h_INCLUDED__ 26 | 27 | /* We check explicitly for IAR and CodeVision. Default is avr-gcc/avr-libc. */ 28 | 29 | /* ------------------------------------------------------------------------- */ 30 | #if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ /* check for IAR */ 31 | /* ------------------------------------------------------------------------- */ 32 | 33 | #ifndef ENABLE_BIT_DEFINITIONS 34 | # define ENABLE_BIT_DEFINITIONS 1 /* Enable bit definitions */ 35 | #endif 36 | 37 | /* Include IAR headers */ 38 | #include 39 | #ifndef __IAR_SYSTEMS_ASM__ 40 | # include 41 | #endif 42 | 43 | #define __attribute__(arg) /* not supported on IAR */ 44 | 45 | #ifdef __IAR_SYSTEMS_ASM__ 46 | # define __ASSEMBLER__ /* IAR does not define standard macro for asm */ 47 | #endif 48 | 49 | #ifdef __HAS_ELPM__ 50 | # define PROGMEM __farflash 51 | #else 52 | # define PROGMEM __flash 53 | #endif 54 | 55 | #define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) 56 | 57 | /* The following definitions are not needed by the driver, but may be of some 58 | * help if you port a gcc based project to IAR. 59 | */ 60 | #define cli() __disable_interrupt() 61 | #define sei() __enable_interrupt() 62 | #define wdt_reset() __watchdog_reset() 63 | #define _BV(x) (1 << (x)) 64 | 65 | /* assembler compatibility macros */ 66 | #define nop2 rjmp $+2 /* jump to next instruction */ 67 | #define XL r26 68 | #define XH r27 69 | #define YL r28 70 | #define YH r29 71 | #define ZL r30 72 | #define ZH r31 73 | #define lo8(x) LOW(x) 74 | #define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ 75 | 76 | /* Depending on the device you use, you may get problems with the way usbdrv.h 77 | * handles the differences between devices. Since IAR does not use #defines 78 | * for MCU registers, we can't check for the existence of a particular 79 | * register with an #ifdef. If the autodetection mechanism fails, include 80 | * definitions for the required USB_INTR_* macros in your usbconfig.h. See 81 | * usbconfig-prototype.h and usbdrv.h for details. 82 | */ 83 | 84 | /* ------------------------------------------------------------------------- */ 85 | #elif __CODEVISIONAVR__ /* check for CodeVision AVR */ 86 | /* ------------------------------------------------------------------------- */ 87 | /* This port is not working (yet) */ 88 | 89 | /* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ 90 | 91 | #include 92 | #include 93 | 94 | #define __attribute__(arg) /* not supported on IAR */ 95 | 96 | #define PROGMEM __flash 97 | #define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) 98 | 99 | #ifndef __ASSEMBLER__ 100 | static inline void cli(void) 101 | { 102 | #asm("cli"); 103 | } 104 | static inline void sei(void) 105 | { 106 | #asm("sei"); 107 | } 108 | #endif 109 | #define _delay_ms(t) delay_ms(t) 110 | #define _BV(x) (1 << (x)) 111 | #define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ 112 | 113 | #define macro .macro 114 | #define endm .endmacro 115 | #define nop2 rjmp .+0 /* jump to next instruction */ 116 | 117 | /* ------------------------------------------------------------------------- */ 118 | #else /* default development environment is avr-gcc/avr-libc */ 119 | /* ------------------------------------------------------------------------- */ 120 | 121 | #include 122 | #ifdef __ASSEMBLER__ 123 | # define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ 124 | #else 125 | # include 126 | #endif 127 | 128 | #define USB_READ_FLASH(addr) pgm_read_byte(addr) 129 | 130 | #define macro .macro 131 | #define endm .endm 132 | #define nop2 rjmp .+0 /* jump to next instruction */ 133 | 134 | #endif /* development environment */ 135 | 136 | /* for conveniecne, ensure that PRG_RDB exists */ 137 | #ifndef PRG_RDB 138 | # define PRG_RDB(addr) USB_READ_FLASH(addr) 139 | #endif 140 | #endif /* __usbportability_h_INCLUDED__ */ 141 | -------------------------------------------------------------------------------- /keycodes.h: -------------------------------------------------------------------------------- 1 | #ifndef __KEYCODES__ 2 | #define __KEYCODES__ 3 | 4 | 5 | PROGMEM const uint8_t KEYCODES_LAYOUT_US[96] = { 6 | 0x2C, /* Space */ 7 | 0x1E, /* ! */ 8 | 0x34, /* " */ 9 | 0x20, /* # */ 10 | 0x21, /* $ */ 11 | 0x22, /* % */ 12 | 0x24, /* & */ 13 | 0x34, /* ' */ 14 | 0x26, /* ( */ 15 | 0x27, /* ) */ 16 | 0x25, /* * */ 17 | 0x2E, /* + */ 18 | 0x36, /* , */ 19 | 0x2D, /* - */ 20 | 0x37, /* . */ 21 | 0x38, /* / */ 22 | 0x27, /* 0 */ 23 | 0x1E, /* 1 */ 24 | 0x1F, /* 2 */ 25 | 0x20, /* 3 */ 26 | 0x21, /* 4 */ 27 | 0x22, /* 5 */ 28 | 0x23, /* 6 */ 29 | 0x24, /* 7 */ 30 | 0x25, /* 8 */ 31 | 0x26, /* 9 */ 32 | 0x33, /* : */ 33 | 0x33, /* ; */ 34 | 0x36, /* < */ 35 | 0x2E, /* = */ 36 | 0x37, /* > */ 37 | 0x38, /* ? */ 38 | 0x1F, /* @ */ 39 | 0x04, /* A */ 40 | 0x05, /* B */ 41 | 0x06, /* C */ 42 | 0x07, /* D */ 43 | 0x08, /* E */ 44 | 0x09, /* F */ 45 | 0x0A, /* G */ 46 | 0x0B, /* H */ 47 | 0x0C, /* I */ 48 | 0x0D, /* J */ 49 | 0x0E, /* K */ 50 | 0x0F, /* L */ 51 | 0x10, /* M */ 52 | 0x11, /* N */ 53 | 0x12, /* O */ 54 | 0x13, /* P */ 55 | 0x14, /* Q */ 56 | 0x15, /* R */ 57 | 0x16, /* S */ 58 | 0x17, /* T */ 59 | 0x18, /* U */ 60 | 0x19, /* V */ 61 | 0x1A, /* W */ 62 | 0x1B, /* X */ 63 | 0x1C, /* Y */ 64 | 0x1D, /* Z */ 65 | 0x2F, /* [ */ 66 | 0x31, /* \ */ 67 | 0x30, /* ] */ 68 | 0x23, /* ^ */ 69 | 0x2D, /* _ */ 70 | 0x35, /* ` */ 71 | 0x04, /* a */ 72 | 0x05, /* b */ 73 | 0x06, /* c */ 74 | 0x07, /* d */ 75 | 0x08, /* e */ 76 | 0x09, /* f */ 77 | 0x0A, /* g */ 78 | 0x0B, /* h */ 79 | 0x0C, /* i */ 80 | 0x0D, /* j */ 81 | 0x0E, /* k */ 82 | 0x0F, /* l */ 83 | 0x10, /* m */ 84 | 0x11, /* n */ 85 | 0x12, /* o */ 86 | 0x13, /* p */ 87 | 0x14, /* q */ 88 | 0x15, /* r */ 89 | 0x16, /* s */ 90 | 0x17, /* t */ 91 | 0x18, /* u */ 92 | 0x19, /* v */ 93 | 0x1A, /* w */ 94 | 0x1B, /* x */ 95 | 0x1C, /* y */ 96 | 0x1D, /* z */ 97 | 0x2F, /* } */ 98 | 0x31, /* | */ 99 | 0x30, /* } */ 100 | 0x35 /* ~ */ 101 | }; 102 | 103 | 104 | PROGMEM const uint8_t KEYCODES_LAYOUT_DE[96] = { 105 | 0x2C, /* Space */ 106 | 0x1E, /* ! */ 107 | 0x1F, /* " */ 108 | 0x31, /* # */ 109 | 0x21, /* $ */ 110 | 0x22, /* % */ 111 | 0x23, /* & */ 112 | 0x31, /* ' */ 113 | 0x25, /* ( */ 114 | 0x26, /* ) */ 115 | 0x30, /* * */ 116 | 0x30, /* + */ 117 | 0x36, /* , */ 118 | 0x38, /* - */ 119 | 0x37, /* . */ 120 | 0x24, /* / */ 121 | 0x27, /* 0 */ 122 | 0x1E, /* 1 */ 123 | 0x1F, /* 2 */ 124 | 0x20, /* 3 */ 125 | 0x21, /* 4 */ 126 | 0x22, /* 5 */ 127 | 0x23, /* 6 */ 128 | 0x24, /* 7 */ 129 | 0x25, /* 8 */ 130 | 0x26, /* 9 */ 131 | 0x37, /* : */ 132 | 0x2E, /* ; */ 133 | 0x31, /* < */ 134 | 0x27, /* = */ 135 | 0x31, /* > */ 136 | 0x2E, /* ? */ 137 | 0x14, /* @ */ 138 | 0x04, /* A */ 139 | 0x05, /* B */ 140 | 0x06, /* C */ 141 | 0x07, /* D */ 142 | 0x08, /* E */ 143 | 0x09, /* F */ 144 | 0x0A, /* G */ 145 | 0x0B, /* H */ 146 | 0x0C, /* I */ 147 | 0x0D, /* J */ 148 | 0x0E, /* K */ 149 | 0x0F, /* L */ 150 | 0x10, /* M */ 151 | 0x11, /* N */ 152 | 0x12, /* O */ 153 | 0x13, /* P */ 154 | 0x14, /* Q */ 155 | 0x15, /* R */ 156 | 0x16, /* S */ 157 | 0x17, /* T */ 158 | 0x18, /* U */ 159 | 0x19, /* V */ 160 | 0x1A, /* W */ 161 | 0x1B, /* X */ 162 | 0x1D, /* Y */ 163 | 0x1c, /* Z */ 164 | 0x25, /* [ */ 165 | 0x2E, /* \ */ 166 | 0x26, /* ] */ 167 | 0x35, /* ^ */ 168 | 0x38, /* _ */ 169 | 0x2E, /* ` */ 170 | 0x04, /* a */ 171 | 0x05, /* b */ 172 | 0x06, /* c */ 173 | 0x07, /* d */ 174 | 0x08, /* e */ 175 | 0x09, /* f */ 176 | 0x0A, /* g */ 177 | 0x0B, /* h */ 178 | 0x0C, /* i */ 179 | 0x0D, /* j */ 180 | 0x0E, /* k */ 181 | 0x0F, /* l */ 182 | 0x10, /* m */ 183 | 0x11, /* n */ 184 | 0x12, /* o */ 185 | 0x13, /* p */ 186 | 0x14, /* q */ 187 | 0x15, /* r */ 188 | 0x16, /* s */ 189 | 0x17, /* t */ 190 | 0x18, /* u */ 191 | 0x19, /* v */ 192 | 0x1A, /* w */ 193 | 0x1B, /* x */ 194 | 0x1D, /* y */ 195 | 0x1C, /* z */ 196 | 0x24, /* } */ 197 | 0x31, /* | */ 198 | 0x27, /* } */ 199 | 0x30 /* ~ */ 200 | }; 201 | 202 | 203 | PROGMEM const uint8_t MODIFIER_SHIFT_LAYOUT_US[12] = { 204 | B01111111, /* Space ! " # $ % & ' */ 205 | B11110000, /* ( ) * + , - . / */ 206 | B00000000, /* 0 1 2 3 4 5 6 7 */ 207 | B00101010, /* 8 9 : ; < = > ? */ 208 | B11111111, /* @ A B C D E F G */ 209 | B11111111, /* H I J K L M N O */ 210 | B11111111, /* P Q R S T U V W */ 211 | B11100011, /* X Y Z [ \ ] ^ _ */ 212 | B00000000, /* ` a b c d e f g */ 213 | B00000000, /* h i j k l m n o */ 214 | B00000000, /* p q r s t u v w */ 215 | B00011110, /* x y z { | } ~ Lock */ 216 | }; 217 | 218 | 219 | PROGMEM const uint8_t MODIFIER_SHIFT_LAYOUT_DE[12] = { 220 | B01111111, /* Space ! " # $ % & ' */ 221 | B11100001, /* ( ) * + , - . / */ 222 | B00000000, /* 0 1 2 3 4 5 6 7 */ 223 | B00110111, /* 8 9 : ; < = > ? */ 224 | B01111111, /* @ A B C D E F G */ 225 | B11111111, /* H I J K L M N O */ 226 | B11111111, /* P Q R S T U V W */ 227 | B11100001, /* X Y Z [ \ ] ^ _ */ 228 | B10000000, /* ` a b c d e f g */ 229 | B00000000, /* h i j k l m n o */ 230 | B00000000, /* p q r s t u v w */ 231 | B00000000, /* x y z { | } ~ Lock */ 232 | }; 233 | /* The last bit, described as "Lock" above, tells about the function of the Caps * 234 | * Lock key for your OS and layout. 0 means Caps Lock (only letters toggled), 1 * 235 | * means Shift Lock (all keys toggled). */ 236 | 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /USB-ID-FAQ.txt: -------------------------------------------------------------------------------- 1 | Version 2009-08-22 2 | 3 | ========================== 4 | WHY DO WE NEED THESE IDs? 5 | ========================== 6 | 7 | USB is more than a low level protocol for data transport. It also defines a 8 | common set of requests which must be understood by all devices. And as part 9 | of these common requests, the specification defines data structures, the 10 | USB Descriptors, which are used to describe the properties of the device. 11 | 12 | From the perspective of an operating system, it is therefore possible to find 13 | out basic properties of a device (such as e.g. the manufacturer and the name 14 | of the device) without a device-specific driver. This is essential because 15 | the operating system can choose a driver to load based on this information 16 | (Plug-And-Play). 17 | 18 | Among the most important properties in the Device Descriptor are the USB 19 | Vendor- and Product-ID. Both are 16 bit integers. The most simple form of 20 | driver matching is based on these IDs. The driver announces the Vendor- and 21 | Product-IDs of the devices it can handle and the operating system loads the 22 | appropriate driver when the device is connected. 23 | 24 | It is obvious that this technique only works if the pair Vendor- plus 25 | Product-ID is unique: Only devices which require the same driver can have the 26 | same pair of IDs. 27 | 28 | 29 | ===================================================== 30 | HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? 31 | ===================================================== 32 | 33 | Since it is so important that USB IDs are unique, the USB Implementers Forum, 34 | Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by 35 | law to build a device and assign it any random numbers as IDs. Usb.org 36 | therefore needs an agreement to regulate the use of USB IDs. The agreement 37 | binds only parties who agreed to it, of course. Everybody else is free to use 38 | any numbers for their IDs. 39 | 40 | So how can usb.org ensure that every manufacturer of USB devices enters into 41 | an agreement with them? They do it via trademark licensing. Usb.org has 42 | registered the trademark "USB", all associated logos and related terms. If 43 | you want to put an USB logo on your product or claim that it is USB 44 | compliant, you must license these trademarks from usb.org. And this is where 45 | you enter into an agreement. See the "USB-IF Trademark License Agreement and 46 | Usage Guidelines for the USB-IF Logo" at 47 | http://www.usb.org/developers/logo_license/. 48 | 49 | Licensing the USB trademarks requires that you buy a USB Vendor-ID from 50 | usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org 51 | (yearly fee of ca. 4,000 USD) and that you meet all the technical 52 | specifications from the USB spec. 53 | 54 | This means that most hobbyists and small companies will never be able to 55 | become USB compliant, just because membership is so expensive. And you can't 56 | be compliant with a driver based on V-USB anyway, because the AVR's port pins 57 | don't meet the electrical specifications for USB. So, in principle, all 58 | hobbyists and small companies are free to choose any random numbers for their 59 | IDs. They have nothing to lose... 60 | 61 | There is one exception worth noting, though: If you use a sub-component which 62 | implements USB, the vendor of the sub-components may guarantee USB 63 | compliance. This might apply to some or all of FTDI's solutions. 64 | 65 | 66 | ======================================================================= 67 | WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? 68 | ======================================================================= 69 | 70 | You have learned in the previous section that you are free to choose any 71 | numbers for your IDs anyway. So why not do exactly this? There is still the 72 | technical issue. If you choose IDs which are already in use by somebody else, 73 | operating systems will load the wrong drivers and your device won't work. 74 | Even if you choose IDs which are not currently in use, they may be in use in 75 | the next version of the operating system or even after an automatic update. 76 | 77 | So what you need is a pair of Vendor- and Product-IDs for which you have the 78 | guarantee that no USB compliant product uses them. This implies that no 79 | operating system will ever ship with drivers responsible for these IDs. 80 | 81 | 82 | ============================================== 83 | HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? 84 | ============================================== 85 | 86 | Objective Development gives away pairs of USB-IDs with their V-USB licenses. 87 | In order to ensure that these IDs are unique, Objective Development has an 88 | agreement with the company/person who has bought the USB Vendor-ID from 89 | usb.org. This agreement ensures that a range of USB Product-IDs is reserved 90 | for assignment by Objective Development and that the owner of the Vendor-ID 91 | won't give it to anybody else. 92 | 93 | This means that you have to trust three parties to ensure uniqueness of 94 | your IDs: 95 | 96 | - Objective Development, that they don't give the same PID to more than 97 | one person. 98 | - The owner of the Vendor-ID that they don't assign PIDs from the range 99 | assigned to Objective Development to anybody else. 100 | - Usb.org that they don't assign the same Vendor-ID a second time. 101 | 102 | 103 | ================================== 104 | WHO IS THE OWNER OF THE VENDOR-ID? 105 | ================================== 106 | 107 | Objective Development has obtained ranges of USB Product-IDs under two 108 | Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen 109 | Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason 110 | Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their 111 | Vendor-ID directly from usb.org. 112 | 113 | 114 | ========================================================================= 115 | CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? 116 | ========================================================================= 117 | 118 | The short answer is: Yes. All you get is a guarantee that the IDs are never 119 | assigned to anybody else. What more do you need? 120 | 121 | 122 | ============================ 123 | WHAT ABOUT SHARED ID PAIRS? 124 | ============================ 125 | 126 | Objective Development has reserved some PID/VID pairs for shared use. You 127 | have no guarantee of uniqueness for them, except that no USB compliant device 128 | uses them. In order to avoid technical problems, we must ensure that all 129 | devices with the same pair of IDs use the same driver on kernel level. For 130 | details, see the file USB-IDs-for-free.txt. 131 | 132 | 133 | ====================================================== 134 | I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? 135 | ====================================================== 136 | 137 | A 16 bit integer number cannot be protected by copyright laws. It is not 138 | sufficiently complex. And since none of the parties involved entered into the 139 | USB-IF Trademark License Agreement, we are not bound by this agreement. So 140 | there is no reason why it should be illegal to sub-license USB-IDs. 141 | 142 | 143 | ============================================= 144 | WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? 145 | ============================================= 146 | 147 | Objective Development disclaims all liabilities which might arise from the 148 | assignment of IDs. If you guarantee product features to your customers 149 | without proper disclaimer, YOU are liable for that. 150 | -------------------------------------------------------------------------------- /USB-IDs-for-free.txt: -------------------------------------------------------------------------------- 1 | Version 2009-08-22 2 | 3 | =========================== 4 | FREE USB-IDs FOR SHARED USE 5 | =========================== 6 | 7 | Objective Development has reserved a set of USB Product-IDs for use according 8 | to the guidelines outlined below. For more information about the concept of 9 | USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees 10 | that the IDs listed below are not used by any USB compliant devices. 11 | 12 | 13 | ==================== 14 | MECHANISM OF SHARING 15 | ==================== 16 | 17 | From a technical point of view, two different devices can share the same USB 18 | Vendor- and Product-ID if they require the same driver on operating system 19 | level. We make use of this fact by assigning separate IDs for various device 20 | classes. On application layer, devices must be distinguished by their textual 21 | name or serial number. We offer separate sets of IDs for discrimination by 22 | textual name and for serial number. 23 | 24 | Examples for shared use of USB IDs are included with V-USB in the "examples" 25 | subdirectory. 26 | 27 | 28 | ====================================== 29 | IDs FOR DISCRIMINATION BY TEXTUAL NAME 30 | ====================================== 31 | 32 | If you use one of the IDs listed below, your device and host-side software 33 | must conform to these rules: 34 | 35 | (1) The USB device MUST provide a textual representation of the manufacturer 36 | and product identification. The manufacturer identification MUST be available 37 | at least in USB language 0x0409 (English/US). 38 | 39 | (2) The textual manufacturer identification MUST contain either an Internet 40 | domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail 41 | address under your control (e.g. "myname@gmx.net"). You can embed the domain 42 | name or e-mail address in any string you like, e.g. "Objective Development 43 | http://www.obdev.at/vusb/". 44 | 45 | (3) You are responsible for retaining ownership of the domain or e-mail 46 | address for as long as any of your products are in use. 47 | 48 | (4) You may choose any string for the textual product identification, as long 49 | as this string is unique within the scope of your textual manufacturer 50 | identification. 51 | 52 | (5) Application side device look-up MUST be based on the textual manufacturer 53 | and product identification in addition to VID/PID matching. The driver 54 | matching MUST be a comparison of the entire strings, NOT a sub-string match. 55 | 56 | (6) For devices which implement a particular USB device class (e.g. HID), the 57 | operating system's default class driver MUST be used. If an operating system 58 | driver for Vendor Class devices is needed, this driver must be libusb or 59 | libusb-win32 (see http://libusb.org/ and 60 | http://libusb-win32.sourceforge.net/). 61 | 62 | Table if IDs for discrimination by textual name: 63 | 64 | PID dec (hex) | VID dec (hex) | Description of use 65 | ==============+===============+============================================ 66 | 1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb 67 | --------------+---------------+-------------------------------------------- 68 | 1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are 69 | | | NOT mice, keyboards or joysticks) 70 | --------------+---------------+-------------------------------------------- 71 | 1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) 72 | --------------+---------------+-------------------------------------------- 73 | 1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices 74 | --------------+---------------+-------------------------------------------- 75 | 76 | Note that Windows caches the textual product- and vendor-description for 77 | mice, keyboards and joysticks. Name-bsed discrimination is therefore not 78 | recommended for these device classes. 79 | 80 | 81 | ======================================= 82 | IDs FOR DISCRIMINATION BY SERIAL NUMBER 83 | ======================================= 84 | 85 | If you use one of the IDs listed below, your device and host-side software 86 | must conform to these rules: 87 | 88 | (1) The USB device MUST provide a textual representation of the serial 89 | number, unless ONLY the operating system's default class driver is used. 90 | The serial number string MUST be available at least in USB language 0x0409 91 | (English/US). 92 | 93 | (2) The serial number MUST start with either an Internet domain name (e.g. 94 | "mycompany.com") registered and owned by you, or an e-mail address under your 95 | control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. 96 | You MAY append any string you like for further discrimination of your devices. 97 | 98 | (3) You are responsible for retaining ownership of the domain or e-mail 99 | address for as long as any of your products are in use. 100 | 101 | (5) Application side device look-up MUST be based on the serial number string 102 | in addition to VID/PID matching. The matching must start at the first 103 | character of the serial number string and include the colon character 104 | terminating your domain or e-mail address. It MAY stop anywhere after that. 105 | 106 | (6) For devices which implement a particular USB device class (e.g. HID), the 107 | operating system's default class driver MUST be used. If an operating system 108 | driver for Vendor Class devices is needed, this driver must be libusb or 109 | libusb-win32 (see http://libusb.org/ and 110 | http://libusb-win32.sourceforge.net/). 111 | 112 | (7) If ONLY the operating system's default class driver is used, e.g. for 113 | mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an 114 | application is needed, the serial number may be omitted. 115 | 116 | 117 | Table if IDs for discrimination by serial number string: 118 | 119 | PID dec (hex) | VID dec (hex) | Description of use 120 | ===============+===============+=========================================== 121 | 10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb 122 | ---------------+---------------+------------------------------------------- 123 | 10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are 124 | | | NOT mice, keyboards or joysticks) 125 | ---------------+---------------+------------------------------------------- 126 | 10202 (0x27da) | 5824 (0x16c0) | For USB Mice 127 | ---------------+---------------+------------------------------------------- 128 | 10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards 129 | ---------------+---------------+------------------------------------------- 130 | 10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks 131 | ---------------+---------------+------------------------------------------- 132 | 10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) 133 | ---------------+---------------+------------------------------------------- 134 | 10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices 135 | ---------------+---------------+------------------------------------------- 136 | 137 | 138 | ================= 139 | ORIGIN OF USB-IDs 140 | ================= 141 | 142 | OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed 143 | here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. 144 | Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. 145 | (see www.usb.org). The VID is registered for the company name "Van Ooijen 146 | Technische Informatica". 147 | 148 | 149 | ========== 150 | DISCLAIMER 151 | ========== 152 | 153 | OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any 154 | problems which are caused by the shared use of these VID/PID pairs. 155 | -------------------------------------------------------------------------------- /USBKeyboard.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * 3 | * This is the C++ part of the USBKeyboard library. 4 | * See USBKeyboard.h and the example files for a full documentation. 5 | * 6 | *********************************************************************/ 7 | 8 | #include 9 | 10 | 11 | /* set USB HID report descriptor for boot protocol keyboard */ 12 | PROGMEM const uint8_t usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { 13 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 14 | 0x09, 0x06, /* USAGE (Keyboard) */ 15 | 0xa1, 0x01, /* COLLECTION (Application) */ 16 | 0x75, 0x01, /* REPORT_SIZE (1) */ 17 | 0x95, 0x08, /* REPORT_COUNT (8) */ 18 | 0x05, 0x07, /* USAGE_PAGE (Keyboard)(Key Codes) */ 19 | 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl)(224) */ 20 | 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI)(231) */ 21 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 22 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 23 | 0x81, 0x02, /* INPUT (Data,Var,Abs) ; Modifier byte */ 24 | 0x95, 0x01, /* REPORT_COUNT (1) */ 25 | 0x75, 0x08, /* REPORT_SIZE (8) */ 26 | 0x81, 0x03, /* INPUT (Cnst,Var,Abs) ; Reserved byte */ 27 | 0x95, 0x05, /* REPORT_COUNT (5) */ 28 | 0x75, 0x01, /* REPORT_SIZE (1) */ 29 | 0x05, 0x08, /* USAGE_PAGE (LEDs) */ 30 | 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ 31 | 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ 32 | 0x91, 0x02, /* OUTPUT (Data,Var,Abs) ; LED report */ 33 | 0x95, 0x01, /* REPORT_COUNT (1) */ 34 | 0x75, 0x03, /* REPORT_SIZE (3) */ 35 | 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) ; LED report padding */ 36 | 0x95, 0x06, /* REPORT_COUNT (6) */ 37 | 0x75, 0x08, /* REPORT_SIZE (8) */ 38 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 39 | 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ 40 | 0x05, 0x07, /* USAGE_PAGE (Keyboard)(Key Codes) */ 41 | 0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated))(0) */ 42 | 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application)(101) */ 43 | 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ 44 | 0xc0 /* END_COLLECTION */ 45 | }; 46 | 47 | 48 | 49 | /*##################################### PUBLIC FUNCTIONS #####################################*/ 50 | 51 | void USBKeyboard::begin(uint8_t layout) { 52 | cli(); 53 | USBOUT &= ~USBMASK; 54 | USBDDR &= ~USBMASK; 55 | usbDeviceDisconnect(); 56 | delayMicroseconds(250000); 57 | usbDeviceConnect(); 58 | usbInit(); 59 | sei(); 60 | keyboard_layout = layout; 61 | } 62 | 63 | /* make usbPoll() accessable from the outside */ 64 | void USBKeyboard::update() { 65 | usbPoll(); 66 | } 67 | 68 | 69 | /* Type a single char to the USB host */ 70 | size_t USBKeyboard::write(uint8_t ascii) { 71 | sendKey(asciiToKeycode(ascii), asciiToShiftState(ascii)); 72 | return 1; /* write successfull */ 73 | } 74 | 75 | 76 | /* send a single key */ 77 | void USBKeyboard::sendKey(uint8_t keycode, uint8_t modifiers) { 78 | /* make sure Enter/Return is send properly */ 79 | if (keycode == 0x28) 80 | USBKeyboard::sendReport(0, 0x2C, 0x2A, 0x28, 0, 0, 0); 81 | else 82 | USBKeyboard::sendReport(modifiers, keycode, 0, 0, 0, 0, 0); 83 | /* release all keys */ 84 | USBKeyboard::sendReport(0, 0, 0, 0, 0, 0, 0); 85 | } 86 | 87 | 88 | /* send up to 6 keys */ 89 | void USBKeyboard::sendKeys(uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5, uint8_t keycode6, uint8_t modifiers) { 90 | /* press keys */ 91 | USBKeyboard::sendReport(modifiers, keycode1, keycode2, keycode3, keycode4, keycode5, keycode6); 92 | /* release all keys */ 93 | USBKeyboard::sendReport(0, 0, 0, 0, 0, 0, 0); 94 | } 95 | 96 | 97 | /* translate ASCII char to keycode */ 98 | uint8_t USBKeyboard::asciiToKeycode(char ascii) { 99 | /* check if input is valid ASCII char and translate it to keycode */ 100 | if (ascii >= ' ' && ascii <= '~') 101 | //return keycodes[ascii - 32][keyboard_layout]; 102 | return concat(KEYCODES_, LAYOUT)[ascii - 32]; 103 | 104 | /* translate \t and \n */ 105 | if (ascii == '\t') return 0x2B; 106 | if (ascii == '\n') return 0x28; 107 | 108 | /* char unknown */ 109 | return 0; 110 | } 111 | 112 | 113 | /* translate ASCII char to shift state */ 114 | uint8_t USBKeyboard::asciiToShiftState(char ascii) { 115 | /* check if input is valid ASCII char and translate it to keycode */ 116 | if (ascii >= ' ' && ascii <= '~') { 117 | if ((concat(MODIFIER_SHIFT_, LAYOUT)[11] & 1) || (ascii >= 'a' && ascii <= 'z') || (ascii >= 'A' && ascii <= 'Z')) { 118 | /* set shift depending on the Caps Lock state and input char */ 119 | return (LED_states ^ ((concat(MODIFIER_SHIFT_, LAYOUT)[ascii/8 - 4] >> (7 - ((ascii - 32) % 8))) << 1)) & 2; 120 | } 121 | else { 122 | /* set shift depending on the input char */ 123 | return (concat(MODIFIER_SHIFT_, LAYOUT)[ascii/8 - 4] >> (7 - ((ascii - 32) % 8))) << 1 & 2; 124 | } 125 | } 126 | 127 | /* char unknown or \t or \n, they need no Shift compensation for Caps Lock */ 128 | return 0; 129 | } 130 | 131 | 132 | bool USBKeyboard::isCapsLockActivated() { 133 | return (LED_states & 2); 134 | } 135 | 136 | 137 | uint8_t USBKeyboard::getCapsLockToggleCount() { 138 | return toggle_counter; 139 | } 140 | 141 | 142 | void USBKeyboard::resetCapsLockToggleCount() { 143 | toggle_counter = 0; 144 | } 145 | 146 | 147 | /*#################################### PRIVATE FUNCTIONS #####################################*/ 148 | 149 | /* send the keyoard report */ 150 | void USBKeyboard::sendReport(uint8_t modifiers, uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5, uint8_t keycode6) { 151 | /* perform usb background tasks until the report can be sent, then send it */ 152 | while (!usbInterruptIsReady()) usbPoll(); 153 | uint8_t report_buffer[8] = {modifiers, 0, keycode1, keycode2, keycode3, keycode4, keycode5, keycode6}; 154 | usbSetInterrupt(report_buffer, sizeof(report_buffer)); /* send */ 155 | /* see http://vusb.wikidot.com/driver-api */ 156 | } 157 | 158 | 159 | /*##################################### V-USB FUNCTIONS ######################################*/ 160 | 161 | /* declare the USB device as HID keyboard */ 162 | usbMsgLen_t usbFunctionSetup(uint8_t data[8]) { 163 | /* see HID1_11.pdf sect 7.2 and http://vusb.wikidot.com/driver-api */ 164 | usbRequest_t *rq = (void *)data; 165 | 166 | if ((rq->bmRequestType & USBRQ_TYPE_MASK) != USBRQ_TYPE_CLASS) 167 | return 0; /* ignore request if it's not a class specific request */ 168 | 169 | /* see HID1_11.pdf sect 7.2 */ 170 | switch (rq->bRequest) { 171 | case USBRQ_HID_GET_IDLE: 172 | usbMsgPtr = &idle_rate; /* send data starting from this byte */ 173 | return 1; /* send 1 byte */ 174 | case USBRQ_HID_SET_IDLE: 175 | idle_rate = rq->wValue.bytes[1]; /* read in idle rate */ 176 | return 0; /* send nothing */ 177 | case USBRQ_HID_GET_PROTOCOL: 178 | usbMsgPtr = &protocol_version; /* send data starting from this byte */ 179 | return 1; /* send 1 byte */ 180 | case USBRQ_HID_SET_PROTOCOL: 181 | protocol_version = rq->wValue.bytes[1]; 182 | return 0; /* send nothing */ 183 | case USBRQ_HID_SET_REPORT: 184 | if (rq->wLength.word == 1) { /* check data is available 185 | 1 byte, we don't check report type (it can only be output or feature) 186 | we never implemented "feature" reports so it can't be feature 187 | so assume "output" reports 188 | this means set LED status 189 | since it's the only one in the descriptor */ 190 | return USB_NO_MSG; /* send nothing but call usbFunctionWrite */ 191 | } 192 | default: /* do not understand data, ignore */ 193 | return 0; /* send nothing */ 194 | } 195 | } 196 | 197 | 198 | /* detect LED states (Caps Lock, Num Lock, Scroll Lcok) */ 199 | usbMsgLen_t usbFunctionWrite(uint8_t* data, uchar len) { 200 | if (data[0] == LED_states) /* return, if no LED states have changed */ 201 | return 1; 202 | if (data[0] ^ LED_states & 2) /* increase counter, if Caps Lock state changed */ 203 | toggle_counter++; 204 | LED_states = data[0]; 205 | return 1; /* Data read, not expecting more */ 206 | } 207 | -------------------------------------------------------------------------------- /asmcommon.inc: -------------------------------------------------------------------------------- 1 | /* Name: asmcommon.inc 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2007-11-05 5 | * Tabsize: 4 6 | * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | * Revision: $Id$ 9 | */ 10 | 11 | /* Do not link this file! Link usbdrvasm.S instead, which includes the 12 | * appropriate implementation! 13 | */ 14 | 15 | /* 16 | General Description: 17 | This file contains assembler code which is shared among the USB driver 18 | implementations for different CPU cocks. Since the code must be inserted 19 | in the middle of the module, it's split out into this file and #included. 20 | 21 | Jump destinations called from outside: 22 | sofError: Called when no start sequence was found. 23 | se0: Called when a package has been successfully received. 24 | overflow: Called when receive buffer overflows. 25 | doReturn: Called after sending data. 26 | 27 | Outside jump destinations used by this module: 28 | waitForJ: Called to receive an already arriving packet. 29 | sendAckAndReti: 30 | sendNakAndReti: 31 | sendCntAndReti: 32 | usbSendAndReti: 33 | 34 | The following macros must be defined before this file is included: 35 | .macro POP_STANDARD 36 | .endm 37 | .macro POP_RETI 38 | .endm 39 | */ 40 | 41 | #define token x1 42 | 43 | overflow: 44 | ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte 30 | ; Numbers in brackets are clocks counted from center of last sync bit 31 | ; when instruction starts 32 | 33 | USB_INTR_VECTOR: 34 | ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt 35 | push YL ;[-25] push only what is necessary to sync with edge ASAP 36 | in YL, SREG ;[-23] 37 | push YL ;[-22] 38 | push YH ;[-20] 39 | ;---------------------------------------------------------------------------- 40 | ; Synchronize with sync pattern: 41 | ;---------------------------------------------------------------------------- 42 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 43 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 44 | ;The first part waits at most 1 bit long since we must be in sync pattern. 45 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 46 | ;waitForJ, ensure that this prerequisite is met. 47 | waitForJ: 48 | inc YL 49 | sbis USBIN, USBMINUS 50 | brne waitForJ ; just make sure we have ANY timeout 51 | waitForK: 52 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 53 | sbis USBIN, USBMINUS ;[-15] 54 | rjmp foundK ;[-14] 55 | sbis USBIN, USBMINUS 56 | rjmp foundK 57 | sbis USBIN, USBMINUS 58 | rjmp foundK 59 | sbis USBIN, USBMINUS 60 | rjmp foundK 61 | sbis USBIN, USBMINUS 62 | rjmp foundK 63 | sbis USBIN, USBMINUS 64 | rjmp foundK 65 | #if USB_COUNT_SOF 66 | lds YL, usbSofCount 67 | inc YL 68 | sts usbSofCount, YL 69 | #endif /* USB_COUNT_SOF */ 70 | #ifdef USB_SOF_HOOK 71 | USB_SOF_HOOK 72 | #endif 73 | rjmp sofError 74 | foundK: ;[-12] 75 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] 76 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 77 | ;are cycles from center of first sync (double K) bit after the instruction 78 | push bitcnt ;[-12] 79 | ; [---] ;[-11] 80 | lds YL, usbInputBufOffset;[-10] 81 | ; [---] ;[-9] 82 | clr YH ;[-8] 83 | subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] 84 | sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] 85 | push shift ;[-5] 86 | ; [---] ;[-4] 87 | ldi bitcnt, 0x55 ;[-3] [rx loop init] 88 | sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) 89 | rjmp haveTwoBitsK ;[-1] 90 | pop shift ;[0] undo the push from before 91 | pop bitcnt ;[2] undo the push from before 92 | rjmp waitForK ;[4] this was not the end of sync, retry 93 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 94 | ; bit times (= 21 cycles). 95 | 96 | ;---------------------------------------------------------------------------- 97 | ; push more registers and initialize values while we sample the first bits: 98 | ;---------------------------------------------------------------------------- 99 | haveTwoBitsK: 100 | push x1 ;[1] 101 | push x2 ;[3] 102 | push x3 ;[5] 103 | ldi shift, 0 ;[7] 104 | ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that 105 | push x4 ;[9] == leap 106 | 107 | in x1, USBIN ;[11] <-- sample bit 0 108 | andi x1, USBMASK ;[12] 109 | bst x1, USBMINUS ;[13] 110 | bld shift, 7 ;[14] 111 | push cnt ;[15] 112 | ldi leap, 0 ;[17] [rx loop init] 113 | ldi cnt, USB_BUFSIZE;[18] [rx loop init] 114 | rjmp rxbit1 ;[19] arrives at [21] 115 | 116 | ;---------------------------------------------------------------------------- 117 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 118 | ;---------------------------------------------------------------------------- 119 | 120 | ; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" 121 | ; accordingly to approximate this value in the long run. 122 | 123 | unstuff6: 124 | andi x2, USBMASK ;[03] 125 | ori x3, 1<<6 ;[04] will not be shifted any more 126 | andi shift, ~0x80;[05] 127 | mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 128 | subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 129 | rjmp didUnstuff6 ;[08] 130 | 131 | unstuff7: 132 | ori x3, 1<<7 ;[09] will not be shifted any more 133 | in x2, USBIN ;[00] [10] re-sample bit 7 134 | andi x2, USBMASK ;[01] 135 | andi shift, ~0x80;[02] 136 | subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 137 | rjmp didUnstuff7 ;[04] 138 | 139 | unstuffEven: 140 | ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 141 | in x1, USBIN ;[00] [10] 142 | andi shift, ~0x80;[01] 143 | andi x1, USBMASK ;[02] 144 | breq se0 ;[03] 145 | subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 146 | nop2 ;[05] 147 | rjmp didUnstuffE ;[06] 148 | 149 | unstuffOdd: 150 | ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 151 | in x2, USBIN ;[00] [10] 152 | andi shift, ~0x80;[01] 153 | andi x2, USBMASK ;[02] 154 | breq se0 ;[03] 155 | subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 156 | nop2 ;[05] 157 | rjmp didUnstuffO ;[06] 158 | 159 | rxByteLoop: 160 | andi x1, USBMASK ;[03] 161 | eor x2, x1 ;[04] 162 | subi leap, 1 ;[05] 163 | brpl skipLeap ;[06] 164 | subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte 165 | nop ;1 166 | skipLeap: 167 | subi x2, 1 ;[08] 168 | ror shift ;[09] 169 | didUnstuff6: 170 | cpi shift, 0xfc ;[10] 171 | in x2, USBIN ;[00] [11] <-- sample bit 7 172 | brcc unstuff6 ;[01] 173 | andi x2, USBMASK ;[02] 174 | eor x1, x2 ;[03] 175 | subi x1, 1 ;[04] 176 | ror shift ;[05] 177 | didUnstuff7: 178 | cpi shift, 0xfc ;[06] 179 | brcc unstuff7 ;[07] 180 | eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others 181 | st y+, x3 ;[09] store data 182 | rxBitLoop: 183 | in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 184 | andi x1, USBMASK ;[01] 185 | eor x2, x1 ;[02] 186 | andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 187 | subi x2, 1 ;[04] 188 | ror shift ;[05] 189 | cpi shift, 0xfc ;[06] 190 | brcc unstuffEven ;[07] 191 | didUnstuffE: 192 | lsr x3 ;[08] 193 | lsr x3 ;[09] 194 | rxbit1: 195 | in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 196 | andi x2, USBMASK ;[01] 197 | breq se0 ;[02] 198 | eor x1, x2 ;[03] 199 | subi x1, 1 ;[04] 200 | ror shift ;[05] 201 | cpi shift, 0xfc ;[06] 202 | brcc unstuffOdd ;[07] 203 | didUnstuffO: 204 | subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 205 | brcs rxBitLoop ;[09] 206 | 207 | subi cnt, 1 ;[10] 208 | in x1, USBIN ;[00] [11] <-- sample bit 6 209 | brcc rxByteLoop ;[01] 210 | rjmp overflow 211 | 212 | macro POP_STANDARD ; 14 cycles 213 | pop cnt 214 | pop x4 215 | pop x3 216 | pop x2 217 | pop x1 218 | pop shift 219 | pop bitcnt 220 | endm 221 | macro POP_RETI ; 7 cycles 222 | pop YH 223 | pop YL 224 | out SREG, YL 225 | pop YL 226 | endm 227 | 228 | #include "asmcommon.inc" 229 | 230 | ; USB spec says: 231 | ; idle = J 232 | ; J = (D+ = 0), (D- = 1) 233 | ; K = (D+ = 1), (D- = 0) 234 | ; Spec allows 7.5 bit times from EOP to SOP for replies 235 | 236 | bitstuffN: 237 | eor x1, x4 ;[5] 238 | ldi x2, 0 ;[6] 239 | nop2 ;[7] 240 | nop ;[9] 241 | out USBOUT, x1 ;[10] <-- out 242 | rjmp didStuffN ;[0] 243 | 244 | bitstuff6: 245 | eor x1, x4 ;[5] 246 | ldi x2, 0 ;[6] Carry is zero due to brcc 247 | rol shift ;[7] compensate for ror shift at branch destination 248 | rjmp didStuff6 ;[8] 249 | 250 | bitstuff7: 251 | ldi x2, 0 ;[2] Carry is zero due to brcc 252 | rjmp didStuff7 ;[3] 253 | 254 | 255 | sendNakAndReti: 256 | ldi x3, USBPID_NAK ;[-18] 257 | rjmp sendX3AndReti ;[-17] 258 | sendAckAndReti: 259 | ldi cnt, USBPID_ACK ;[-17] 260 | sendCntAndReti: 261 | mov x3, cnt ;[-16] 262 | sendX3AndReti: 263 | ldi YL, 20 ;[-15] x3==r20 address is 20 264 | ldi YH, 0 ;[-14] 265 | ldi cnt, 2 ;[-13] 266 | ; rjmp usbSendAndReti fallthrough 267 | 268 | ;usbSend: 269 | ;pointer to data in 'Y' 270 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 271 | ;uses: x1...x4, btcnt, shift, cnt, Y 272 | ;Numbers in brackets are time since first bit of sync pattern is sent 273 | ;We don't match the transfer rate exactly (don't insert leap cycles every third 274 | ;byte) because the spec demands only 1.5% precision anyway. 275 | usbSendAndReti: ; 12 cycles until SOP 276 | in x2, USBDDR ;[-12] 277 | ori x2, USBMASK ;[-11] 278 | sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 279 | in x1, USBOUT ;[-8] port mirror for tx loop 280 | out USBDDR, x2 ;[-7] <- acquire bus 281 | ; need not init x2 (bitstuff history) because sync starts with 0 282 | ldi x4, USBMASK ;[-6] exor mask 283 | ldi shift, 0x80 ;[-5] sync byte is first byte sent 284 | txByteLoop: 285 | ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 286 | txBitLoop: 287 | sbrs shift, 0 ;[-3] [7] 288 | eor x1, x4 ;[-2] [8] 289 | out USBOUT, x1 ;[-1] [9] <-- out N 290 | ror shift ;[0] [10] 291 | ror x2 ;[1] 292 | didStuffN: 293 | cpi x2, 0xfc ;[2] 294 | brcc bitstuffN ;[3] 295 | lsr bitcnt ;[4] 296 | brcc txBitLoop ;[5] 297 | brne txBitLoop ;[6] 298 | 299 | sbrs shift, 0 ;[7] 300 | eor x1, x4 ;[8] 301 | didStuff6: 302 | out USBOUT, x1 ;[-1] [9] <-- out 6 303 | ror shift ;[0] [10] 304 | ror x2 ;[1] 305 | cpi x2, 0xfc ;[2] 306 | brcc bitstuff6 ;[3] 307 | ror shift ;[4] 308 | didStuff7: 309 | ror x2 ;[5] 310 | sbrs x2, 7 ;[6] 311 | eor x1, x4 ;[7] 312 | nop ;[8] 313 | cpi x2, 0xfc ;[9] 314 | out USBOUT, x1 ;[-1][10] <-- out 7 315 | brcc bitstuff7 ;[0] [11] 316 | ld shift, y+ ;[1] 317 | dec cnt ;[3] 318 | brne txByteLoop ;[4] 319 | ;make SE0: 320 | cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] 321 | lds x2, usbNewDeviceAddr;[6] 322 | lsl x2 ;[8] we compare with left shifted address 323 | subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 324 | sbci YH, 0 ;[10] 325 | out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle 326 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 327 | ;set address only after data packet was sent, not after handshake 328 | breq skipAddrAssign ;[0] 329 | sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer 330 | skipAddrAssign: 331 | ;end of usbDeviceAddress transfer 332 | ldi x2, 1< 13.333333 cycles per bit, 106.666667 cycles per byte 38 | ; Numbers in brackets are clocks counted from center of last sync bit 39 | ; when instruction starts 40 | ;register use in receive loop: 41 | ; shift assembles the byte currently being received 42 | ; x1 holds the D+ and D- line state 43 | ; x2 holds the previous line state 44 | ; x4 (leap) is used to add a leap cycle once every three bytes received 45 | ; X3 (leap2) is used to add a leap cycle once every three stuff bits received 46 | ; bitcnt is used to determine when a stuff bit is due 47 | ; cnt holds the number of bytes left in the receive buffer 48 | 49 | USB_INTR_VECTOR: 50 | ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt 51 | push YL ;[-28] push only what is necessary to sync with edge ASAP 52 | in YL, SREG ;[-26] 53 | push YL ;[-25] 54 | push YH ;[-23] 55 | ;---------------------------------------------------------------------------- 56 | ; Synchronize with sync pattern: 57 | ;---------------------------------------------------------------------------- 58 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 59 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 60 | ;The first part waits at most 1 bit long since we must be in sync pattern. 61 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 62 | ;waitForJ, ensure that this prerequisite is met. 63 | waitForJ: 64 | inc YL 65 | sbis USBIN, USBMINUS 66 | brne waitForJ ; just make sure we have ANY timeout 67 | waitForK: 68 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 69 | sbis USBIN, USBMINUS ;[-19] 70 | rjmp foundK ;[-18] 71 | sbis USBIN, USBMINUS 72 | rjmp foundK 73 | sbis USBIN, USBMINUS 74 | rjmp foundK 75 | sbis USBIN, USBMINUS 76 | rjmp foundK 77 | sbis USBIN, USBMINUS 78 | rjmp foundK 79 | sbis USBIN, USBMINUS 80 | rjmp foundK 81 | sbis USBIN, USBMINUS 82 | rjmp foundK 83 | sbis USBIN, USBMINUS 84 | rjmp foundK 85 | sbis USBIN, USBMINUS 86 | rjmp foundK 87 | #if USB_COUNT_SOF 88 | lds YL, usbSofCount 89 | inc YL 90 | sts usbSofCount, YL 91 | #endif /* USB_COUNT_SOF */ 92 | #ifdef USB_SOF_HOOK 93 | USB_SOF_HOOK 94 | #endif 95 | rjmp sofError 96 | foundK: ;[-16] 97 | ;{3, 5} after falling D- edge, average delay: 4 cycles 98 | ;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample 99 | ;use 1 bit time for setup purposes, then sample again. Numbers in brackets 100 | ;are cycles from center of first sync (double K) bit after the instruction 101 | push bitcnt ;[-16] 102 | ; [---] ;[-15] 103 | lds YL, usbInputBufOffset;[-14] 104 | ; [---] ;[-13] 105 | clr YH ;[-12] 106 | subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] 107 | sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] 108 | push shift ;[-9] 109 | ; [---] ;[-8] 110 | ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected 111 | nop2 ;[-6] 112 | ; [---] ;[-5] 113 | ldi bitcnt, 5 ;[-4] [rx loop init] 114 | sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) 115 | rjmp haveTwoBitsK ;[-2] 116 | pop shift ;[-1] undo the push from before 117 | pop bitcnt ;[1] 118 | rjmp waitForK ;[3] this was not the end of sync, retry 119 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 120 | ; bit times (= 27 cycles). 121 | 122 | ;---------------------------------------------------------------------------- 123 | ; push more registers and initialize values while we sample the first bits: 124 | ;---------------------------------------------------------------------------- 125 | haveTwoBitsK: 126 | push x1 ;[0] 127 | push x2 ;[2] 128 | push x3 ;[4] (leap2) 129 | ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit 130 | push x4 ;[7] == leap 131 | ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received 132 | push cnt ;[10] 133 | ldi cnt, USB_BUFSIZE ;[12] [rx loop init] 134 | ldi x2, 1< max 25 cycles interrupt disable 39 | ;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes 40 | ;Numbers in brackets are maximum cycles since SOF. 41 | USB_INTR_VECTOR: 42 | ;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt 43 | push YL ;2 [35] push only what is necessary to sync with edge ASAP 44 | in YL, SREG ;1 [37] 45 | push YL ;2 [39] 46 | ;---------------------------------------------------------------------------- 47 | ; Synchronize with sync pattern: 48 | ;---------------------------------------------------------------------------- 49 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 50 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 51 | ;The first part waits at most 1 bit long since we must be in sync pattern. 52 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 53 | ;waitForJ, ensure that this prerequisite is met. 54 | waitForJ: 55 | inc YL 56 | sbis USBIN, USBMINUS 57 | brne waitForJ ; just make sure we have ANY timeout 58 | waitForK: 59 | ;The following code results in a sampling window of 1/4 bit which meets the spec. 60 | sbis USBIN, USBMINUS 61 | rjmp foundK 62 | sbis USBIN, USBMINUS 63 | rjmp foundK 64 | sbis USBIN, USBMINUS 65 | rjmp foundK 66 | sbis USBIN, USBMINUS 67 | rjmp foundK 68 | sbis USBIN, USBMINUS 69 | rjmp foundK 70 | #if USB_COUNT_SOF 71 | lds YL, usbSofCount 72 | inc YL 73 | sts usbSofCount, YL 74 | #endif /* USB_COUNT_SOF */ 75 | #ifdef USB_SOF_HOOK 76 | USB_SOF_HOOK 77 | #endif 78 | rjmp sofError 79 | foundK: 80 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] 81 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 82 | ;are cycles from center of first sync (double K) bit after the instruction 83 | push YH ;2 [2] 84 | lds YL, usbInputBufOffset;2 [4] 85 | clr YH ;1 [5] 86 | subi YL, lo8(-(usbRxBuf));1 [6] 87 | sbci YH, hi8(-(usbRxBuf));1 [7] 88 | 89 | sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] 90 | rjmp haveTwoBitsK ;2 [10] 91 | pop YH ;2 [11] undo the push from before 92 | rjmp waitForK ;2 [13] this was not the end of sync, retry 93 | haveTwoBitsK: 94 | ;---------------------------------------------------------------------------- 95 | ; push more registers and initialize values while we sample the first bits: 96 | ;---------------------------------------------------------------------------- 97 | push shift ;2 [16] 98 | push x1 ;2 [12] 99 | push x2 ;2 [14] 100 | 101 | in x1, USBIN ;1 [17] <-- sample bit 0 102 | ldi shift, 0xff ;1 [18] 103 | bst x1, USBMINUS ;1 [19] 104 | bld shift, 0 ;1 [20] 105 | push x3 ;2 [22] 106 | push cnt ;2 [24] 107 | 108 | in x2, USBIN ;1 [25] <-- sample bit 1 109 | ser x3 ;1 [26] [inserted init instruction] 110 | eor x1, x2 ;1 [27] 111 | bst x1, USBMINUS ;1 [28] 112 | bld shift, 1 ;1 [29] 113 | ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] 114 | rjmp rxbit2 ;2 [32] 115 | 116 | ;---------------------------------------------------------------------------- 117 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 118 | ;---------------------------------------------------------------------------- 119 | 120 | unstuff0: ;1 (branch taken) 121 | andi x3, ~0x01 ;1 [15] 122 | mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit 123 | in x2, USBIN ;1 [17] <-- sample bit 1 again 124 | ori shift, 0x01 ;1 [18] 125 | rjmp didUnstuff0 ;2 [20] 126 | 127 | unstuff1: ;1 (branch taken) 128 | mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit 129 | andi x3, ~0x02 ;1 [22] 130 | ori shift, 0x02 ;1 [23] 131 | nop ;1 [24] 132 | in x1, USBIN ;1 [25] <-- sample bit 2 again 133 | rjmp didUnstuff1 ;2 [27] 134 | 135 | unstuff2: ;1 (branch taken) 136 | andi x3, ~0x04 ;1 [29] 137 | ori shift, 0x04 ;1 [30] 138 | mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit 139 | nop ;1 [32] 140 | in x2, USBIN ;1 [33] <-- sample bit 3 141 | rjmp didUnstuff2 ;2 [35] 142 | 143 | unstuff3: ;1 (branch taken) 144 | in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] 145 | andi x3, ~0x08 ;1 [35] 146 | ori shift, 0x08 ;1 [36] 147 | rjmp didUnstuff3 ;2 [38] 148 | 149 | unstuff4: ;1 (branch taken) 150 | andi x3, ~0x10 ;1 [40] 151 | in x1, USBIN ;1 [41] <-- sample stuffed bit 4 152 | ori shift, 0x10 ;1 [42] 153 | rjmp didUnstuff4 ;2 [44] 154 | 155 | unstuff5: ;1 (branch taken) 156 | andi x3, ~0x20 ;1 [48] 157 | in x2, USBIN ;1 [49] <-- sample stuffed bit 5 158 | ori shift, 0x20 ;1 [50] 159 | rjmp didUnstuff5 ;2 [52] 160 | 161 | unstuff6: ;1 (branch taken) 162 | andi x3, ~0x40 ;1 [56] 163 | in x1, USBIN ;1 [57] <-- sample stuffed bit 6 164 | ori shift, 0x40 ;1 [58] 165 | rjmp didUnstuff6 ;2 [60] 166 | 167 | ; extra jobs done during bit interval: 168 | ; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] 169 | ; bit 1: se0 check 170 | ; bit 2: overflow check 171 | ; bit 3: recovery from delay [bit 0 tasks took too long] 172 | ; bit 4: none 173 | ; bit 5: none 174 | ; bit 6: none 175 | ; bit 7: jump, eor 176 | rxLoop: 177 | eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others 178 | in x1, USBIN ;1 [1] <-- sample bit 0 179 | st y+, x3 ;2 [3] store data 180 | ser x3 ;1 [4] 181 | nop ;1 [5] 182 | eor x2, x1 ;1 [6] 183 | bst x2, USBMINUS;1 [7] 184 | bld shift, 0 ;1 [8] 185 | in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) 186 | andi x2, USBMASK ;1 [10] 187 | breq se0 ;1 [11] SE0 check for bit 1 188 | andi shift, 0xf9 ;1 [12] 189 | didUnstuff0: 190 | breq unstuff0 ;1 [13] 191 | eor x1, x2 ;1 [14] 192 | bst x1, USBMINUS;1 [15] 193 | bld shift, 1 ;1 [16] 194 | rxbit2: 195 | in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) 196 | andi shift, 0xf3 ;1 [18] 197 | breq unstuff1 ;1 [19] do remaining work for bit 1 198 | didUnstuff1: 199 | subi cnt, 1 ;1 [20] 200 | brcs overflow ;1 [21] loop control 201 | eor x2, x1 ;1 [22] 202 | bst x2, USBMINUS;1 [23] 203 | bld shift, 2 ;1 [24] 204 | in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) 205 | andi shift, 0xe7 ;1 [26] 206 | breq unstuff2 ;1 [27] 207 | didUnstuff2: 208 | eor x1, x2 ;1 [28] 209 | bst x1, USBMINUS;1 [29] 210 | bld shift, 3 ;1 [30] 211 | didUnstuff3: 212 | andi shift, 0xcf ;1 [31] 213 | breq unstuff3 ;1 [32] 214 | in x1, USBIN ;1 [33] <-- sample bit 4 215 | eor x2, x1 ;1 [34] 216 | bst x2, USBMINUS;1 [35] 217 | bld shift, 4 ;1 [36] 218 | didUnstuff4: 219 | andi shift, 0x9f ;1 [37] 220 | breq unstuff4 ;1 [38] 221 | nop2 ;2 [40] 222 | in x2, USBIN ;1 [41] <-- sample bit 5 223 | eor x1, x2 ;1 [42] 224 | bst x1, USBMINUS;1 [43] 225 | bld shift, 5 ;1 [44] 226 | didUnstuff5: 227 | andi shift, 0x3f ;1 [45] 228 | breq unstuff5 ;1 [46] 229 | nop2 ;2 [48] 230 | in x1, USBIN ;1 [49] <-- sample bit 6 231 | eor x2, x1 ;1 [50] 232 | bst x2, USBMINUS;1 [51] 233 | bld shift, 6 ;1 [52] 234 | didUnstuff6: 235 | cpi shift, 0x02 ;1 [53] 236 | brlo unstuff6 ;1 [54] 237 | nop2 ;2 [56] 238 | in x2, USBIN ;1 [57] <-- sample bit 7 239 | eor x1, x2 ;1 [58] 240 | bst x1, USBMINUS;1 [59] 241 | bld shift, 7 ;1 [60] 242 | didUnstuff7: 243 | cpi shift, 0x04 ;1 [61] 244 | brsh rxLoop ;2 [63] loop control 245 | unstuff7: 246 | andi x3, ~0x80 ;1 [63] 247 | ori shift, 0x80 ;1 [64] 248 | in x2, USBIN ;1 [65] <-- sample stuffed bit 7 249 | nop ;1 [66] 250 | rjmp didUnstuff7 ;2 [68] 251 | 252 | macro POP_STANDARD ; 12 cycles 253 | pop cnt 254 | pop x3 255 | pop x2 256 | pop x1 257 | pop shift 258 | pop YH 259 | endm 260 | macro POP_RETI ; 5 cycles 261 | pop YL 262 | out SREG, YL 263 | pop YL 264 | endm 265 | 266 | #include "asmcommon.inc" 267 | 268 | ;---------------------------------------------------------------------------- 269 | ; Transmitting data 270 | ;---------------------------------------------------------------------------- 271 | 272 | txByteLoop: 273 | txBitloop: 274 | stuffN1Delay: ; [03] 275 | ror shift ;[-5] [11] [59] 276 | brcc doExorN1 ;[-4] [60] 277 | subi x4, 1 ;[-3] 278 | brne commonN1 ;[-2] 279 | lsl shift ;[-1] compensate ror after rjmp stuffDelay 280 | nop ;[00] stuffing consists of just waiting 8 cycles 281 | rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear 282 | 283 | sendNakAndReti: ;0 [-19] 19 cycles until SOP 284 | ldi x3, USBPID_NAK ;1 [-18] 285 | rjmp usbSendX3 ;2 [-16] 286 | sendAckAndReti: ;0 [-19] 19 cycles until SOP 287 | ldi x3, USBPID_ACK ;1 [-18] 288 | rjmp usbSendX3 ;2 [-16] 289 | sendCntAndReti: ;0 [-17] 17 cycles until SOP 290 | mov x3, cnt ;1 [-16] 291 | usbSendX3: ;0 [-16] 292 | ldi YL, 20 ;1 [-15] 'x3' is R20 293 | ldi YH, 0 ;1 [-14] 294 | ldi cnt, 2 ;1 [-13] 295 | ; rjmp usbSendAndReti fallthrough 296 | 297 | ; USB spec says: 298 | ; idle = J 299 | ; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 300 | ; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 301 | ; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) 302 | 303 | ;usbSend: 304 | ;pointer to data in 'Y' 305 | ;number of bytes in 'cnt' -- including sync byte 306 | ;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] 307 | ;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) 308 | usbSendAndReti: 309 | in x2, USBDDR ;[-12] 12 cycles until SOP 310 | ori x2, USBMASK ;[-11] 311 | sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 312 | out USBDDR, x2 ;[-8] <--- acquire bus 313 | in x1, USBOUT ;[-7] port mirror for tx loop 314 | ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) 315 | ldi x2, USBMASK ;[-5] 316 | push x4 ;[-4] 317 | doExorN1: 318 | eor x1, x2 ;[-2] [06] [62] 319 | ldi x4, 6 ;[-1] [07] [63] 320 | commonN1: 321 | stuffN2Delay: 322 | out USBOUT, x1 ;[00] [08] [64] <--- set bit 323 | ror shift ;[01] 324 | brcc doExorN2 ;[02] 325 | subi x4, 1 ;[03] 326 | brne commonN2 ;[04] 327 | lsl shift ;[05] compensate ror after rjmp stuffDelay 328 | rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear 329 | doExorN2: 330 | eor x1, x2 ;[04] [12] 331 | ldi x4, 6 ;[05] [13] 332 | commonN2: 333 | nop ;[06] [14] 334 | subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 335 | out USBOUT, x1 ;[08] [16] <--- set bit 336 | brcs txBitloop ;[09] [25] [41] 337 | 338 | stuff6Delay: 339 | ror shift ;[42] [50] 340 | brcc doExor6 ;[43] 341 | subi x4, 1 ;[44] 342 | brne common6 ;[45] 343 | lsl shift ;[46] compensate ror after rjmp stuffDelay 344 | nop ;[47] stuffing consists of just waiting 8 cycles 345 | rjmp stuff6Delay ;[48] after ror, C bit is reliably clear 346 | doExor6: 347 | eor x1, x2 ;[45] [53] 348 | ldi x4, 6 ;[46] 349 | common6: 350 | stuff7Delay: 351 | ror shift ;[47] [55] 352 | out USBOUT, x1 ;[48] <--- set bit 353 | brcc doExor7 ;[49] 354 | subi x4, 1 ;[50] 355 | brne common7 ;[51] 356 | lsl shift ;[52] compensate ror after rjmp stuffDelay 357 | rjmp stuff7Delay ;[53] after ror, C bit is reliably clear 358 | doExor7: 359 | eor x1, x2 ;[51] [59] 360 | ldi x4, 6 ;[52] 361 | common7: 362 | ld shift, y+ ;[53] 363 | tst cnt ;[55] 364 | out USBOUT, x1 ;[56] <--- set bit 365 | brne txByteLoop ;[57] 366 | 367 | ;make SE0: 368 | cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] 369 | lds x2, usbNewDeviceAddr;[59] 370 | lsl x2 ;[61] we compare with left shifted address 371 | subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 372 | sbci YH, 0 ;[63] 373 | out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle 374 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 375 | ;set address only after data packet was sent, not after handshake 376 | breq skipAddrAssign ;[01] 377 | sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer 378 | skipAddrAssign: 379 | ;end of usbDeviceAddress transfer 380 | ldi x2, 1< max 52 cycles interrupt disable 32 | ;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes 33 | ;nominal frequency: 16.5 MHz -> 11 cycles per bit 34 | ; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) 35 | ; Numbers in brackets are clocks counted from center of last sync bit 36 | ; when instruction starts 37 | 38 | 39 | USB_INTR_VECTOR: 40 | ;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt 41 | push YL ;[-23] push only what is necessary to sync with edge ASAP 42 | in YL, SREG ;[-21] 43 | push YL ;[-20] 44 | ;---------------------------------------------------------------------------- 45 | ; Synchronize with sync pattern: 46 | ;---------------------------------------------------------------------------- 47 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 48 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 49 | ;The first part waits at most 1 bit long since we must be in sync pattern. 50 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 51 | ;waitForJ, ensure that this prerequisite is met. 52 | waitForJ: 53 | inc YL 54 | sbis USBIN, USBMINUS 55 | brne waitForJ ; just make sure we have ANY timeout 56 | waitForK: 57 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 58 | sbis USBIN, USBMINUS ;[-15] 59 | rjmp foundK ;[-14] 60 | sbis USBIN, USBMINUS 61 | rjmp foundK 62 | sbis USBIN, USBMINUS 63 | rjmp foundK 64 | sbis USBIN, USBMINUS 65 | rjmp foundK 66 | sbis USBIN, USBMINUS 67 | rjmp foundK 68 | sbis USBIN, USBMINUS 69 | rjmp foundK 70 | #if USB_COUNT_SOF 71 | lds YL, usbSofCount 72 | inc YL 73 | sts usbSofCount, YL 74 | #endif /* USB_COUNT_SOF */ 75 | #ifdef USB_SOF_HOOK 76 | USB_SOF_HOOK 77 | #endif 78 | rjmp sofError 79 | foundK: ;[-12] 80 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] 81 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 82 | ;are cycles from center of first sync (double K) bit after the instruction 83 | push r0 ;[-12] 84 | ; [---] ;[-11] 85 | push YH ;[-10] 86 | ; [---] ;[-9] 87 | lds YL, usbInputBufOffset;[-8] 88 | ; [---] ;[-7] 89 | clr YH ;[-6] 90 | subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] 91 | sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] 92 | mov r0, x2 ;[-3] [rx loop init] 93 | sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) 94 | rjmp haveTwoBitsK ;[-1] 95 | pop YH ;[0] undo the pushes from before 96 | pop r0 ;[2] 97 | rjmp waitForK ;[4] this was not the end of sync, retry 98 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 99 | ; bit times (= 22 cycles). 100 | 101 | ;---------------------------------------------------------------------------- 102 | ; push more registers and initialize values while we sample the first bits: 103 | ;---------------------------------------------------------------------------- 104 | haveTwoBitsK: ;[1] 105 | push shift ;[1] 106 | push x1 ;[3] 107 | push x2 ;[5] 108 | push x3 ;[7] 109 | ldi shift, 0xff ;[9] [rx loop init] 110 | ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag 111 | 112 | in x1, USBIN ;[11] <-- sample bit 0 113 | bst x1, USBMINUS ;[12] 114 | bld shift, 0 ;[13] 115 | push x4 ;[14] == phase 116 | ; [---] ;[15] 117 | push cnt ;[16] 118 | ; [---] ;[17] 119 | ldi phase, 0 ;[18] [rx loop init] 120 | ldi cnt, USB_BUFSIZE;[19] [rx loop init] 121 | rjmp rxbit1 ;[20] 122 | ; [---] ;[21] 123 | 124 | ;---------------------------------------------------------------------------- 125 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 126 | ;---------------------------------------------------------------------------- 127 | /* 128 | byte oriented operations done during loop: 129 | bit 0: store data 130 | bit 1: SE0 check 131 | bit 2: overflow check 132 | bit 3: catch up 133 | bit 4: rjmp to achieve conditional jump range 134 | bit 5: PLL 135 | bit 6: catch up 136 | bit 7: jump, fixup bitstuff 137 | ; 87 [+ 2] cycles 138 | ------------------------------------------------------------------ 139 | */ 140 | continueWithBit5: 141 | in x2, USBIN ;[055] <-- bit 5 142 | eor r0, x2 ;[056] 143 | or phase, r0 ;[057] 144 | sbrc phase, USBMINUS ;[058] 145 | lpm ;[059] optional nop3; modifies r0 146 | in phase, USBIN ;[060] <-- phase 147 | eor x1, x2 ;[061] 148 | bst x1, USBMINUS ;[062] 149 | bld shift, 5 ;[063] 150 | andi shift, 0x3f ;[064] 151 | in x1, USBIN ;[065] <-- bit 6 152 | breq unstuff5 ;[066] *** unstuff escape 153 | eor phase, x1 ;[067] 154 | eor x2, x1 ;[068] 155 | bst x2, USBMINUS ;[069] 156 | bld shift, 6 ;[070] 157 | didUnstuff6: ;[ ] 158 | in r0, USBIN ;[071] <-- phase 159 | cpi shift, 0x02 ;[072] 160 | brlo unstuff6 ;[073] *** unstuff escape 161 | didUnstuff5: ;[ ] 162 | nop2 ;[074] 163 | ; [---] ;[075] 164 | in x2, USBIN ;[076] <-- bit 7 165 | eor x1, x2 ;[077] 166 | bst x1, USBMINUS ;[078] 167 | bld shift, 7 ;[079] 168 | didUnstuff7: ;[ ] 169 | eor r0, x2 ;[080] 170 | or phase, r0 ;[081] 171 | in r0, USBIN ;[082] <-- phase 172 | cpi shift, 0x04 ;[083] 173 | brsh rxLoop ;[084] 174 | ; [---] ;[085] 175 | unstuff7: ;[ ] 176 | andi x3, ~0x80 ;[085] 177 | ori shift, 0x80 ;[086] 178 | in x2, USBIN ;[087] <-- sample stuffed bit 7 179 | nop ;[088] 180 | rjmp didUnstuff7 ;[089] 181 | ; [---] ;[090] 182 | ;[080] 183 | 184 | unstuff5: ;[067] 185 | eor phase, x1 ;[068] 186 | andi x3, ~0x20 ;[069] 187 | ori shift, 0x20 ;[070] 188 | in r0, USBIN ;[071] <-- phase 189 | mov x2, x1 ;[072] 190 | nop ;[073] 191 | nop2 ;[074] 192 | ; [---] ;[075] 193 | in x1, USBIN ;[076] <-- bit 6 194 | eor r0, x1 ;[077] 195 | or phase, r0 ;[078] 196 | eor x2, x1 ;[079] 197 | bst x2, USBMINUS ;[080] 198 | bld shift, 6 ;[081] no need to check bitstuffing, we just had one 199 | in r0, USBIN ;[082] <-- phase 200 | rjmp didUnstuff5 ;[083] 201 | ; [---] ;[084] 202 | ;[074] 203 | 204 | unstuff6: ;[074] 205 | andi x3, ~0x40 ;[075] 206 | in x1, USBIN ;[076] <-- bit 6 again 207 | ori shift, 0x40 ;[077] 208 | nop2 ;[078] 209 | ; [---] ;[079] 210 | rjmp didUnstuff6 ;[080] 211 | ; [---] ;[081] 212 | ;[071] 213 | 214 | unstuff0: ;[013] 215 | eor r0, x2 ;[014] 216 | or phase, r0 ;[015] 217 | andi x2, USBMASK ;[016] check for SE0 218 | in r0, USBIN ;[017] <-- phase 219 | breq didUnstuff0 ;[018] direct jump to se0 would be too long 220 | andi x3, ~0x01 ;[019] 221 | ori shift, 0x01 ;[020] 222 | mov x1, x2 ;[021] mov existing sample 223 | in x2, USBIN ;[022] <-- bit 1 again 224 | rjmp didUnstuff0 ;[023] 225 | ; [---] ;[024] 226 | ;[014] 227 | 228 | unstuff1: ;[024] 229 | eor r0, x1 ;[025] 230 | or phase, r0 ;[026] 231 | andi x3, ~0x02 ;[027] 232 | in r0, USBIN ;[028] <-- phase 233 | ori shift, 0x02 ;[029] 234 | mov x2, x1 ;[030] 235 | rjmp didUnstuff1 ;[031] 236 | ; [---] ;[032] 237 | ;[022] 238 | 239 | unstuff2: ;[035] 240 | eor r0, x2 ;[036] 241 | or phase, r0 ;[037] 242 | andi x3, ~0x04 ;[038] 243 | in r0, USBIN ;[039] <-- phase 244 | ori shift, 0x04 ;[040] 245 | mov x1, x2 ;[041] 246 | rjmp didUnstuff2 ;[042] 247 | ; [---] ;[043] 248 | ;[033] 249 | 250 | unstuff3: ;[043] 251 | in x2, USBIN ;[044] <-- bit 3 again 252 | eor r0, x2 ;[045] 253 | or phase, r0 ;[046] 254 | andi x3, ~0x08 ;[047] 255 | ori shift, 0x08 ;[048] 256 | nop ;[049] 257 | in r0, USBIN ;[050] <-- phase 258 | rjmp didUnstuff3 ;[051] 259 | ; [---] ;[052] 260 | ;[042] 261 | 262 | unstuff4: ;[053] 263 | andi x3, ~0x10 ;[054] 264 | in x1, USBIN ;[055] <-- bit 4 again 265 | ori shift, 0x10 ;[056] 266 | rjmp didUnstuff4 ;[057] 267 | ; [---] ;[058] 268 | ;[048] 269 | 270 | rxLoop: ;[085] 271 | eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others 272 | in x1, USBIN ;[000] <-- bit 0 273 | st y+, x3 ;[001] 274 | ; [---] ;[002] 275 | eor r0, x1 ;[003] 276 | or phase, r0 ;[004] 277 | eor x2, x1 ;[005] 278 | in r0, USBIN ;[006] <-- phase 279 | ser x3 ;[007] 280 | bst x2, USBMINUS ;[008] 281 | bld shift, 0 ;[009] 282 | andi shift, 0xf9 ;[010] 283 | rxbit1: ;[ ] 284 | in x2, USBIN ;[011] <-- bit 1 285 | breq unstuff0 ;[012] *** unstuff escape 286 | andi x2, USBMASK ;[013] SE0 check for bit 1 287 | didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff 288 | breq se0 ;[014] 289 | eor r0, x2 ;[015] 290 | or phase, r0 ;[016] 291 | in r0, USBIN ;[017] <-- phase 292 | eor x1, x2 ;[018] 293 | bst x1, USBMINUS ;[019] 294 | bld shift, 1 ;[020] 295 | andi shift, 0xf3 ;[021] 296 | didUnstuff1: ;[ ] 297 | in x1, USBIN ;[022] <-- bit 2 298 | breq unstuff1 ;[023] *** unstuff escape 299 | eor r0, x1 ;[024] 300 | or phase, r0 ;[025] 301 | subi cnt, 1 ;[026] overflow check 302 | brcs overflow ;[027] 303 | in r0, USBIN ;[028] <-- phase 304 | eor x2, x1 ;[029] 305 | bst x2, USBMINUS ;[030] 306 | bld shift, 2 ;[031] 307 | andi shift, 0xe7 ;[032] 308 | didUnstuff2: ;[ ] 309 | in x2, USBIN ;[033] <-- bit 3 310 | breq unstuff2 ;[034] *** unstuff escape 311 | eor r0, x2 ;[035] 312 | or phase, r0 ;[036] 313 | eor x1, x2 ;[037] 314 | bst x1, USBMINUS ;[038] 315 | in r0, USBIN ;[039] <-- phase 316 | bld shift, 3 ;[040] 317 | andi shift, 0xcf ;[041] 318 | didUnstuff3: ;[ ] 319 | breq unstuff3 ;[042] *** unstuff escape 320 | nop ;[043] 321 | in x1, USBIN ;[044] <-- bit 4 322 | eor x2, x1 ;[045] 323 | bst x2, USBMINUS ;[046] 324 | bld shift, 4 ;[047] 325 | didUnstuff4: ;[ ] 326 | eor r0, x1 ;[048] 327 | or phase, r0 ;[049] 328 | in r0, USBIN ;[050] <-- phase 329 | andi shift, 0x9f ;[051] 330 | breq unstuff4 ;[052] *** unstuff escape 331 | rjmp continueWithBit5;[053] 332 | ; [---] ;[054] 333 | 334 | macro POP_STANDARD ; 16 cycles 335 | pop cnt 336 | pop x4 337 | pop x3 338 | pop x2 339 | pop x1 340 | pop shift 341 | pop YH 342 | pop r0 343 | endm 344 | macro POP_RETI ; 5 cycles 345 | pop YL 346 | out SREG, YL 347 | pop YL 348 | endm 349 | 350 | #include "asmcommon.inc" 351 | 352 | 353 | ; USB spec says: 354 | ; idle = J 355 | ; J = (D+ = 0), (D- = 1) 356 | ; K = (D+ = 1), (D- = 0) 357 | ; Spec allows 7.5 bit times from EOP to SOP for replies 358 | 359 | bitstuff7: 360 | eor x1, x4 ;[4] 361 | ldi x2, 0 ;[5] 362 | nop2 ;[6] C is zero (brcc) 363 | rjmp didStuff7 ;[8] 364 | 365 | bitstuffN: 366 | eor x1, x4 ;[5] 367 | ldi x2, 0 ;[6] 368 | lpm ;[7] 3 cycle NOP, modifies r0 369 | out USBOUT, x1 ;[10] <-- out 370 | rjmp didStuffN ;[0] 371 | 372 | #define bitStatus x3 373 | 374 | sendNakAndReti: 375 | ldi cnt, USBPID_NAK ;[-19] 376 | rjmp sendCntAndReti ;[-18] 377 | sendAckAndReti: 378 | ldi cnt, USBPID_ACK ;[-17] 379 | sendCntAndReti: 380 | mov r0, cnt ;[-16] 381 | ldi YL, 0 ;[-15] R0 address is 0 382 | ldi YH, 0 ;[-14] 383 | ldi cnt, 2 ;[-13] 384 | ; rjmp usbSendAndReti fallthrough 385 | 386 | ;usbSend: 387 | ;pointer to data in 'Y' 388 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 389 | ;uses: x1...x4, shift, cnt, Y 390 | ;Numbers in brackets are time since first bit of sync pattern is sent 391 | usbSendAndReti: ; 12 cycles until SOP 392 | in x2, USBDDR ;[-12] 393 | ori x2, USBMASK ;[-11] 394 | sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 395 | in x1, USBOUT ;[-8] port mirror for tx loop 396 | out USBDDR, x2 ;[-7] <- acquire bus 397 | ; need not init x2 (bitstuff history) because sync starts with 0 398 | ldi x4, USBMASK ;[-6] exor mask 399 | ldi shift, 0x80 ;[-5] sync byte is first byte sent 400 | ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes 401 | byteloop: 402 | bitloop: 403 | sbrs shift, 0 ;[8] [-3] 404 | eor x1, x4 ;[9] [-2] 405 | out USBOUT, x1 ;[10] [-1] <-- out 406 | ror shift ;[0] 407 | ror x2 ;[1] 408 | didStuffN: 409 | cpi x2, 0xfc ;[2] 410 | brcc bitstuffN ;[3] 411 | nop ;[4] 412 | subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 413 | brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value 414 | sbrs shift, 0 ;[7] 415 | eor x1, x4 ;[8] 416 | ror shift ;[9] 417 | didStuff7: 418 | out USBOUT, x1 ;[10] <-- out 419 | ror x2 ;[0] 420 | cpi x2, 0xfc ;[1] 421 | brcc bitstuff7 ;[2] 422 | ld shift, y+ ;[3] 423 | dec cnt ;[5] 424 | brne byteloop ;[6] 425 | ;make SE0: 426 | cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] 427 | lds x2, usbNewDeviceAddr;[8] 428 | lsl x2 ;[10] we compare with left shifted address 429 | out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle 430 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 431 | ;set address only after data packet was sent, not after handshake 432 | subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 433 | sbci YH, 0 ;[1] 434 | breq skipAddrAssign ;[2] 435 | sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer 436 | skipAddrAssign: 437 | ;end of usbDeviceAddress transfer 438 | ldi x2, 1< 10.0 cycles per bit, 80.0 cycles per byte 30 | ; Numbers in brackets are clocks counted from center of last sync bit 31 | ; when instruction starts 32 | 33 | ;---------------------------------------------------------------------------- 34 | ; order of registers pushed: 35 | ; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 36 | ;---------------------------------------------------------------------------- 37 | USB_INTR_VECTOR: 38 | push YL ;2 push only what is necessary to sync with edge ASAP 39 | in YL, SREG ;1 40 | push YL ;2 41 | ;---------------------------------------------------------------------------- 42 | ; Synchronize with sync pattern: 43 | ; 44 | ; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 45 | ; sync up with J to K edge during sync pattern -- use fastest possible loops 46 | ;The first part waits at most 1 bit long since we must be in sync pattern. 47 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 48 | ;waitForJ, ensure that this prerequisite is met. 49 | waitForJ: 50 | inc YL 51 | sbis USBIN, USBMINUS 52 | brne waitForJ ; just make sure we have ANY timeout 53 | ;------------------------------------------------------------------------------- 54 | ; The following code results in a sampling window of < 1/4 bit 55 | ; which meets the spec. 56 | ;------------------------------------------------------------------------------- 57 | waitForK: ;- 58 | sbis USBIN, USBMINUS ;1 [00] <-- sample 59 | rjmp foundK ;2 [01] 60 | sbis USBIN, USBMINUS ; <-- sample 61 | rjmp foundK 62 | sbis USBIN, USBMINUS ; <-- sample 63 | rjmp foundK 64 | sbis USBIN, USBMINUS ; <-- sample 65 | rjmp foundK 66 | sbis USBIN, USBMINUS ; <-- sample 67 | rjmp foundK 68 | sbis USBIN, USBMINUS ; <-- sample 69 | rjmp foundK 70 | #if USB_COUNT_SOF 71 | lds YL, usbSofCount 72 | inc YL 73 | sts usbSofCount, YL 74 | #endif /* USB_COUNT_SOF */ 75 | #ifdef USB_SOF_HOOK 76 | USB_SOF_HOOK 77 | #endif 78 | rjmp sofError 79 | ;------------------------------------------------------------------------------ 80 | ; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for 81 | ; center sampling] 82 | ; we have 1 bit time for setup purposes, then sample again. 83 | ; Numbers in brackets are cycles from center of first sync (double K) 84 | ; bit after the instruction 85 | ;------------------------------------------------------------------------------ 86 | foundK: ;- [02] 87 | lds YL, usbInputBufOffset;2 [03+04] tx loop 88 | push YH ;2 [05+06] 89 | clr YH ;1 [07] 90 | subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] 91 | sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] 92 | push shift ;2 [10+11] 93 | ser shift ;1 [12] 94 | sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) 95 | rjmp haveTwoBitsK ;2 [00] [14] 96 | pop shift ;2 [15+16] undo the push from before 97 | pop YH ;2 [17+18] undo the push from before 98 | rjmp waitForK ;2 [19+20] this was not the end of sync, retry 99 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 100 | ; bit times (= 20 cycles). 101 | 102 | ;---------------------------------------------------------------------------- 103 | ; push more registers and initialize values while we sample the first bits: 104 | ;---------------------------------------------------------------------------- 105 | haveTwoBitsK: ;- [01] 106 | push x1 ;2 [02+03] 107 | push x2 ;2 [04+05] 108 | push x3 ;2 [06+07] 109 | push bitcnt ;2 [08+09] 110 | in x1, USBIN ;1 [00] [10] <-- sample bit 0 111 | bst x1, USBMINUS ;1 [01] 112 | bld shift, 0 ;1 [02] 113 | push cnt ;2 [03+04] 114 | ldi cnt, USB_BUFSIZE ;1 [05] 115 | push x4 ;2 [06+07] tx loop 116 | rjmp rxLoop ;2 [08] 117 | ;---------------------------------------------------------------------------- 118 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 119 | ;---------------------------------------------------------------------------- 120 | unstuff0: ;- [07] (branch taken) 121 | andi x3, ~0x01 ;1 [08] 122 | mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit 123 | in x2, USBIN ;1 [00] [10] <-- sample bit 1 again 124 | andi x2, USBMASK ;1 [01] 125 | breq se0Hop ;1 [02] SE0 check for bit 1 126 | ori shift, 0x01 ;1 [03] 0b00000001 127 | nop ;1 [04] 128 | rjmp didUnstuff0 ;2 [05] 129 | ;----------------------------------------------------- 130 | unstuff1: ;- [05] (branch taken) 131 | mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit 132 | andi x3, ~0x02 ;1 [07] 133 | ori shift, 0x02 ;1 [08] 0b00000010 134 | nop ;1 [09] 135 | in x1, USBIN ;1 [00] [10] <-- sample bit 2 again 136 | andi x1, USBMASK ;1 [01] 137 | breq se0Hop ;1 [02] SE0 check for bit 2 138 | rjmp didUnstuff1 ;2 [03] 139 | ;----------------------------------------------------- 140 | unstuff2: ;- [05] (branch taken) 141 | andi x3, ~0x04 ;1 [06] 142 | ori shift, 0x04 ;1 [07] 0b00000100 143 | mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit 144 | nop ;1 [09] 145 | in x2, USBIN ;1 [00] [10] <-- sample bit 3 146 | andi x2, USBMASK ;1 [01] 147 | breq se0Hop ;1 [02] SE0 check for bit 3 148 | rjmp didUnstuff2 ;2 [03] 149 | ;----------------------------------------------------- 150 | unstuff3: ;- [00] [10] (branch taken) 151 | in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late 152 | andi x2, USBMASK ;1 [02] 153 | breq se0Hop ;1 [03] SE0 check for stuffed bit 3 154 | andi x3, ~0x08 ;1 [04] 155 | ori shift, 0x08 ;1 [05] 0b00001000 156 | rjmp didUnstuff3 ;2 [06] 157 | ;---------------------------------------------------------------------------- 158 | ; extra jobs done during bit interval: 159 | ; 160 | ; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], 161 | ; overflow check, jump to the head of rxLoop 162 | ; bit 1: SE0 check 163 | ; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] 164 | ; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] 165 | ; bit 4: SE0 check, none 166 | ; bit 5: SE0 check, none 167 | ; bit 6: SE0 check, none 168 | ; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others 169 | ;---------------------------------------------------------------------------- 170 | rxLoop: ;- [09] 171 | in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) 172 | andi x2, USBMASK ;1 [01] 173 | brne SkipSe0Hop ;1 [02] 174 | se0Hop: ;- [02] 175 | rjmp se0 ;2 [03] SE0 check for bit 1 176 | SkipSe0Hop: ;- [03] 177 | ser x3 ;1 [04] 178 | andi shift, 0xf9 ;1 [05] 0b11111001 179 | breq unstuff0 ;1 [06] 180 | didUnstuff0: ;- [06] 181 | eor x1, x2 ;1 [07] 182 | bst x1, USBMINUS ;1 [08] 183 | bld shift, 1 ;1 [09] 184 | in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) 185 | andi x1, USBMASK ;1 [01] 186 | breq se0Hop ;1 [02] SE0 check for bit 2 187 | andi shift, 0xf3 ;1 [03] 0b11110011 188 | breq unstuff1 ;1 [04] do remaining work for bit 1 189 | didUnstuff1: ;- [04] 190 | eor x2, x1 ;1 [05] 191 | bst x2, USBMINUS ;1 [06] 192 | bld shift, 2 ;1 [07] 193 | nop2 ;2 [08+09] 194 | in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) 195 | andi x2, USBMASK ;1 [01] 196 | breq se0Hop ;1 [02] SE0 check for bit 3 197 | andi shift, 0xe7 ;1 [03] 0b11100111 198 | breq unstuff2 ;1 [04] 199 | didUnstuff2: ;- [04] 200 | eor x1, x2 ;1 [05] 201 | bst x1, USBMINUS ;1 [06] 202 | bld shift, 3 ;1 [07] 203 | didUnstuff3: ;- [07] 204 | andi shift, 0xcf ;1 [08] 0b11001111 205 | breq unstuff3 ;1 [09] 206 | in x1, USBIN ;1 [00] [10] <-- sample bit 4 207 | andi x1, USBMASK ;1 [01] 208 | breq se0Hop ;1 [02] SE0 check for bit 4 209 | eor x2, x1 ;1 [03] 210 | bst x2, USBMINUS ;1 [04] 211 | bld shift, 4 ;1 [05] 212 | didUnstuff4: ;- [05] 213 | andi shift, 0x9f ;1 [06] 0b10011111 214 | breq unstuff4 ;1 [07] 215 | nop2 ;2 [08+09] 216 | in x2, USBIN ;1 [00] [10] <-- sample bit 5 217 | andi x2, USBMASK ;1 [01] 218 | breq se0 ;1 [02] SE0 check for bit 5 219 | eor x1, x2 ;1 [03] 220 | bst x1, USBMINUS ;1 [04] 221 | bld shift, 5 ;1 [05] 222 | didUnstuff5: ;- [05] 223 | andi shift, 0x3f ;1 [06] 0b00111111 224 | breq unstuff5 ;1 [07] 225 | nop2 ;2 [08+09] 226 | in x1, USBIN ;1 [00] [10] <-- sample bit 6 227 | andi x1, USBMASK ;1 [01] 228 | breq se0 ;1 [02] SE0 check for bit 6 229 | eor x2, x1 ;1 [03] 230 | bst x2, USBMINUS ;1 [04] 231 | bld shift, 6 ;1 [05] 232 | didUnstuff6: ;- [05] 233 | cpi shift, 0x02 ;1 [06] 0b00000010 234 | brlo unstuff6 ;1 [07] 235 | nop2 ;2 [08+09] 236 | in x2, USBIN ;1 [00] [10] <-- sample bit 7 237 | andi x2, USBMASK ;1 [01] 238 | breq se0 ;1 [02] SE0 check for bit 7 239 | eor x1, x2 ;1 [03] 240 | bst x1, USBMINUS ;1 [04] 241 | bld shift, 7 ;1 [05] 242 | didUnstuff7: ;- [05] 243 | cpi shift, 0x04 ;1 [06] 0b00000100 244 | brlo unstuff7 ;1 [07] 245 | eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others 246 | nop ;1 [09] 247 | in x1, USBIN ;1 [00] [10] <-- sample bit 0 248 | st y+, x3 ;2 [01+02] store data 249 | eor x2, x1 ;1 [03] 250 | bst x2, USBMINUS ;1 [04] 251 | bld shift, 0 ;1 [05] 252 | subi cnt, 1 ;1 [06] 253 | brcs overflow ;1 [07] 254 | rjmp rxLoop ;2 [08] 255 | ;----------------------------------------------------- 256 | unstuff4: ;- [08] 257 | andi x3, ~0x10 ;1 [09] 258 | in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 259 | andi x1, USBMASK ;1 [01] 260 | breq se0 ;1 [02] SE0 check for stuffed bit 4 261 | ori shift, 0x10 ;1 [03] 262 | rjmp didUnstuff4 ;2 [04] 263 | ;----------------------------------------------------- 264 | unstuff5: ;- [08] 265 | ori shift, 0x20 ;1 [09] 266 | in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 267 | andi x2, USBMASK ;1 [01] 268 | breq se0 ;1 [02] SE0 check for stuffed bit 5 269 | andi x3, ~0x20 ;1 [03] 270 | rjmp didUnstuff5 ;2 [04] 271 | ;----------------------------------------------------- 272 | unstuff6: ;- [08] 273 | andi x3, ~0x40 ;1 [09] 274 | in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 275 | andi x1, USBMASK ;1 [01] 276 | breq se0 ;1 [02] SE0 check for stuffed bit 6 277 | ori shift, 0x40 ;1 [03] 278 | rjmp didUnstuff6 ;2 [04] 279 | ;----------------------------------------------------- 280 | unstuff7: ;- [08] 281 | andi x3, ~0x80 ;1 [09] 282 | in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 283 | andi x2, USBMASK ;1 [01] 284 | breq se0 ;1 [02] SE0 check for stuffed bit 7 285 | ori shift, 0x80 ;1 [03] 286 | rjmp didUnstuff7 ;2 [04] 287 | 288 | macro POP_STANDARD ; 16 cycles 289 | pop x4 290 | pop cnt 291 | pop bitcnt 292 | pop x3 293 | pop x2 294 | pop x1 295 | pop shift 296 | pop YH 297 | endm 298 | macro POP_RETI ; 5 cycles 299 | pop YL 300 | out SREG, YL 301 | pop YL 302 | endm 303 | 304 | #include "asmcommon.inc" 305 | 306 | ;--------------------------------------------------------------------------- 307 | ; USB spec says: 308 | ; idle = J 309 | ; J = (D+ = 0), (D- = 1) 310 | ; K = (D+ = 1), (D- = 0) 311 | ; Spec allows 7.5 bit times from EOP to SOP for replies 312 | ;--------------------------------------------------------------------------- 313 | bitstuffN: ;- [04] 314 | eor x1, x4 ;1 [05] 315 | clr x2 ;1 [06] 316 | nop ;1 [07] 317 | rjmp didStuffN ;1 [08] 318 | ;--------------------------------------------------------------------------- 319 | bitstuff6: ;- [04] 320 | eor x1, x4 ;1 [05] 321 | clr x2 ;1 [06] 322 | rjmp didStuff6 ;1 [07] 323 | ;--------------------------------------------------------------------------- 324 | bitstuff7: ;- [02] 325 | eor x1, x4 ;1 [03] 326 | clr x2 ;1 [06] 327 | nop ;1 [05] 328 | rjmp didStuff7 ;1 [06] 329 | ;--------------------------------------------------------------------------- 330 | sendNakAndReti: ;- [-19] 331 | ldi x3, USBPID_NAK ;1 [-18] 332 | rjmp sendX3AndReti ;1 [-17] 333 | ;--------------------------------------------------------------------------- 334 | sendAckAndReti: ;- [-17] 335 | ldi cnt, USBPID_ACK ;1 [-16] 336 | sendCntAndReti: ;- [-16] 337 | mov x3, cnt ;1 [-15] 338 | sendX3AndReti: ;- [-15] 339 | ldi YL, 20 ;1 [-14] x3==r20 address is 20 340 | ldi YH, 0 ;1 [-13] 341 | ldi cnt, 2 ;1 [-12] 342 | ; rjmp usbSendAndReti fallthrough 343 | ;--------------------------------------------------------------------------- 344 | ;usbSend: 345 | ;pointer to data in 'Y' 346 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 347 | ;uses: x1...x4, btcnt, shift, cnt, Y 348 | ;Numbers in brackets are time since first bit of sync pattern is sent 349 | ;We need not to match the transfer rate exactly because the spec demands 350 | ;only 1.5% precision anyway. 351 | usbSendAndReti: ;- [-13] 13 cycles until SOP 352 | in x2, USBDDR ;1 [-12] 353 | ori x2, USBMASK ;1 [-11] 354 | sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) 355 | in x1, USBOUT ;1 [-08] port mirror for tx loop 356 | out USBDDR, x2 ;1 [-07] <- acquire bus 357 | ; need not init x2 (bitstuff history) because sync starts with 0 358 | ldi x4, USBMASK ;1 [-06] exor mask 359 | ldi shift, 0x80 ;1 [-05] sync byte is first byte sent 360 | ldi bitcnt, 6 ;1 [-04] 361 | txBitLoop: ;- [-04] [06] 362 | sbrs shift, 0 ;1 [-03] [07] 363 | eor x1, x4 ;1 [-02] [08] 364 | ror shift ;1 [-01] [09] 365 | didStuffN: ;- [09] 366 | out USBOUT, x1 ;1 [00] [10] <-- out N 367 | ror x2 ;1 [01] 368 | cpi x2, 0xfc ;1 [02] 369 | brcc bitstuffN ;1 [03] 370 | dec bitcnt ;1 [04] 371 | brne txBitLoop ;1 [05] 372 | sbrs shift, 0 ;1 [06] 373 | eor x1, x4 ;1 [07] 374 | ror shift ;1 [08] 375 | didStuff6: ;- [08] 376 | nop ;1 [09] 377 | out USBOUT, x1 ;1 [00] [10] <-- out 6 378 | ror x2 ;1 [01] 379 | cpi x2, 0xfc ;1 [02] 380 | brcc bitstuff6 ;1 [03] 381 | sbrs shift, 0 ;1 [04] 382 | eor x1, x4 ;1 [05] 383 | ror shift ;1 [06] 384 | ror x2 ;1 [07] 385 | didStuff7: ;- [07] 386 | ldi bitcnt, 6 ;1 [08] 387 | cpi x2, 0xfc ;1 [09] 388 | out USBOUT, x1 ;1 [00] [10] <-- out 7 389 | brcc bitstuff7 ;1 [01] 390 | ld shift, y+ ;2 [02+03] 391 | dec cnt ;1 [04] 392 | brne txBitLoop ;1 [05] 393 | makeSE0: 394 | cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] 395 | lds x2, usbNewDeviceAddr;2 [07+08] 396 | lsl x2 ;1 [09] we compare with left shifted address 397 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 398 | ;set address only after data packet was sent, not after handshake 399 | out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle 400 | subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 401 | sbci YH, 0 ;1 [02] 402 | breq skipAddrAssign ;1 [03] 403 | sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer 404 | ;---------------------------------------------------------------------------- 405 | ;end of usbDeviceAddress transfer 406 | skipAddrAssign: ;- [03/04] 407 | ldi x2, 1<len & 0x10){ /* packet buffer was empty */ 233 | txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ 234 | }else{ 235 | txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ 236 | } 237 | p = txStatus->buffer + 1; 238 | i = len; 239 | do{ /* if len == 0, we still copy 1 byte, but that's no problem */ 240 | *p++ = *data++; 241 | }while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ 242 | usbCrc16Append(&txStatus->buffer[1], len); 243 | txStatus->len = len + 4; /* len must be given including sync byte */ 244 | DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); 245 | } 246 | 247 | USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) 248 | { 249 | usbGenericSetInterrupt(data, len, &usbTxStatus1); 250 | } 251 | #endif 252 | 253 | #if USB_CFG_HAVE_INTRIN_ENDPOINT3 254 | USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) 255 | { 256 | usbGenericSetInterrupt(data, len, &usbTxStatus3); 257 | } 258 | #endif 259 | #endif /* USB_CFG_SUPPRESS_INTR_CODE */ 260 | 261 | /* ------------------ utilities for code following below ------------------- */ 262 | 263 | /* Use defines for the switch statement so that we can choose between an 264 | * if()else if() and a switch/case based implementation. switch() is more 265 | * efficient for a LARGE set of sequential choices, if() is better in all other 266 | * cases. 267 | */ 268 | #if USB_CFG_USE_SWITCH_STATEMENT 269 | # define SWITCH_START(cmd) switch(cmd){{ 270 | # define SWITCH_CASE(value) }break; case (value):{ 271 | # define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ 272 | # define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ 273 | # define SWITCH_DEFAULT }break; default:{ 274 | # define SWITCH_END }} 275 | #else 276 | # define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ 277 | # define SWITCH_CASE(value) }else if(_cmd == (value)){ 278 | # define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ 279 | # define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ 280 | # define SWITCH_DEFAULT }else{ 281 | # define SWITCH_END }} 282 | #endif 283 | 284 | #ifndef USB_RX_USER_HOOK 285 | #define USB_RX_USER_HOOK(data, len) 286 | #endif 287 | #ifndef USB_SET_ADDRESS_HOOK 288 | #define USB_SET_ADDRESS_HOOK() 289 | #endif 290 | 291 | /* ------------------------------------------------------------------------- */ 292 | 293 | /* We use if() instead of #if in the macro below because #if can't be used 294 | * in macros and the compiler optimizes constant conditions anyway. 295 | * This may cause problems with undefined symbols if compiled without 296 | * optimizing! 297 | */ 298 | #define GET_DESCRIPTOR(cfgProp, staticName) \ 299 | if(cfgProp){ \ 300 | if((cfgProp) & USB_PROP_IS_RAM) \ 301 | flags = 0; \ 302 | if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ 303 | len = usbFunctionDescriptor(rq); \ 304 | }else{ \ 305 | len = USB_PROP_LENGTH(cfgProp); \ 306 | usbMsgPtr = (uchar *)(staticName); \ 307 | } \ 308 | } 309 | 310 | /* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used 311 | * internally for all types of descriptors. 312 | */ 313 | static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) 314 | { 315 | usbMsgLen_t len = 0; 316 | uchar flags = USB_FLG_MSGPTR_IS_ROM; 317 | 318 | SWITCH_START(rq->wValue.bytes[1]) 319 | SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ 320 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) 321 | SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ 322 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) 323 | SWITCH_CASE(USBDESCR_STRING) /* 3 */ 324 | #if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC 325 | if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) 326 | flags = 0; 327 | len = usbFunctionDescriptor(rq); 328 | #else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ 329 | SWITCH_START(rq->wValue.bytes[0]) 330 | SWITCH_CASE(0) 331 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) 332 | SWITCH_CASE(1) 333 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) 334 | SWITCH_CASE(2) 335 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) 336 | SWITCH_CASE(3) 337 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) 338 | SWITCH_DEFAULT 339 | if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ 340 | len = usbFunctionDescriptor(rq); 341 | } 342 | SWITCH_END 343 | #endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ 344 | #if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ 345 | SWITCH_CASE(USBDESCR_HID) /* 0x21 */ 346 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) 347 | SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ 348 | GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) 349 | #endif 350 | SWITCH_DEFAULT 351 | if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ 352 | len = usbFunctionDescriptor(rq); 353 | } 354 | SWITCH_END 355 | usbMsgFlags = flags; 356 | return len; 357 | } 358 | 359 | /* ------------------------------------------------------------------------- */ 360 | 361 | /* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for 362 | * standard requests instead of class and custom requests. 363 | */ 364 | static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) 365 | { 366 | uchar len = 0, *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ 367 | uchar value = rq->wValue.bytes[0]; 368 | #if USB_CFG_IMPLEMENT_HALT 369 | uchar index = rq->wIndex.bytes[0]; 370 | #endif 371 | 372 | dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ 373 | SWITCH_START(rq->bRequest) 374 | SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ 375 | uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ 376 | if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) 377 | dataPtr[0] = USB_CFG_IS_SELF_POWERED; 378 | #if USB_CFG_IMPLEMENT_HALT 379 | if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ 380 | dataPtr[0] = usbTxLen1 == USBPID_STALL; 381 | #endif 382 | dataPtr[1] = 0; 383 | len = 2; 384 | #if USB_CFG_IMPLEMENT_HALT 385 | SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ 386 | if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ 387 | usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; 388 | usbResetDataToggling(); 389 | } 390 | #endif 391 | SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ 392 | usbNewDeviceAddr = value; 393 | USB_SET_ADDRESS_HOOK(); 394 | SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ 395 | len = usbDriverDescriptor(rq); 396 | goto skipMsgPtrAssignment; 397 | SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ 398 | dataPtr = &usbConfiguration; /* send current configuration value */ 399 | len = 1; 400 | SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ 401 | usbConfiguration = value; 402 | usbResetStall(); 403 | SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ 404 | len = 1; 405 | #if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE 406 | SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ 407 | usbResetDataToggling(); 408 | usbResetStall(); 409 | #endif 410 | SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ 411 | /* Should we add an optional hook here? */ 412 | SWITCH_END 413 | usbMsgPtr = dataPtr; 414 | skipMsgPtrAssignment: 415 | return len; 416 | } 417 | 418 | /* ------------------------------------------------------------------------- */ 419 | 420 | /* usbProcessRx() is called for every message received by the interrupt 421 | * routine. It distinguishes between SETUP and DATA packets and processes 422 | * them accordingly. 423 | */ 424 | static inline void usbProcessRx(uchar *data, uchar len) 425 | { 426 | usbRequest_t *rq = (usbRequest_t *)((void *)data); 427 | 428 | /* usbRxToken can be: 429 | * 0x2d 00101101 (USBPID_SETUP for setup data) 430 | * 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) 431 | * 0...0x0f for OUT on endpoint X 432 | */ 433 | DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ 434 | USB_RX_USER_HOOK(data, len) 435 | #if USB_CFG_IMPLEMENT_FN_WRITEOUT 436 | if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ 437 | usbFunctionWriteOut(data, len); 438 | return; 439 | } 440 | #endif 441 | if(usbRxToken == (uchar)USBPID_SETUP){ 442 | if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ 443 | return; 444 | usbMsgLen_t replyLen; 445 | usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ 446 | usbTxLen = USBPID_NAK; /* abort pending transmit */ 447 | usbMsgFlags = 0; 448 | uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; 449 | if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ 450 | replyLen = usbFunctionSetup(data); 451 | }else{ 452 | replyLen = usbDriverSetup(rq); 453 | } 454 | #if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE 455 | if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ 456 | /* do some conditioning on replyLen, but on IN transfers only */ 457 | if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ 458 | if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ 459 | replyLen = rq->wLength.bytes[0]; 460 | }else{ 461 | replyLen = rq->wLength.word; 462 | } 463 | } 464 | usbMsgFlags = USB_FLG_USE_USER_RW; 465 | }else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ 466 | #endif 467 | if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ 468 | if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ 469 | replyLen = rq->wLength.bytes[0]; 470 | }else{ 471 | if(replyLen > rq->wLength.word) /* limit length to max */ 472 | replyLen = rq->wLength.word; 473 | } 474 | usbMsgLen = replyLen; 475 | }else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ 476 | #if USB_CFG_IMPLEMENT_FN_WRITE 477 | if(usbMsgFlags & USB_FLG_USE_USER_RW){ 478 | uchar rval = usbFunctionWrite(data, len); 479 | if(rval == 0xff){ /* an error occurred */ 480 | usbTxLen = USBPID_STALL; 481 | }else if(rval != 0){ /* This was the final package */ 482 | usbMsgLen = 0; /* answer with a zero-sized data packet */ 483 | } 484 | } 485 | #endif 486 | } 487 | } 488 | 489 | /* ------------------------------------------------------------------------- */ 490 | 491 | /* This function is similar to usbFunctionRead(), but it's also called for 492 | * data handled automatically by the driver (e.g. descriptor reads). 493 | */ 494 | static uchar usbDeviceRead(uchar *data, uchar len) 495 | { 496 | if(len > 0){ /* don't bother app with 0 sized reads */ 497 | #if USB_CFG_IMPLEMENT_FN_READ 498 | if(usbMsgFlags & USB_FLG_USE_USER_RW){ 499 | len = usbFunctionRead(data, len); 500 | }else 501 | #endif 502 | { 503 | uchar i = len, *r = usbMsgPtr; 504 | if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ 505 | do{ 506 | uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ 507 | *data++ = c; 508 | r++; 509 | }while(--i); 510 | }else{ /* RAM data */ 511 | do{ 512 | *data++ = *r++; 513 | }while(--i); 514 | } 515 | usbMsgPtr = r; 516 | } 517 | } 518 | return len; 519 | } 520 | 521 | /* ------------------------------------------------------------------------- */ 522 | 523 | /* usbBuildTxBlock() is called when we have data to transmit and the 524 | * interrupt routine's transmit buffer is empty. 525 | */ 526 | static inline void usbBuildTxBlock(void) 527 | { 528 | usbMsgLen_t wantLen; 529 | uchar len; 530 | 531 | wantLen = usbMsgLen; 532 | if(wantLen > 8) 533 | wantLen = 8; 534 | usbMsgLen -= wantLen; 535 | usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ 536 | len = usbDeviceRead(usbTxBuf + 1, wantLen); 537 | if(len <= 8){ /* valid data packet */ 538 | usbCrc16Append(&usbTxBuf[1], len); 539 | len += 4; /* length including sync byte */ 540 | if(len < 12) /* a partial package identifies end of message */ 541 | usbMsgLen = USB_NO_MSG; 542 | }else{ 543 | len = USBPID_STALL; /* stall the endpoint */ 544 | usbMsgLen = USB_NO_MSG; 545 | } 546 | usbTxLen = len; 547 | DBG2(0x20, usbTxBuf, len-1); 548 | } 549 | 550 | /* ------------------------------------------------------------------------- */ 551 | 552 | static inline void usbHandleResetHook(uchar notResetState) 553 | { 554 | #ifdef USB_RESET_HOOK 555 | static uchar wasReset; 556 | uchar isReset = !notResetState; 557 | 558 | if(wasReset != isReset){ 559 | USB_RESET_HOOK(isReset); 560 | wasReset = isReset; 561 | } 562 | #endif 563 | } 564 | 565 | /* ------------------------------------------------------------------------- */ 566 | 567 | USB_PUBLIC void usbPoll(void) 568 | { 569 | schar len; 570 | uchar i; 571 | 572 | len = usbRxLen - 3; 573 | if(len >= 0){ 574 | /* We could check CRC16 here -- but ACK has already been sent anyway. If you 575 | * need data integrity checks with this driver, check the CRC in your app 576 | * code and report errors back to the host. Since the ACK was already sent, 577 | * retries must be handled on application level. 578 | * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); 579 | */ 580 | usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); 581 | #if USB_CFG_HAVE_FLOWCONTROL 582 | if(usbRxLen > 0) /* only mark as available if not inactivated */ 583 | usbRxLen = 0; 584 | #else 585 | usbRxLen = 0; /* mark rx buffer as available */ 586 | #endif 587 | } 588 | if(usbTxLen & 0x10){ /* transmit system idle */ 589 | if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ 590 | usbBuildTxBlock(); 591 | } 592 | } 593 | for(i = 20; i > 0; i--){ 594 | uchar usbLineStatus = USBIN & USBMASK; 595 | if(usbLineStatus != 0) /* SE0 has ended */ 596 | goto isNotReset; 597 | } 598 | /* RESET condition, called multiple times during reset */ 599 | usbNewDeviceAddr = 0; 600 | usbDeviceAddr = 0; 601 | usbResetStall(); 602 | DBG1(0xff, 0, 0); 603 | isNotReset: 604 | usbHandleResetHook(i); 605 | } 606 | 607 | /* ------------------------------------------------------------------------- */ 608 | 609 | USB_PUBLIC void usbInit(void) 610 | { 611 | #if USB_INTR_CFG_SET != 0 612 | USB_INTR_CFG |= USB_INTR_CFG_SET; 613 | #endif 614 | #if USB_INTR_CFG_CLR != 0 615 | USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); 616 | #endif 617 | USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); 618 | usbResetDataToggling(); 619 | #if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE 620 | usbTxLen1 = USBPID_NAK; 621 | #if USB_CFG_HAVE_INTRIN_ENDPOINT3 622 | usbTxLen3 = USBPID_NAK; 623 | #endif 624 | #endif 625 | } 626 | 627 | /* ------------------------------------------------------------------------- */ --------------------------------------------------------------------------------