parent
5282c3e7d0
commit
fbcb466855
@ -0,0 +1,73 @@ |
||||
|
||||
What is this thing? |
||||
------------------- |
||||
|
||||
This device is a hash-based-one-time-password (=HOTP, RFC 4226) generator. You |
||||
can use them to make an existing authentication more secure or as a single |
||||
authentication barrier to enter a system. You can easily do both on linux |
||||
machines using the oath toolkit (http://www.nongnu.org/oath-toolkit/). |
||||
|
||||
How does it work? |
||||
----------------- |
||||
|
||||
The device is an USB-Stick, which opearates as a virtual keyboard. A login |
||||
sequence usually happens like this: |
||||
|
||||
1. When asked for the OTP, plug in the device and wait a few seconds |
||||
2. The device should generate a few keystrokes - this is the token |
||||
3. Confirm the login by pressing the button on the device this will |
||||
generate an ENTER-keystroke and the internal counter is icremented |
||||
by one. |
||||
4. Pressing the button again reset the device, and the sequence |
||||
repeats. Otherwise just unplug it. |
||||
|
||||
How to install the firmware? |
||||
---------------------------- |
||||
|
||||
You will need the avr-gcc toolchain (avr-gcc, avr-binutils and the avrlibc). |
||||
Go to the source tree (the same directory where this file should be). Now edit |
||||
the Makefile. If you don't know what you're doing simply edit the "SECRET" |
||||
define: |
||||
|
||||
SECRET = -DSECLEN=6 -DSECRET="{0xC0, 0xFF, 0xEE, 0xDE, 0xCA, 0xDE}" |
||||
|
||||
It is recomended to take a secret of length 20 (longer secrets are supported but |
||||
do not provide additional security). This is basically a sha1 digest. I usually |
||||
do the following to generate secrets: |
||||
|
||||
|
||||
|
||||
type make. If the compilation runs well, you will end up with some .hex files: |
||||
- The main file: hotp.hex (this goes to the flash) |
||||
- The eeprom init: eeprom.hex (this goes to the internal eeprom) |
||||
|
||||
Security considerations: |
||||
------------------------ |
||||
|
||||
If you're interested in HOTP security in general, pleas read the corresponding |
||||
sections in the RFC 4226. This section deals with the security of the device |
||||
hardware. |
||||
|
||||
The device was designed in order to provide additional security, not as the |
||||
only protection from attacking/entering your system. If your entire |
||||
authentication is based on this device you should consider the following: |
||||
|
||||
Anyone who obtains a one time passwordt(=token) from the device, will be able |
||||
to authenticate as you until the system and the token-generator get synchronized |
||||
(i.e. your next legitimate login)! |
||||
|
||||
The ATtiny85 contains two Lock bits which can be programmed (=set to 0) in order |
||||
to protect the program and EEPROM memory. While the program contains the shared |
||||
secret, the EEPROM contains the counter used to compute the. Although the shared |
||||
secret is a little more critical, nobody should know either one of them. You |
||||
should therefore enable the two lock bits. |
||||
|
||||
If you lose the token-generator it might be hard to get into the system. You |
||||
could ask the super user, to look up the counter and secret or disable the |
||||
HOTP login method. If you lost your super users token-generator and you have |
||||
physical access to the device you can use a live-cd to rescue your system. |
||||
There are several tutorials on the web that describe how to do that. And if |
||||
you don't have physical access or can't ask your super user I can only recomend |
||||
not to loose the token-generator. |
||||
|
||||
|
||||
@ -0,0 +1,54 @@ |
||||
#include "buffer.h" |
||||
|
||||
uint8_t buff_empty=1; |
||||
static uint8_t buff_start; |
||||
static uint8_t buff_end; |
||||
|
||||
static uint8_t buffer[BUFFER_SIZE]; |
||||
|
||||
uint8_t readBuffer(void){ |
||||
uint8_t ret;
|
||||
if(buff_empty){ |
||||
return 0; |
||||
} |
||||
ret=buffer[buff_end]; |
||||
buff_end++; |
||||
if(buff_end==BUFFER_SIZE){ |
||||
buff_end=0; |
||||
} |
||||
if(buff_end==buff_start){ |
||||
buff_empty=1; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
void writeToBuffer(uint8_t data){ |
||||
if(buff_start==buff_end && !buff_empty){ |
||||
/*overrun*/ |
||||
return; |
||||
} |
||||
buffer[buff_start]=data; |
||||
buff_start++; |
||||
if(buff_start==BUFFER_SIZE){ |
||||
buff_start=0; |
||||
} |
||||
buff_empty=0; |
||||
} |
||||
|
||||
void buffer_unum(uint32_t num){ |
||||
uint8_t text[8] = {0,0,0,0,0,0,0,0}; |
||||
uint8_t i; |
||||
|
||||
for(i=1;i<=8;i++){ |
||||
/*calculate scancode from digit*/ |
||||
text[8-i] = USB_KEY_1-1+((num)%10); |
||||
if(text[8-i]==USB_KEY_1-1){ |
||||
text[8-i]=USB_KEY_0; |
||||
} |
||||
num/=10; |
||||
} |
||||
for(i=0;i<8;i++){ |
||||
writeToBuffer(text[i]); |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,16 @@ |
||||
#ifndef _BUFFER_H |
||||
#define _BUFFER_H |
||||
|
||||
#include "../usb_key_codes.h" |
||||
#include <avr/io.h> |
||||
|
||||
#define BUFFER_SIZE 10 |
||||
|
||||
extern uint8_t buff_empty; |
||||
uint8_t readBuffer(void); |
||||
void writeToBuffer(uint8_t data); |
||||
void buffer_unum(uint32_t num); |
||||
|
||||
#endif //_BUFFER_H
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@ |
||||
Author: Stefan Krulj aka powertomato@gmail.com |
||||
|
||||
The layouts were all derived from the US layout which has been taken from the |
||||
PS/2 to USB Keyboard scancode transition table which can be found at: |
||||
|
||||
http://www.microsoft.com/taiwan/whdc/archive/scancode.mspx#ENB |
||||
|
||||
I didn't test most of the codes so use them with care. If you find any errors |
||||
please feel free to email me. |
||||
@ -0,0 +1,162 @@ |
||||
#ifndef _USB_KEY_CODES_H |
||||
#define _USB_KEY_CODES_H |
||||
|
||||
//DE Layout
|
||||
|
||||
#define USB_MOD_CONTROL_LEFT (1<<0) |
||||
#define USB_MOD_SHIFT_LEFT (1<<1) |
||||
#define USB_MOD_ALT_LEFT (1<<2) |
||||
#define USB_MOD_GUI_LEFT (1<<3) |
||||
#define USB_MOD_CONTROL_RIGHT (1<<4) |
||||
#define USB_MOD_SHIFT_RIGHT (1<<5) |
||||
#define USB_MOD_ALT_RIGHT (1<<6) |
||||
#define USB_MOD_GUI_RIGHT (1<<7) |
||||
|
||||
#define USB_KEY_NOP 0x00 |
||||
#define USB_KEY_OVERRUN 0x01 |
||||
#define USB_KEY_POST_FAIL 0x02 |
||||
#define USB_KEY_ERR_UNDEF 0x03 |
||||
|
||||
#define USB_KEY_A 0x04 |
||||
#define USB_KEY_B 0x05 |
||||
#define USB_KEY_C 0x06 |
||||
#define USB_KEY_D 0x07 |
||||
#define USB_KEY_E 0x08 |
||||
#define USB_KEY_F 0x09 |
||||
#define USB_KEY_G 0x0A |
||||
#define USB_KEY_H 0x0B |
||||
#define USB_KEY_I 0x0C |
||||
#define USB_KEY_J 0x0D |
||||
#define USB_KEY_K 0x0E |
||||
#define USB_KEY_L 0x0F |
||||
#define USB_KEY_M 0x10 |
||||
#define USB_KEY_N 0x11 |
||||
#define USB_KEY_O 0x12 |
||||
#define USB_KEY_P 0x13 |
||||
#define USB_KEY_Q 0x14 |
||||
#define USB_KEY_R 0x15 |
||||
#define USB_KEY_S 0x16 |
||||
#define USB_KEY_T 0x17 |
||||
#define USB_KEY_U 0x18 |
||||
#define USB_KEY_V 0x19 |
||||
#define USB_KEY_W 0x1A |
||||
#define USB_KEY_X 0x1B |
||||
#define USB_KEY_Z 0x1C |
||||
#define USB_KEY_Y 0x1D |
||||
|
||||
#define USB_KEY_1 0x1E |
||||
#define USB_KEY_2 0x1F |
||||
#define USB_KEY_3 0x20 |
||||
#define USB_KEY_4 0x21 |
||||
#define USB_KEY_5 0x22 |
||||
#define USB_KEY_6 0x23 |
||||
#define USB_KEY_7 0x24 |
||||
#define USB_KEY_8 0x25 |
||||
#define USB_KEY_9 0x26 |
||||
#define USB_KEY_0 0x27 |
||||
|
||||
#define USB_KEY_ENTER 0x28 |
||||
#define USB_KEY_ESC 0x29 |
||||
#define USB_KEY_BSPACE 0x2A |
||||
#define USB_KEY_TAB 0x2B |
||||
#define USB_KEY_SPACE 0x2C |
||||
|
||||
#define USB_KEY_SZ 0x2D |
||||
#define USB_KEY_AOSTROPHE 0x2E |
||||
#define USB_KEY_UE 0x2F |
||||
#define USB_KEY_PLUS 0x30 |
||||
#define USB_KEY_NUM_SIGN_ALT 0x31 |
||||
#define USB_KEY_NUM_SIGN 0x32 |
||||
#define USB_KEY_OE 0x33 |
||||
#define USB_KEY_AE 0x34 |
||||
#define USB_KEY_CIRCUMFLEX 0x35 |
||||
#define USB_KEY_COMMA 0x36 |
||||
#define USB_KEY_DOT 0x37 |
||||
#define USB_KEY_MINUS 0x38 |
||||
|
||||
#define USB_KEY_F1 0x3A |
||||
#define USB_KEY_F2 0x3B |
||||
#define USB_KEY_F3 0x3C |
||||
#define USB_KEY_F4 0x3D |
||||
#define USB_KEY_F5 0x3E |
||||
#define USB_KEY_F6 0x3F |
||||
#define USB_KEY_F7 0x40 |
||||
#define USB_KEY_F8 0x41 |
||||
#define USB_KEY_F9 0x42 |
||||
#define USB_KEY_F10 0x43 |
||||
#define USB_KEY_F11 0x44 |
||||
#define USB_KEY_F12 0x45 |
||||
|
||||
#define USB_KEY_PRINT 0x46 |
||||
#define USB_KEY_PAUSE 0x48 |
||||
#define USB_KEY_INSERT 0x49 |
||||
#define USB_KEY_HOME 0x4A |
||||
#define USB_KEY_PAGE_UP 0x4B |
||||
#define USB_KEY_DEL 0x4C |
||||
#define USB_KEY_END 0x4D |
||||
#define USB_KEY_PAGE_DOWN 0x4E |
||||
|
||||
#define USB_KEY_RIGHT 0x4F |
||||
#define USB_KEY_LEFT 0x50 |
||||
#define USB_KEY_DOWN 0x51 |
||||
#define USB_KEY_UP 0x52 |
||||
|
||||
#define USB_KEY_NUM_LOCK 0x53 |
||||
#define USB_KEY_CAPS_LOCK 0x39 |
||||
#define USB_KEY_SCROLL_LOCK 0x47 |
||||
|
||||
#define USB_KEY_KP_SLASH 0x54 |
||||
#define USB_KEY_KP_ASTERISK 0x55 |
||||
#define USB_KEY_KP_MINUS 0x56 |
||||
#define USB_KEY_KP_PLUS 0x57 |
||||
#define USB_KEY_KP_ENTER 0x58 |
||||
#define USB_KEY_KP_1 0x59 |
||||
#define USB_KEY_KP_2 0x5A |
||||
#define USB_KEY_KP_3 0x5B |
||||
#define USB_KEY_KP_4 0x5C |
||||
#define USB_KEY_KP_5 0x5D |
||||
#define USB_KEY_KP_6 0x5E |
||||
#define USB_KEY_KP_7 0x5F |
||||
#define USB_KEY_KP_8 0x60 |
||||
#define USB_KEY_KP_9 0x61 |
||||
#define USB_KEY_KP_0 0x62 |
||||
#define USB_KEY_KP_DOT 0x63 |
||||
|
||||
#define USB_KEY_LT 0x64 |
||||
|
||||
//Special & media-keys
|
||||
#define USB_KEY_NEXT_TRACK 0x00B5 |
||||
#define USB_KEY_PREV_TRACK 0x00B6 |
||||
|
||||
#define USB_KEY_STOP 0x00B7 |
||||
#define USB_KEY_PLAY_PAUSE 0x00CD |
||||
#define USB_KEY_MUTE 0x00E2 |
||||
|
||||
#define USB_KEY_BASS_BOOST 0x00E5 |
||||
#define USB_KEY_LOUDNESS 0x00E7 |
||||
|
||||
#define USB_KEY_VOL_UP 0x00E9 |
||||
#define USB_KEY_VOL_DOWN 0x00EA |
||||
|
||||
#define USB_KEY_BASS_UP 0x0152 |
||||
#define USB_KEY_BASS_DOWN 0x0153 |
||||
|
||||
#define USB_KEY_TREBLE_UP 0x0154 |
||||
#define USB_KEY_TREBLE_DOWN 0x0155 |
||||
|
||||
#define USB_KEY_MEDIA_SELECT 0x0183 |
||||
|
||||
#define USB_KEY_MAIL 0x018A |
||||
#define USB_KEY_CALC 0x0192 |
||||
#define USB_KEY_PLACES 0x0194 |
||||
#define USB_KEY_MY_COMPUTER 0x0194 |
||||
|
||||
#define USB_KEY_WWW_SEARCH 0x0221 |
||||
#define USB_KEY_WWW_HOME 0x0223 |
||||
#define USB_KEY_WWW_BACK 0x0224 |
||||
#define USB_KEY_WWW_FORWARD 0x0225 |
||||
#define USB_KEY_WWW_STOP 0x0226 |
||||
#define USB_KEY_WWW_REFRESH 0x0227 |
||||
#define USB_KEY_WWW_BOOKMARKS 0x022A |
||||
|
||||
#endif //_USB_KEY_CODES_H***
|
||||
@ -0,0 +1,163 @@ |
||||
#ifndef _USB_KEY_CODES_H |
||||
#define _USB_KEY_CODES_H |
||||
|
||||
//US Layout
|
||||
|
||||
#define USB_MOD_CONTROL_LEFT (1<<0) |
||||
#define USB_MOD_SHIFT_LEFT (1<<1) |
||||
#define USB_MOD_ALT_LEFT (1<<2) |
||||
#define USB_MOD_GUI_LEFT (1<<3) |
||||
#define USB_MOD_CONTROL_RIGHT (1<<4) |
||||
#define USB_MOD_SHIFT_RIGHT (1<<5) |
||||
#define USB_MOD_ALT_RIGHT (1<<6) |
||||
#define USB_MOD_GUI_RIGHT (1<<7) |
||||
|
||||
#define USB_KEY_NOP 0x00 |
||||
#define USB_KEY_OVERRUN 0x01 |
||||
#define USB_KEY_POST_FAIL 0x02 |
||||
#define USB_KEY_ERR_UNDEF 0x03 |
||||
|
||||
#define USB_KEY_A 0x04 |
||||
#define USB_KEY_B 0x05 |
||||
#define USB_KEY_C 0x06 |
||||
#define USB_KEY_D 0x07 |
||||
#define USB_KEY_E 0x08 |
||||
#define USB_KEY_F 0x09 |
||||
#define USB_KEY_G 0x0A |
||||
#define USB_KEY_H 0x0B |
||||
#define USB_KEY_I 0x0C |
||||
#define USB_KEY_J 0x0D |
||||
#define USB_KEY_K 0x0E |
||||
#define USB_KEY_L 0x0F |
||||
#define USB_KEY_M 0x10 |
||||
#define USB_KEY_N 0x11 |
||||
#define USB_KEY_O 0x12 |
||||
#define USB_KEY_P 0x13 |
||||
#define USB_KEY_Q 0x14 |
||||
#define USB_KEY_R 0x15 |
||||
#define USB_KEY_S 0x16 |
||||
#define USB_KEY_T 0x17 |
||||
#define USB_KEY_U 0x18 |
||||
#define USB_KEY_V 0x19 |
||||
#define USB_KEY_W 0x1A |
||||
#define USB_KEY_X 0x1B |
||||
#define USB_KEY_Y 0x1C |
||||
#define USB_KEY_Z 0x1D |
||||
|
||||
#define USB_KEY_1 0x1E |
||||
#define USB_KEY_2 0x1F |
||||
#define USB_KEY_3 0x20 |
||||
#define USB_KEY_4 0x21 |
||||
#define USB_KEY_5 0x22 |
||||
#define USB_KEY_6 0x23 |
||||
#define USB_KEY_7 0x24 |
||||
#define USB_KEY_8 0x25 |
||||
#define USB_KEY_9 0x26 |
||||
#define USB_KEY_0 0x27 |
||||
|
||||
#define USB_KEY_ENTER 0x28 |
||||
#define USB_KEY_ESC 0x29 |
||||
#define USB_KEY_BSPACE 0x2A |
||||
#define USB_KEY_TAB 0x2B |
||||
#define USB_KEY_SPACE 0x2C |
||||
|
||||
#define USB_KEY_MINUS 0x2D |
||||
#define USB_KEY_EQ 0x2E |
||||
#define USB_KEY_BRACKET_O 0x2F |
||||
#define USB_KEY_BRACKET_C 0x30 |
||||
#define USB_KEY_BSLASH 0x31 |
||||
#define USB_KEY_EURO1 0x32 |
||||
#define USB_KEY_SEMICOL 0x33 |
||||
#define USB_KEY_QUOT 0x34 |
||||
#define USB_KEY_GRAVIS 0x35 |
||||
#define USB_KEY_BTICK 0x35 |
||||
#define USB_KEY_COMMA 0x36 |
||||
#define USB_KEY_DOT 0x37 |
||||
#define USB_KEY_SLASH 0x38 |
||||
|
||||
#define USB_KEY_F1 0x3A |
||||
#define USB_KEY_F2 0x3B |
||||
#define USB_KEY_F3 0x3C |
||||
#define USB_KEY_F4 0x3D |
||||
#define USB_KEY_F5 0x3E |
||||
#define USB_KEY_F6 0x3F |
||||
#define USB_KEY_F7 0x40 |
||||
#define USB_KEY_F8 0x41 |
||||
#define USB_KEY_F9 0x42 |
||||
#define USB_KEY_F10 0x43 |
||||
#define USB_KEY_F11 0x44 |
||||
#define USB_KEY_F12 0x45 |
||||
|
||||
#define USB_KEY_PRINT 0x46 |
||||
#define USB_KEY_PAUSE 0x48 |
||||
#define USB_KEY_INSERT 0x49 |
||||
#define USB_KEY_HOME 0x4A |
||||
#define USB_KEY_PAGE_UP 0x4B |
||||
#define USB_KEY_DEL 0x4C |
||||
#define USB_KEY_END 0x4D |
||||
#define USB_KEY_PAGE_DOWN 0x4E |
||||
|
||||
#define USB_KEY_RIGHT 0x4F |
||||
#define USB_KEY_LEFT 0x50 |
||||
#define USB_KEY_DOWN 0x51 |
||||
#define USB_KEY_UP 0x52 |
||||
|
||||
#define USB_KEY_NUM_LOCK 0x53 |
||||
#define USB_KEY_CAPS_LOCK 0x39 |
||||
#define USB_KEY_SCROLL_LOCK 0x47 |
||||
|
||||
#define USB_KEY_KP_SLASH 0x54 |
||||
#define USB_KEY_KP_ASTERISK 0x55 |
||||
#define USB_KEY_KP_MINUS 0x56 |
||||
#define USB_KEY_KP_PLUS 0x57 |
||||
#define USB_KEY_KP_ENTER 0x58 |
||||
#define USB_KEY_KP_1 0x59 |
||||
#define USB_KEY_KP_2 0x5A |
||||
#define USB_KEY_KP_3 0x5B |
||||
#define USB_KEY_KP_4 0x5C |
||||
#define USB_KEY_KP_5 0x5D |
||||
#define USB_KEY_KP_6 0x5E |
||||
#define USB_KEY_KP_7 0x5F |
||||
#define USB_KEY_KP_8 0x60 |
||||
#define USB_KEY_KP_9 0x61 |
||||
#define USB_KEY_KP_0 0x62 |
||||
#define USB_KEY_KP_DOT 0x63 |
||||
|
||||
#define USB_KEY_EURO2 0x64 |
||||
|
||||
//Special & media-keys
|
||||
#define USB_KEY_NEXT_TRACK 0x00B5 |
||||
#define USB_KEY_PREV_TRACK 0x00B6 |
||||
|
||||
#define USB_KEY_STOP 0x00B7 |
||||
#define USB_KEY_PLAY_PAUSE 0x00CD |
||||
#define USB_KEY_MUTE 0x00E2 |
||||
|
||||
#define USB_KEY_BASS_BOOST 0x00E5 |
||||
#define USB_KEY_LOUDNESS 0x00E7 |
||||
|
||||
#define USB_KEY_VOL_UP 0x00E9 |
||||
#define USB_KEY_VOL_DOWN 0x00EA |
||||
|
||||
#define USB_KEY_BASS_UP 0x0152 |
||||
#define USB_KEY_BASS_DOWN 0x0153 |
||||
|
||||
#define USB_KEY_TREBLE_UP 0x0154 |
||||
#define USB_KEY_TREBLE_DOWN 0x0155 |
||||
|
||||
#define USB_KEY_MEDIA_SELECT 0x0183 |
||||
|
||||
#define USB_KEY_MAIL 0x018A |
||||
#define USB_KEY_CALC 0x0192 |
||||
#define USB_KEY_PLACES 0x0194 |
||||
#define USB_KEY_MY_COMPUTER 0x0194 |
||||
|
||||
#define USB_KEY_WWW_SEARCH 0x0221 |
||||
#define USB_KEY_WWW_HOME 0x0223 |
||||
#define USB_KEY_WWW_BACK 0x0224 |
||||
#define USB_KEY_WWW_FORWARD 0x0225 |
||||
#define USB_KEY_WWW_STOP 0x0226 |
||||
#define USB_KEY_WWW_REFRESH 0x0227 |
||||
#define USB_KEY_WWW_BOOKMARKS 0x022A |
||||
|
||||
#endif //_USB_KEY_CODES_H***
|
||||
@ -0,0 +1,10 @@ |
||||
|
||||
#ifdef KBD_DE |
||||
#include "kbd_layouts/de.h" |
||||
#endif |
||||
|
||||
#if KBD_US |
||||
#include "kbd_layouts/us.h" |
||||
#endif |
||||
|
||||
|
||||
@ -0,0 +1,149 @@ |
||||
Version 2009-08-22 |
||||
|
||||
========================== |
||||
WHY DO WE NEED THESE IDs? |
||||
========================== |
||||
|
||||
USB is more than a low level protocol for data transport. It also defines a |
||||
common set of requests which must be understood by all devices. And as part |
||||
of these common requests, the specification defines data structures, the |
||||
USB Descriptors, which are used to describe the properties of the device. |
||||
|
||||
From the perspective of an operating system, it is therefore possible to find |
||||
out basic properties of a device (such as e.g. the manufacturer and the name |
||||
of the device) without a device-specific driver. This is essential because |
||||
the operating system can choose a driver to load based on this information |
||||
(Plug-And-Play). |
||||
|
||||
Among the most important properties in the Device Descriptor are the USB |
||||
Vendor- and Product-ID. Both are 16 bit integers. The most simple form of |
||||
driver matching is based on these IDs. The driver announces the Vendor- and |
||||
Product-IDs of the devices it can handle and the operating system loads the |
||||
appropriate driver when the device is connected. |
||||
|
||||
It is obvious that this technique only works if the pair Vendor- plus |
||||
Product-ID is unique: Only devices which require the same driver can have the |
||||
same pair of IDs. |
||||
|
||||
|
||||
===================================================== |
||||
HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? |
||||
===================================================== |
||||
|
||||
Since it is so important that USB IDs are unique, the USB Implementers Forum, |
||||
Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by |
||||
law to build a device and assign it any random numbers as IDs. Usb.org |
||||
therefore needs an agreement to regulate the use of USB IDs. The agreement |
||||
binds only parties who agreed to it, of course. Everybody else is free to use |
||||
any numbers for their IDs. |
||||
|
||||
So how can usb.org ensure that every manufacturer of USB devices enters into |
||||
an agreement with them? They do it via trademark licensing. Usb.org has |
||||
registered the trademark "USB", all associated logos and related terms. If |
||||
you want to put an USB logo on your product or claim that it is USB |
||||
compliant, you must license these trademarks from usb.org. And this is where |
||||
you enter into an agreement. See the "USB-IF Trademark License Agreement and |
||||
Usage Guidelines for the USB-IF Logo" at |
||||
http://www.usb.org/developers/logo_license/. |
||||
|
||||
Licensing the USB trademarks requires that you buy a USB Vendor-ID from |
||||
usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org |
||||
(yearly fee of ca. 4,000 USD) and that you meet all the technical |
||||
specifications from the USB spec. |
||||
|
||||
This means that most hobbyists and small companies will never be able to |
||||
become USB compliant, just because membership is so expensive. And you can't |
||||
be compliant with a driver based on V-USB anyway, because the AVR's port pins |
||||
don't meet the electrical specifications for USB. So, in principle, all |
||||
hobbyists and small companies are free to choose any random numbers for their |
||||
IDs. They have nothing to lose... |
||||
|
||||
There is one exception worth noting, though: If you use a sub-component which |
||||
implements USB, the vendor of the sub-components may guarantee USB |
||||
compliance. This might apply to some or all of FTDI's solutions. |
||||
|
||||
|
||||
======================================================================= |
||||
WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? |
||||
======================================================================= |
||||
|
||||
You have learned in the previous section that you are free to choose any |
||||
numbers for your IDs anyway. So why not do exactly this? There is still the |
||||
technical issue. If you choose IDs which are already in use by somebody else, |
||||
operating systems will load the wrong drivers and your device won't work. |
||||
Even if you choose IDs which are not currently in use, they may be in use in |
||||
the next version of the operating system or even after an automatic update. |
||||
|
||||
So what you need is a pair of Vendor- and Product-IDs for which you have the |
||||
guarantee that no USB compliant product uses them. This implies that no |
||||
operating system will ever ship with drivers responsible for these IDs. |
||||
|
||||
|
||||
============================================== |
||||
HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? |
||||
============================================== |
||||
|
||||
Objective Development gives away pairs of USB-IDs with their V-USB licenses. |
||||
In order to ensure that these IDs are unique, Objective Development has an |
||||
agreement with the company/person who has bought the USB Vendor-ID from |
||||
usb.org. This agreement ensures that a range of USB Product-IDs is reserved |
||||
for assignment by Objective Development and that the owner of the Vendor-ID |
||||
won't give it to anybody else. |
||||
|
||||
This means that you have to trust three parties to ensure uniqueness of |
||||
your IDs: |
||||
|
||||
- Objective Development, that they don't give the same PID to more than |
||||
one person. |
||||
- The owner of the Vendor-ID that they don't assign PIDs from the range |
||||
assigned to Objective Development to anybody else. |
||||
- Usb.org that they don't assign the same Vendor-ID a second time. |
||||
|
||||
|
||||
================================== |
||||
WHO IS THE OWNER OF THE VENDOR-ID? |
||||
================================== |
||||
|
||||
Objective Development has obtained ranges of USB Product-IDs under two |
||||
Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen |
||||
Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason |
||||
Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their |
||||
Vendor-ID directly from usb.org. |
||||
|
||||
|
||||
========================================================================= |
||||
CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? |
||||
========================================================================= |
||||
|
||||
The short answer is: Yes. All you get is a guarantee that the IDs are never |
||||
assigned to anybody else. What more do you need? |
||||
|
||||
|
||||
============================ |
||||
WHAT ABOUT SHARED ID PAIRS? |
||||
============================ |
||||
|
||||
Objective Development has reserved some PID/VID pairs for shared use. You |
||||
have no guarantee of uniqueness for them, except that no USB compliant device |
||||
uses them. In order to avoid technical problems, we must ensure that all |
||||
devices with the same pair of IDs use the same driver on kernel level. For |
||||
details, see the file USB-IDs-for-free.txt. |
||||
|
||||
|
||||
====================================================== |
||||
I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? |
||||
====================================================== |
||||
|
||||
A 16 bit integer number cannot be protected by copyright laws. It is not |
||||
sufficiently complex. And since none of the parties involved entered into the |
||||
USB-IF Trademark License Agreement, we are not bound by this agreement. So |
||||
there is no reason why it should be illegal to sub-license USB-IDs. |
||||
|
||||
|
||||
============================================= |
||||
WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? |
||||
============================================= |
||||
|
||||
Objective Development disclaims all liabilities which might arise from the |
||||
assignment of IDs. If you guarantee product features to your customers |
||||
without proper disclaimer, YOU are liable for that. |
||||
@ -0,0 +1,154 @@ |
||||
Version 2009-08-22 |
||||
|
||||
=========================== |
||||
FREE USB-IDs FOR SHARED USE |
||||
=========================== |
||||
|
||||
Objective Development has reserved a set of USB Product-IDs for use according |
||||
to the guidelines outlined below. For more information about the concept of |
||||
USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees |
||||
that the IDs listed below are not used by any USB compliant devices. |
||||
|
||||
|
||||
==================== |
||||
MECHANISM OF SHARING |
||||
==================== |
||||
|
||||
From a technical point of view, two different devices can share the same USB |
||||
Vendor- and Product-ID if they require the same driver on operating system |
||||
level. We make use of this fact by assigning separate IDs for various device |
||||
classes. On application layer, devices must be distinguished by their textual |
||||
name or serial number. We offer separate sets of IDs for discrimination by |
||||
textual name and for serial number. |
||||
|
||||
Examples for shared use of USB IDs are included with V-USB in the "examples" |
||||
subdirectory. |
||||
|
||||
|
||||
====================================== |
||||
IDs FOR DISCRIMINATION BY TEXTUAL NAME |
||||
====================================== |
||||
|
||||
If you use one of the IDs listed below, your device and host-side software |
||||
must conform to these rules: |
||||
|
||||
(1) The USB device MUST provide a textual representation of the manufacturer |
||||
and product identification. The manufacturer identification MUST be available |
||||
at least in USB language 0x0409 (English/US). |
||||
|
||||
(2) The textual manufacturer identification MUST contain either an Internet |
||||
domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail |
||||
address under your control (e.g. "myname@gmx.net"). You can embed the domain |
||||
name or e-mail address in any string you like, e.g. "Objective Development |
||||
http://www.obdev.at/vusb/". |
||||
|
||||
(3) You are responsible for retaining ownership of the domain or e-mail |
||||
address for as long as any of your products are in use. |
||||
|
||||
(4) You may choose any string for the textual product identification, as long |
||||
as this string is unique within the scope of your textual manufacturer |
||||
identification. |
||||
|
||||
(5) Application side device look-up MUST be based on the textual manufacturer |
||||
and product identification in addition to VID/PID matching. The driver |
||||
matching MUST be a comparison of the entire strings, NOT a sub-string match. |
||||
|
||||
(6) For devices which implement a particular USB device class (e.g. HID), the |
||||
operating system's default class driver MUST be used. If an operating system |
||||
driver for Vendor Class devices is needed, this driver must be libusb or |
||||
libusb-win32 (see http://libusb.org/ and |
||||
http://libusb-win32.sourceforge.net/). |
||||
|
||||
Table if IDs for discrimination by textual name: |
||||
|
||||
PID dec (hex) | VID dec (hex) | Description of use |
||||
==============+===============+============================================ |
||||
1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb |
||||
--------------+---------------+-------------------------------------------- |
||||
1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are |
||||
| | NOT mice, keyboards or joysticks) |
||||
--------------+---------------+-------------------------------------------- |
||||
1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) |
||||
--------------+---------------+-------------------------------------------- |
||||
1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices |
||||
--------------+---------------+-------------------------------------------- |
||||
|
||||
Note that Windows caches the textual product- and vendor-description for |
||||
mice, keyboards and joysticks. Name-bsed discrimination is therefore not |
||||
recommended for these device classes. |
||||
|
||||
|
||||
======================================= |
||||
IDs FOR DISCRIMINATION BY SERIAL NUMBER |
||||
======================================= |
||||
|
||||
If you use one of the IDs listed below, your device and host-side software |
||||
must conform to these rules: |
||||
|
||||
(1) The USB device MUST provide a textual representation of the serial |
||||
number, unless ONLY the operating system's default class driver is used. |
||||
The serial number string MUST be available at least in USB language 0x0409 |
||||
(English/US). |
||||
|
||||
(2) The serial number MUST start with either an Internet domain name (e.g. |
||||
"mycompany.com") registered and owned by you, or an e-mail address under your |
||||
control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. |
||||
You MAY append any string you like for further discrimination of your devices. |
||||
|
||||
(3) You are responsible for retaining ownership of the domain or e-mail |
||||
address for as long as any of your products are in use. |
||||
|
||||
(5) Application side device look-up MUST be based on the serial number string |
||||
in addition to VID/PID matching. The matching must start at the first |
||||
character of the serial number string and include the colon character |
||||
terminating your domain or e-mail address. It MAY stop anywhere after that. |
||||
|
||||
(6) For devices which implement a particular USB device class (e.g. HID), the |
||||
operating system's default class driver MUST be used. If an operating system |
||||
driver for Vendor Class devices is needed, this driver must be libusb or |
||||
libusb-win32 (see http://libusb.org/ and |
||||
http://libusb-win32.sourceforge.net/). |
||||
|
||||
(7) If ONLY the operating system's default class driver is used, e.g. for |
||||
mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an |
||||
application is needed, the serial number may be omitted. |
||||
|
||||
|
||||
Table if IDs for discrimination by serial number string: |
||||
|
||||
PID dec (hex) | VID dec (hex) | Description of use |
||||
===============+===============+=========================================== |
||||
10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb |
||||
---------------+---------------+------------------------------------------- |
||||
10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are |
||||
| | NOT mice, keyboards or joysticks) |
||||
---------------+---------------+------------------------------------------- |
||||
10202 (0x27da) | 5824 (0x16c0) | For USB Mice |
||||
---------------+---------------+------------------------------------------- |
||||
10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards |
||||
---------------+---------------+------------------------------------------- |
||||
10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks |
||||
---------------+---------------+------------------------------------------- |
||||
10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) |
||||
---------------+---------------+------------------------------------------- |
||||
10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices |
||||
---------------+---------------+------------------------------------------- |
||||
|
||||
|
||||
================= |
||||
ORIGIN OF USB-IDs |
||||
================= |
||||
|
||||
OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed |
||||
here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. |
||||
Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. |
||||
(see www.usb.org). The VID is registered for the company name "Van Ooijen |
||||
Technische Informatica". |
||||
|
||||
|
||||
========== |
||||
DISCLAIMER |
||||
========== |
||||
|
||||
OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any |
||||
problems which are caused by the shared use of these VID/PID pairs. |
||||
@ -0,0 +1,750 @@ |
||||
/* Name: usbdrvasm128.inc |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Christian Starkjohann |
||||
* Creation Date: 2008-10-11 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||
* appropriate implementation! |
||||
*/ |
||||
|
||||
/* |
||||
General Description: |
||||
This file is the 12.8 MHz version of the USB driver. It is intended for use |
||||
with the internal RC oscillator. Although 12.8 MHz is outside the guaranteed |
||||
calibration range of the oscillator, almost all AVRs can reach this frequency. |
||||
This version contains a phase locked loop in the receiver routine to cope with |
||||
slight clock rate deviations of up to +/- 1%. |
||||
|
||||
See usbdrv.h for a description of the entire driver. |
||||
|
||||
LIMITATIONS |
||||
=========== |
||||
Although it may seem very handy to save the crystal and use the internal |
||||
RC oscillator of the CPU, this method (and this module) has some serious |
||||
limitations: |
||||
(1) The guaranteed calibration range of the oscillator is only 8.1 MHz. |
||||
They typical range is 14.5 MHz and most AVRs can actually reach this rate. |
||||
(2) Writing EEPROM and Flash may be unreliable (short data lifetime) since |
||||
the write procedure is timed from the RC oscillator. |
||||
(3) End Of Packet detection (SE0) should be in bit 1, bit it is only checked |
||||
if bits 0 and 1 both read as 0 on D- and D+ read as 0 in the middle. This may |
||||
cause problems with old hubs which delay SE0 by up to one cycle. |
||||
(4) Code size is much larger than that of the other modules. |
||||
|
||||
Since almost all of this code is timing critical, don't change unless you |
||||
really know what you are doing! Many parts require not only a maximum number |
||||
of CPU cycles, but even an exact number of cycles! |
||||
|
||||
Implementation notes: |
||||
====================== |
||||
min frequency: 67 cycles for 8 bit -> 12.5625 MHz |
||||
max frequency: 69.286 cycles for 8 bit -> 12.99 MHz |
||||
nominal frequency: 12.77 MHz ( = sqrt(min * max)) |
||||
|
||||
sampling positions: (next even number in range [+/- 0.5]) |
||||
cycle index range: 0 ... 66 |
||||
bits: |
||||
.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 |
||||
[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] |
||||
|
||||
bit number: 0 1 2 3 4 5 6 7 |
||||
spare cycles 1 2 1 2 1 1 1 0 |
||||
|
||||
operations to perform: duration cycle |
||||
---------------- |
||||
eor fix, shift 1 -> 00 |
||||
andi phase, USBMASK 1 -> 08 |
||||
breq se0 1 -> 16 (moved to 11) |
||||
st y+, data 2 -> 24, 25 |
||||
mov data, fix 1 -> 33 |
||||
ser data 1 -> 41 |
||||
subi cnt, 1 1 -> 49 |
||||
brcs overflow 1 -> 50 |
||||
|
||||
layout of samples and operations: |
||||
[##] = sample bit |
||||
<##> = sample phase |
||||
*##* = operation |
||||
|
||||
0: *00* [01] 02 03 04 <05> 06 07 |
||||
1: *08* [09] 10 11 12 <13> 14 15 *16* |
||||
2: [17] 18 19 20 <21> 22 23 |
||||
3: *24* *25* [26] 27 28 29 <30> 31 32 |
||||
4: *33* [34] 35 36 37 <38> 39 40 |
||||
5: *41* [42] 43 44 45 <46> 47 48 |
||||
6: *49* *50* [51] 52 53 54 <55> 56 57 58 |
||||
7: [59] 60 61 62 <63> 64 65 66 |
||||
*****************************************************************************/ |
||||
|
||||
/* we prefer positive expressions (do if condition) instead of negative |
||||
* (skip if condition), therefore use defines for skip instructions: |
||||
*/ |
||||
#define ifioclr sbis |
||||
#define ifioset sbic |
||||
#define ifrclr sbrs |
||||
#define ifrset sbrc |
||||
|
||||
/* The registers "fix" and "data" swap their meaning during the loop. Use |
||||
* defines to keep their name constant. |
||||
*/ |
||||
#define fix x2 |
||||
#define data x1 |
||||
#undef phase /* phase has a default definition to x4 */ |
||||
#define phase x3 |
||||
|
||||
|
||||
USB_INTR_VECTOR: |
||||
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 |
||||
push YL ;2 push only what is necessary to sync with edge ASAP |
||||
in YL, SREG ;1 |
||||
push YL ;2 |
||||
;---------------------------------------------------------------------------- |
||||
; Synchronize with sync pattern: |
||||
;---------------------------------------------------------------------------- |
||||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||
;waitForJ, ensure that this prerequisite is met. |
||||
waitForJ: |
||||
inc YL |
||||
sbis USBIN, USBMINUS |
||||
brne waitForJ ; just make sure we have ANY timeout |
||||
waitForK: |
||||
;The following code results in a sampling window of 1/4 bit which meets the spec. |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS ;[0] |
||||
rjmp foundK ;[1] |
||||
#if USB_COUNT_SOF |
||||
lds YL, usbSofCount |
||||
inc YL |
||||
sts usbSofCount, YL |
||||
#endif /* USB_COUNT_SOF */ |
||||
#ifdef USB_SOF_HOOK |
||||
USB_SOF_HOOK |
||||
#endif |
||||
rjmp sofError |
||||
|
||||
foundK: |
||||
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] |
||||
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||
;are cycles from center of first sync (double K) bit after the instruction |
||||
push YH ;[2] |
||||
lds YL, usbInputBufOffset;[4] |
||||
clr YH ;[6] |
||||
subi YL, lo8(-(usbRxBuf));[7] |
||||
sbci YH, hi8(-(usbRxBuf));[8] |
||||
|
||||
sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] |
||||
rjmp haveTwoBitsK ;[10] |
||||
pop YH ;[11] undo the push from before |
||||
rjmp waitForK ;[13] this was not the end of sync, retry |
||||
haveTwoBitsK: |
||||
;---------------------------------------------------------------------------- |
||||
; push more registers and initialize values while we sample the first bits: |
||||
;---------------------------------------------------------------------------- |
||||
#define fix x2 |
||||
#define data x1 |
||||
|
||||
push shift ;[12] |
||||
push x1 ;[14] |
||||
push x2 ;[16] |
||||
ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 |
||||
ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] |
||||
ori shift, 1<<0 ;[02] |
||||
push x3 ;[03] |
||||
push cnt ;[05] |
||||
push r0 ;[07] |
||||
ifioset USBIN, USBMINUS ;[09] <--- bit 1 |
||||
ori shift, 1<<1 ;[10] |
||||
ser fix ;[11] |
||||
ldi cnt, USB_BUFSIZE ;[12] |
||||
mov data, shift ;[13] |
||||
lsl shift ;[14] |
||||
nop2 ;[15] |
||||
ifioset USBIN, USBMINUS ;[17] <--- bit 2 |
||||
ori data, 3<<2 ;[18] store in bit 2 AND bit 3 |
||||
eor shift, data ;[19] do nrzi decoding |
||||
andi data, 1<<3 ;[20] |
||||
in phase, USBIN ;[21] <- phase |
||||
brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 |
||||
nop ;[23] |
||||
rjmp entryAfterClr ;[24] |
||||
jumpToEntryAfterSet: |
||||
rjmp entryAfterSet ;[24] |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||
;---------------------------------------------------------------------------- |
||||
#undef fix |
||||
#define fix x1 |
||||
#undef data |
||||
#define data x2 |
||||
|
||||
bit7IsSet: |
||||
ifrclr phase, USBMINUS ;[62] check phase only if D- changed |
||||
lpm ;[63] |
||||
in phase, USBIN ;[64] <- phase (one cycle too late) |
||||
ori shift, 1 << 7 ;[65] |
||||
nop ;[66] |
||||
;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump |
||||
bit0AfterSet: |
||||
eor fix, shift ;[00] |
||||
#undef fix |
||||
#define fix x2 |
||||
#undef data |
||||
#define data x1 /* we now have result in data, fix is reset to 0xff */ |
||||
ifioclr USBIN, USBMINUS ;[01] <--- sample 0 |
||||
rjmp bit0IsClr ;[02] |
||||
andi shift, ~(7 << 0) ;[03] |
||||
breq unstuff0s ;[04] |
||||
in phase, USBIN ;[05] <- phase |
||||
rjmp bit1AfterSet ;[06] |
||||
unstuff0s: |
||||
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 0) ;[07] |
||||
ifioclr USBIN, USBMINUS ;[00] |
||||
ifioset USBIN, USBPLUS ;[01] |
||||
rjmp bit0IsClr ;[02] executed if first expr false or second true |
||||
se0AndStore: ; executed only if both bits 0 |
||||
st y+, x1 ;[15/17] cycles after start of byte |
||||
rjmp se0 ;[17/19] |
||||
|
||||
bit0IsClr: |
||||
ifrset phase, USBMINUS ;[04] check phase only if D- changed |
||||
lpm ;[05] |
||||
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||
ori shift, 1 << 0 ;[07] |
||||
bit1AfterClr: |
||||
andi phase, USBMASK ;[08] |
||||
ifioset USBIN, USBMINUS ;[09] <--- sample 1 |
||||
rjmp bit1IsSet ;[10] |
||||
breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 |
||||
andi shift, ~(7 << 1) ;[12] |
||||
in phase, USBIN ;[13] <- phase |
||||
breq unstuff1c ;[14] |
||||
rjmp bit2AfterClr ;[15] |
||||
unstuff1c: |
||||
andi fix, ~(1 << 1) ;[16] |
||||
nop2 ;[08] |
||||
nop2 ;[10] |
||||
bit1IsSet: |
||||
ifrclr phase, USBMINUS ;[12] check phase only if D- changed |
||||
lpm ;[13] |
||||
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||
ori shift, 1 << 1 ;[15] |
||||
nop ;[16] |
||||
bit2AfterSet: |
||||
ifioclr USBIN, USBMINUS ;[17] <--- sample 2 |
||||
rjmp bit2IsClr ;[18] |
||||
andi shift, ~(7 << 2) ;[19] |
||||
breq unstuff2s ;[20] |
||||
in phase, USBIN ;[21] <- phase |
||||
rjmp bit3AfterSet ;[22] |
||||
unstuff2s: |
||||
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 2) ;[23] |
||||
nop2 ;[16] |
||||
nop2 ;[18] |
||||
bit2IsClr: |
||||
ifrset phase, USBMINUS ;[20] check phase only if D- changed |
||||
lpm ;[21] |
||||
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||
ori shift, 1 << 2 ;[23] |
||||
bit3AfterClr: |
||||
st y+, data ;[24] |
||||
entryAfterClr: |
||||
ifioset USBIN, USBMINUS ;[26] <--- sample 3 |
||||
rjmp bit3IsSet ;[27] |
||||
andi shift, ~(7 << 3) ;[28] |
||||
breq unstuff3c ;[29] |
||||
in phase, USBIN ;[30] <- phase |
||||
rjmp bit4AfterClr ;[31] |
||||
unstuff3c: |
||||
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 3) ;[32] |
||||
nop2 ;[25] |
||||
nop2 ;[27] |
||||
bit3IsSet: |
||||
ifrclr phase, USBMINUS ;[29] check phase only if D- changed |
||||
lpm ;[30] |
||||
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||
ori shift, 1 << 3 ;[32] |
||||
bit4AfterSet: |
||||
mov data, fix ;[33] undo this move by swapping defines |
||||
#undef fix |
||||
#define fix x1 |
||||
#undef data |
||||
#define data x2 |
||||
ifioclr USBIN, USBMINUS ;[34] <--- sample 4 |
||||
rjmp bit4IsClr ;[35] |
||||
andi shift, ~(7 << 4) ;[36] |
||||
breq unstuff4s ;[37] |
||||
in phase, USBIN ;[38] <- phase |
||||
rjmp bit5AfterSet ;[39] |
||||
unstuff4s: |
||||
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 4) ;[40] |
||||
nop2 ;[33] |
||||
nop2 ;[35] |
||||
bit4IsClr: |
||||
ifrset phase, USBMINUS ;[37] check phase only if D- changed |
||||
lpm ;[38] |
||||
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||
ori shift, 1 << 4 ;[40] |
||||
bit5AfterClr: |
||||
ser data ;[41] |
||||
ifioset USBIN, USBMINUS ;[42] <--- sample 5 |
||||
rjmp bit5IsSet ;[43] |
||||
andi shift, ~(7 << 5) ;[44] |
||||
breq unstuff5c ;[45] |
||||
in phase, USBIN ;[46] <- phase |
||||
rjmp bit6AfterClr ;[47] |
||||
unstuff5c: |
||||
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 5) ;[48] |
||||
nop2 ;[41] |
||||
nop2 ;[43] |
||||
bit5IsSet: |
||||
ifrclr phase, USBMINUS ;[45] check phase only if D- changed |
||||
lpm ;[46] |
||||
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||
ori shift, 1 << 5 ;[48] |
||||
bit6AfterSet: |
||||
subi cnt, 1 ;[49] |
||||
brcs jumpToOverflow ;[50] |
||||
ifioclr USBIN, USBMINUS ;[51] <--- sample 6 |
||||
rjmp bit6IsClr ;[52] |
||||
andi shift, ~(3 << 6) ;[53] |
||||
cpi shift, 2 ;[54] |
||||
in phase, USBIN ;[55] <- phase |
||||
brlt unstuff6s ;[56] |
||||
rjmp bit7AfterSet ;[57] |
||||
|
||||
jumpToOverflow: |
||||
rjmp overflow |
||||
|
||||
unstuff6s: |
||||
andi fix, ~(1 << 6) ;[50] |
||||
lpm ;[51] |
||||
bit6IsClr: |
||||
ifrset phase, USBMINUS ;[54] check phase only if D- changed |
||||
lpm ;[55] |
||||
in phase, USBIN ;[56] <- phase (one cycle too late) |
||||
ori shift, 1 << 6 ;[57] |
||||
nop ;[58] |
||||
bit7AfterClr: |
||||
ifioset USBIN, USBMINUS ;[59] <--- sample 7 |
||||
rjmp bit7IsSet ;[60] |
||||
andi shift, ~(1 << 7) ;[61] |
||||
cpi shift, 4 ;[62] |
||||
in phase, USBIN ;[63] <- phase |
||||
brlt unstuff7c ;[64] |
||||
rjmp bit0AfterClr ;[65] -> [00] == [67] |
||||
unstuff7c: |
||||
andi fix, ~(1 << 7) ;[58] |
||||
nop ;[59] |
||||
rjmp bit7IsSet ;[60] |
||||
|
||||
bit7IsClr: |
||||
ifrset phase, USBMINUS ;[62] check phase only if D- changed |
||||
lpm ;[63] |
||||
in phase, USBIN ;[64] <- phase (one cycle too late) |
||||
ori shift, 1 << 7 ;[65] |
||||
nop ;[66] |
||||
;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump |
||||
bit0AfterClr: |
||||
eor fix, shift ;[00] |
||||
#undef fix |
||||
#define fix x2 |
||||
#undef data |
||||
#define data x1 /* we now have result in data, fix is reset to 0xff */ |
||||
ifioset USBIN, USBMINUS ;[01] <--- sample 0 |
||||
rjmp bit0IsSet ;[02] |
||||
andi shift, ~(7 << 0) ;[03] |
||||
breq unstuff0c ;[04] |
||||
in phase, USBIN ;[05] <- phase |
||||
rjmp bit1AfterClr ;[06] |
||||
unstuff0c: |
||||
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 0) ;[07] |
||||
ifioclr USBIN, USBMINUS ;[00] |
||||
ifioset USBIN, USBPLUS ;[01] |
||||
rjmp bit0IsSet ;[02] executed if first expr false or second true |
||||
rjmp se0AndStore ;[03] executed only if both bits 0 |
||||
bit0IsSet: |
||||
ifrclr phase, USBMINUS ;[04] check phase only if D- changed |
||||
lpm ;[05] |
||||
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||
ori shift, 1 << 0 ;[07] |
||||
bit1AfterSet: |
||||
andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr |
||||
ifioclr USBIN, USBMINUS ;[09] <--- sample 1 |
||||
rjmp bit1IsClr ;[10] |
||||
breq unstuff1s ;[11] |
||||
nop2 ;[12] do not check for SE0 if bit 0 was 1 |
||||
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||
rjmp bit2AfterSet ;[15] |
||||
unstuff1s: |
||||
in phase, USBIN ;[13] <- phase |
||||
andi fix, ~(1 << 1) ;[14] |
||||
lpm ;[07] |
||||
nop2 ;[10] |
||||
bit1IsClr: |
||||
ifrset phase, USBMINUS ;[12] check phase only if D- changed |
||||
lpm ;[13] |
||||
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||
ori shift, 1 << 1 ;[15] |
||||
nop ;[16] |
||||
bit2AfterClr: |
||||
ifioset USBIN, USBMINUS ;[17] <--- sample 2 |
||||
rjmp bit2IsSet ;[18] |
||||
andi shift, ~(7 << 2) ;[19] |
||||
breq unstuff2c ;[20] |
||||
in phase, USBIN ;[21] <- phase |
||||
rjmp bit3AfterClr ;[22] |
||||
unstuff2c: |
||||
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 2) ;[23] |
||||
nop2 ;[16] |
||||
nop2 ;[18] |
||||
bit2IsSet: |
||||
ifrclr phase, USBMINUS ;[20] check phase only if D- changed |
||||
lpm ;[21] |
||||
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||
ori shift, 1 << 2 ;[23] |
||||
bit3AfterSet: |
||||
st y+, data ;[24] |
||||
entryAfterSet: |
||||
ifioclr USBIN, USBMINUS ;[26] <--- sample 3 |
||||
rjmp bit3IsClr ;[27] |
||||
andi shift, ~(7 << 3) ;[28] |
||||
breq unstuff3s ;[29] |
||||
in phase, USBIN ;[30] <- phase |
||||
rjmp bit4AfterSet ;[31] |
||||
unstuff3s: |
||||
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 3) ;[32] |
||||
nop2 ;[25] |
||||
nop2 ;[27] |
||||
bit3IsClr: |
||||
ifrset phase, USBMINUS ;[29] check phase only if D- changed |
||||
lpm ;[30] |
||||
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||
ori shift, 1 << 3 ;[32] |
||||
bit4AfterClr: |
||||
mov data, fix ;[33] undo this move by swapping defines |
||||
#undef fix |
||||
#define fix x1 |
||||
#undef data |
||||
#define data x2 |
||||
ifioset USBIN, USBMINUS ;[34] <--- sample 4 |
||||
rjmp bit4IsSet ;[35] |
||||
andi shift, ~(7 << 4) ;[36] |
||||
breq unstuff4c ;[37] |
||||
in phase, USBIN ;[38] <- phase |
||||
rjmp bit5AfterClr ;[39] |
||||
unstuff4c: |
||||
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 4) ;[40] |
||||
nop2 ;[33] |
||||
nop2 ;[35] |
||||
bit4IsSet: |
||||
ifrclr phase, USBMINUS ;[37] check phase only if D- changed |
||||
lpm ;[38] |
||||
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||
ori shift, 1 << 4 ;[40] |
||||
bit5AfterSet: |
||||
ser data ;[41] |
||||
ifioclr USBIN, USBMINUS ;[42] <--- sample 5 |
||||
rjmp bit5IsClr ;[43] |
||||
andi shift, ~(7 << 5) ;[44] |
||||
breq unstuff5s ;[45] |
||||
in phase, USBIN ;[46] <- phase |
||||
rjmp bit6AfterSet ;[47] |
||||
unstuff5s: |
||||
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||
andi fix, ~(1 << 5) ;[48] |
||||
nop2 ;[41] |
||||
nop2 ;[43] |
||||
bit5IsClr: |
||||
ifrset phase, USBMINUS ;[45] check phase only if D- changed |
||||
lpm ;[46] |
||||
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||
ori shift, 1 << 5 ;[48] |
||||
bit6AfterClr: |
||||
subi cnt, 1 ;[49] |
||||
brcs overflow ;[50] |
||||
ifioset USBIN, USBMINUS ;[51] <--- sample 6 |
||||
rjmp bit6IsSet ;[52] |
||||
andi shift, ~(3 << 6) ;[53] |
||||
cpi shift, 2 ;[54] |
||||
in phase, USBIN ;[55] <- phase |
||||
brlt unstuff6c ;[56] |
||||
rjmp bit7AfterClr ;[57] |
||||
unstuff6c: |
||||
andi fix, ~(1 << 6) ;[50] |
||||
lpm ;[51] |
||||
bit6IsSet: |
||||
ifrclr phase, USBMINUS ;[54] check phase only if D- changed |
||||
lpm ;[55] |
||||
in phase, USBIN ;[56] <- phase (one cycle too late) |
||||
ori shift, 1 << 6 ;[57] |
||||
bit7AfterSet: |
||||
ifioclr USBIN, USBMINUS ;[59] <--- sample 7 |
||||
rjmp bit7IsClr ;[60] |
||||
andi shift, ~(1 << 7) ;[61] |
||||
cpi shift, 4 ;[62] |
||||
in phase, USBIN ;[63] <- phase |
||||
brlt unstuff7s ;[64] |
||||
rjmp bit0AfterSet ;[65] -> [00] == [67] |
||||
unstuff7s: |
||||
andi fix, ~(1 << 7) ;[58] |
||||
nop ;[59] |
||||
rjmp bit7IsClr ;[60] |
||||
|
||||
macro POP_STANDARD ; 14 cycles |
||||
pop r0 |
||||
pop cnt |
||||
pop x3 |
||||
pop x2 |
||||
pop x1 |
||||
pop shift |
||||
pop YH |
||||
endm |
||||
macro POP_RETI ; 5 cycles |
||||
pop YL |
||||
out SREG, YL |
||||
pop YL |
||||
endm |
||||
|
||||
#include "asmcommon.inc" |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Transmitting data |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
txByteLoop: |
||||
txBitloop: |
||||
stuffN1Delay: ; [03] |
||||
ror shift ;[-5] [11] [63] |
||||
brcc doExorN1 ;[-4] [64] |
||||
subi x3, 1 ;[-3] |
||||
brne commonN1 ;[-2] |
||||
lsl shift ;[-1] compensate ror after rjmp stuffDelay |
||||
nop ;[00] stuffing consists of just waiting 8 cycles |
||||
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear |
||||
|
||||
sendNakAndReti: |
||||
ldi cnt, USBPID_NAK ;[-19] |
||||
rjmp sendCntAndReti ;[-18] |
||||
sendAckAndReti: |
||||
ldi cnt, USBPID_ACK ;[-17] |
||||
sendCntAndReti: |
||||
mov r0, cnt ;[-16] |
||||
ldi YL, 0 ;[-15] R0 address is 0 |
||||
ldi YH, 0 ;[-14] |
||||
ldi cnt, 2 ;[-13] |
||||
; rjmp usbSendAndReti fallthrough |
||||
|
||||
; USB spec says: |
||||
; idle = J |
||||
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 |
||||
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 |
||||
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) |
||||
|
||||
;usbSend: |
||||
;pointer to data in 'Y' |
||||
;number of bytes in 'cnt' -- including sync byte |
||||
;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] |
||||
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) |
||||
usbSendAndReti: |
||||
in x2, USBDDR ;[-10] 10 cycles until SOP |
||||
ori x2, USBMASK ;[-9] |
||||
sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||
out USBDDR, x2 ;[-6] <--- acquire bus |
||||
in x1, USBOUT ;[-5] port mirror for tx loop |
||||
ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) |
||||
ldi x2, USBMASK ;[-3] |
||||
doExorN1: |
||||
eor x1, x2 ;[-2] [06] [62] |
||||
ldi x3, 6 ;[-1] [07] [63] |
||||
commonN1: |
||||
stuffN2Delay: |
||||
out USBOUT, x1 ;[00] [08] [64] <--- set bit |
||||
ror shift ;[01] |
||||
brcc doExorN2 ;[02] |
||||
subi x3, 1 ;[03] |
||||
brne commonN2 ;[04] |
||||
lsl shift ;[05] compensate ror after rjmp stuffDelay |
||||
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear |
||||
doExorN2: |
||||
eor x1, x2 ;[04] [12] |
||||
ldi x3, 6 ;[05] [13] |
||||
commonN2: |
||||
nop2 ;[06] [14] |
||||
subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 |
||||
out USBOUT, x1 ;[09] [17] <--- set bit |
||||
brcs txBitloop ;[10] [27] [44] |
||||
|
||||
stuff6Delay: |
||||
ror shift ;[45] [53] |
||||
brcc doExor6 ;[46] |
||||
subi x3, 1 ;[47] |
||||
brne common6 ;[48] |
||||
lsl shift ;[49] compensate ror after rjmp stuffDelay |
||||
nop ;[50] stuffing consists of just waiting 8 cycles |
||||
rjmp stuff6Delay ;[51] after ror, C bit is reliably clear |
||||
doExor6: |
||||
eor x1, x2 ;[48] [56] |
||||
ldi x3, 6 ;[49] |
||||
common6: |
||||
stuff7Delay: |
||||
ror shift ;[50] [58] |
||||
out USBOUT, x1 ;[51] <--- set bit |
||||
brcc doExor7 ;[52] |
||||
subi x3, 1 ;[53] |
||||
brne common7 ;[54] |
||||
lsl shift ;[55] compensate ror after rjmp stuffDelay |
||||
rjmp stuff7Delay ;[56] after ror, C bit is reliably clear |
||||
doExor7: |
||||
eor x1, x2 ;[54] [62] |
||||
ldi x3, 6 ;[55] |
||||
common7: |
||||
ld shift, y+ ;[56] |
||||
nop ;[58] |
||||
tst cnt ;[59] |
||||
out USBOUT, x1 ;[60] [00]<--- set bit |
||||
brne txByteLoop ;[61] [01] |
||||
;make SE0: |
||||
cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] |
||||
lds x2, usbNewDeviceAddr;[03] |
||||
lsl x2 ;[05] we compare with left shifted address |
||||
subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 |
||||
sbci YH, 0 ;[07] |
||||
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle |
||||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||
;set address only after data packet was sent, not after handshake |
||||
breq skipAddrAssign ;[01] |
||||
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer |
||||
skipAddrAssign: |
||||
;end of usbDeviceAddress transfer |
||||
ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[04] |
||||
ori x1, USBIDLE ;[05] |
||||
in x2, USBDDR ;[06] |
||||
cbr x2, USBMASK ;[07] set both pins to input |
||||
mov x3, x1 ;[08] |
||||
cbr x3, USBMASK ;[09] configure no pullup on both pins |
||||
lpm ;[10] |
||||
lpm ;[13] |
||||
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[17] <-- release bus now |
||||
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
|
||||
|
||||
|
||||
/***************************************************************************** |
||||
The following PHP script generates a code skeleton for the receiver routine: |
||||
|
||||
<?php |
||||
|
||||
function printCmdBuffer($thisBit) |
||||
{ |
||||
global $cycle; |
||||
|
||||
$nextBit = ($thisBit + 1) % 8; |
||||
$s = ob_get_contents(); |
||||
ob_end_clean(); |
||||
$s = str_replace("#", $thisBit, $s); |
||||
$s = str_replace("@", $nextBit, $s); |
||||
$lines = explode("\n", $s); |
||||
for($i = 0; $i < count($lines); $i++){ |
||||
$s = $lines[$i]; |
||||
if(ereg("\\[([0-9-][0-9])\\]", $s, $regs)){ |
||||
$c = $cycle + (int)$regs[1]; |
||||
$s = ereg_replace("\\[[0-9-][0-9]\\]", sprintf("[%02d]", $c), $s); |
||||
} |
||||
if(strlen($s) > 0) |
||||
echo "$s\n"; |
||||
} |
||||
} |
||||
|
||||
function printBit($isAfterSet, $bitNum) |
||||
{ |
||||
ob_start(); |
||||
if($isAfterSet){ |
||||
?> |
||||
ifioclr USBIN, USBMINUS ;[00] <--- sample |
||||
rjmp bit#IsClr ;[01] |
||||
andi shift, ~(7 << #) ;[02] |
||||
breq unstuff#s ;[03] |
||||
in phase, USBIN ;[04] <- phase |
||||
rjmp bit@AfterSet ;[05] |
||||
unstuff#s: |
||||
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||
andi fix, ~(1 << #) ;[06] |
||||
nop2 ;[-1] |
||||
nop2 ;[01] |
||||
bit#IsClr: |
||||
ifrset phase, USBMINUS ;[03] check phase only if D- changed |
||||
lpm ;[04] |
||||
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||
ori shift, 1 << # ;[06] |
||||
<?php |
||||
}else{ |
||||
?> |
||||
ifioset USBIN, USBMINUS ;[00] <--- sample |
||||
rjmp bit#IsSet ;[01] |
||||
andi shift, ~(7 << #) ;[02] |
||||
breq unstuff#c ;[03] |
||||
in phase, USBIN ;[04] <- phase |
||||
rjmp bit@AfterClr ;[05] |
||||
unstuff#c: |
||||
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||
andi fix, ~(1 << #) ;[06] |
||||
nop2 ;[-1] |
||||
nop2 ;[01] |
||||
bit#IsSet: |
||||
ifrclr phase, USBMINUS ;[03] check phase only if D- changed |
||||
lpm ;[04] |
||||
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||
ori shift, 1 << # ;[06] |
||||
<?php |
||||
} |
||||
printCmdBuffer($bitNum); |
||||
} |
||||
|
||||
$bitStartCycles = array(1, 9, 17, 26, 34, 42, 51, 59); |
||||
for($i = 0; $i < 16; $i++){ |
||||
$bit = $i % 8; |
||||
$emitClrCode = ($i + (int)($i / 8)) % 2; |
||||
$cycle = $bitStartCycles[$bit]; |
||||
if($emitClrCode){ |
||||
printf("bit%dAfterClr:\n", $bit); |
||||
}else{ |
||||
printf("bit%dAfterSet:\n", $bit); |
||||
} |
||||
ob_start(); |
||||
echo " ***** ;[-1]\n"; |
||||
printCmdBuffer($bit); |
||||
printBit(!$emitClrCode, $bit); |
||||
if($i == 7) |
||||
echo "\n"; |
||||
} |
||||
|
||||
?> |
||||
*****************************************************************************/ |
||||
@ -0,0 +1,707 @@ |
||||
/* Name: usbdrvasm18.inc |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Lukas Schrittwieser (based on 20 MHz usbdrvasm20.inc by Jeroen Benschop) |
||||
* Creation Date: 2009-01-20 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by Lukas Schrittwieser and OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* Revision: $Id$ |
||||
*/ |
||||
|
||||
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||
* appropriate implementation! |
||||
*/ |
||||
|
||||
/* |
||||
General Description: |
||||
This file is the 18 MHz version of the asssembler part of the USB driver. It |
||||
requires a 18 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||
oscillator). |
||||
|
||||
See usbdrv.h for a description of the entire driver. |
||||
|
||||
Since almost all of this code is timing critical, don't change unless you |
||||
really know what you are doing! Many parts require not only a maximum number |
||||
of CPU cycles, but even an exact number of cycles! |
||||
*/ |
||||
|
||||
|
||||
;max stack usage: [ret(2), YL, SREG, YH, [sofError], bitcnt(x5), shift, x1, x2, x3, x4, cnt, ZL, ZH] = 14 bytes |
||||
;nominal frequency: 18 MHz -> 12 cycles per bit |
||||
; Numbers in brackets are clocks counted from center of last sync bit |
||||
; when instruction starts |
||||
;register use in receive loop to receive the data bytes: |
||||
; shift assembles the byte currently being received |
||||
; x1 holds the D+ and D- line state |
||||
; x2 holds the previous line state |
||||
; cnt holds the number of bytes left in the receive buffer |
||||
; x3 holds the higher crc byte (see algorithm below) |
||||
; x4 is used as temporary register for the crc algorithm |
||||
; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further |
||||
; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted |
||||
; zl lower crc value and crc table index |
||||
; zh used for crc table accesses |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; CRC mods: |
||||
; table driven crc checker, Z points to table in prog space |
||||
; ZL is the lower crc byte, x3 is the higher crc byte |
||||
; x4 is used as temp register to store different results |
||||
; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the |
||||
; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial |
||||
; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. |
||||
; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and |
||||
; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF |
||||
; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; CRC algorithm: |
||||
; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form |
||||
; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order |
||||
; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i |
||||
; propose a research on CRC :-) ) |
||||
; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc |
||||
; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 |
||||
; (its destination). |
||||
; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as |
||||
; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored |
||||
; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc |
||||
; calculation is done. |
||||
; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) |
||||
; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized |
||||
; to a magic number which results in a crc value of 0xFFFF after the first complete byte. |
||||
; |
||||
; This algorithm is split into the extra cycles of the different bits: |
||||
; bit7: XOR the received byte to ZL |
||||
; bit5: load the new high byte to x4 |
||||
; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) |
||||
; move x4 (the new high byte) to x3, the crc value is ready |
||||
; |
||||
|
||||
|
||||
macro POP_STANDARD ; 18 cycles |
||||
pop ZH |
||||
pop ZL |
||||
pop cnt |
||||
pop x5 |
||||
pop x3 |
||||
pop x2 |
||||
pop x1 |
||||
pop shift |
||||
pop x4 |
||||
endm |
||||
macro POP_RETI ; 7 cycles |
||||
pop YH |
||||
pop YL |
||||
out SREG, YL |
||||
pop YL |
||||
endm |
||||
|
||||
macro CRC_CLEANUP_AND_CHECK |
||||
; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor |
||||
; x3 is the higher crc byte, zl the lower one |
||||
ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table |
||||
lpm x2, Z ;[+2][+3][+4] |
||||
ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table |
||||
lpm ZL, Z ;[+6][+7][+8] |
||||
eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value |
||||
cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) |
||||
brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host |
||||
cpi x2, 0xb0 ;[+10] |
||||
brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host |
||||
endm |
||||
|
||||
|
||||
USB_INTR_VECTOR: |
||||
;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH |
||||
push YL ;[-28] push only what is necessary to sync with edge ASAP |
||||
in YL, SREG ;[-26] |
||||
push YL ;[-25] |
||||
push YH ;[-23] |
||||
;---------------------------------------------------------------------------- |
||||
; Synchronize with sync pattern: |
||||
;---------------------------------------------------------------------------- |
||||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||
;waitForJ, ensure that this prerequisite is met. |
||||
waitForJ: |
||||
inc YL |
||||
sbis USBIN, USBMINUS |
||||
brne waitForJ ; just make sure we have ANY timeout |
||||
waitForK: |
||||
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||
sbis USBIN, USBMINUS ;[-17] |
||||
rjmp foundK ;[-16] |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
#if USB_COUNT_SOF |
||||
lds YL, usbSofCount |
||||
inc YL |
||||
sts usbSofCount, YL |
||||
#endif /* USB_COUNT_SOF */ |
||||
#ifdef USB_SOF_HOOK |
||||
USB_SOF_HOOK |
||||
#endif |
||||
rjmp sofError |
||||
foundK: ;[-15] |
||||
;{3, 5} after falling D- edge, average delay: 4 cycles |
||||
;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample |
||||
;use 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||
;are cycles from center of first sync (double K) bit after the instruction |
||||
push x4 ;[-14] |
||||
; [---] ;[-13] |
||||
lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers |
||||
; [---] ;[-11] |
||||
clr YH ;[-10] |
||||
subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] |
||||
sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] |
||||
push shift ;[-7] |
||||
; [---] ;[-6] |
||||
ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop |
||||
clc ;[-4] the carry has to be clear for receipt of pid bit 0 |
||||
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) |
||||
rjmp haveTwoBitsK ;[-2] |
||||
pop shift ;[-1] undo the push from before |
||||
pop x4 ;[1] |
||||
rjmp waitForK ;[3] this was not the end of sync, retry |
||||
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||
; bit times (= 24 cycles). |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; push more registers and initialize values while we sample the first bits: |
||||
;---------------------------------------------------------------------------- |
||||
haveTwoBitsK: |
||||
push x1 ;[0] |
||||
push x2 ;[2] |
||||
push x3 ;[4] crc high byte |
||||
ldi x2, 1<<USBPLUS ;[6] [rx loop init] current line state is K state. D+=="1", D-=="0" |
||||
push x5 ;[7] |
||||
push cnt ;[9] |
||||
ldi cnt, USB_BUFSIZE ;[11] |
||||
|
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives the pid byte |
||||
; there is no real unstuffing algorithm implemented here as a stuffing bit is impossible in the pid byte. |
||||
; That's because the last four bits of the byte are the inverted of the first four bits. If we detect a |
||||
; unstuffing condition something went wrong and abort |
||||
; shift has to be initialized to 0x80 |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
; pid bit 0 - used for even more register saving (we need the z pointer) |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
eor x2, x1 ;[2] generate inverted of actual bit |
||||
sbrc x2, USBMINUS ;[3] if the bit is set we received a zero |
||||
sec ;[4] |
||||
ror shift ;[5] we perform no unstuffing check here as this is the first bit |
||||
mov x2, x1 ;[6] |
||||
push ZL ;[7] |
||||
;[8] |
||||
push ZH ;[9] |
||||
;[10] |
||||
ldi x3, 0xFE ;[11] x3 is the high order crc value |
||||
|
||||
|
||||
bitloopPid: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
breq nse0 ;[2] both lines are low so handle se0 |
||||
eor x2, x1 ;[3] generate inverted of actual bit |
||||
sbrc x2, USBMINUS ;[4] set the carry if we received a zero |
||||
sec ;[5] |
||||
ror shift ;[6] |
||||
ldi ZL, 0x54 ;[7] ZL is the low order crc value |
||||
ser x4 ;[8] the is no bit stuffing check here as the pid bit can't be stuffed. if so |
||||
; some error occured. In this case the paket is discarded later on anyway. |
||||
mov x2, x1 ;[9] prepare for the next cycle |
||||
brcc bitloopPid ;[10] while 0s drop out of shift we get the next bit |
||||
eor x4, shift ;[11] invert all bits in shift and store result in x4 |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives data bytes and calculates the crc |
||||
; the last USBIN state has to be in x2 |
||||
; this is only the first half, due to branch distanc limitations the second half of the loop is near the end |
||||
; of this asm file |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
rxDataStart: |
||||
in x1, USBIN ;[0] sample line state (note: a se0 check is not useful due to bit dribbling) |
||||
ser x5 ;[1] prepare the unstuff marker register |
||||
eor x2, x1 ;[2] generates the inverted of the actual bit |
||||
bst x2, USBMINUS ;[3] copy the bit from x2 |
||||
bld shift, 0 ;[4] and store it in shift |
||||
mov x2, shift ;[5] make a copy of shift for unstuffing check |
||||
andi x2, 0xF9 ;[6] mask the last six bits, if we got six zeros (which are six ones in fact) |
||||
breq unstuff0 ;[7] then Z is set now and we branch to the unstuffing handler |
||||
didunstuff0: |
||||
subi cnt, 1 ;[8] cannot use dec because it doesn't affect the carry flag |
||||
brcs nOverflow ;[9] Too many bytes received. Ignore packet |
||||
st Y+, x4 ;[10] store the last received byte |
||||
;[11] st needs two cycles |
||||
|
||||
; bit1 |
||||
in x2, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 during bit 0 |
||||
breq nse0 ;[2] |
||||
andi x2, USBMASK ;[3] check se0 during bit 1 |
||||
breq nse0 ;[4] |
||||
eor x1, x2 ;[5] |
||||
bst x1, USBMINUS ;[6] |
||||
bld shift, 1 ;[7] |
||||
mov x1, shift ;[8] |
||||
andi x1, 0xF3 ;[9] |
||||
breq unstuff1 ;[10] |
||||
didunstuff1: |
||||
nop ;[11] |
||||
|
||||
; bit2 |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 (as there is nothing else to do here |
||||
breq nOverflow ;[2] |
||||
eor x2, x1 ;[3] generates the inverted of the actual bit |
||||
bst x2, USBMINUS ;[4] |
||||
bld shift, 2 ;[5] store the bit |
||||
mov x2, shift ;[6] |
||||
andi x2, 0xE7 ;[7] if we have six zeros here (which means six 1 in the stream) |
||||
breq unstuff2 ;[8] the next bit is a stuffing bit |
||||
didunstuff2: |
||||
nop2 ;[9] |
||||
;[10] |
||||
nop ;[11] |
||||
|
||||
; bit3 |
||||
in x2, USBIN ;[0] sample line state |
||||
andi x2, USBMASK ;[1] check for se0 |
||||
breq nOverflow ;[2] |
||||
eor x1, x2 ;[3] |
||||
bst x1, USBMINUS ;[4] |
||||
bld shift, 3 ;[5] |
||||
mov x1, shift ;[6] |
||||
andi x1, 0xCF ;[7] |
||||
breq unstuff3 ;[8] |
||||
didunstuff3: |
||||
nop ;[9] |
||||
rjmp rxDataBit4 ;[10] |
||||
;[11] |
||||
|
||||
; the avr branch instructions allow an offset of +63 insturction only, so we need this |
||||
; 'local copy' of se0 |
||||
nse0: |
||||
rjmp se0 ;[4] |
||||
;[5] |
||||
; the same same as for se0 is needed for overflow and StuffErr |
||||
nOverflow: |
||||
stuffErr: |
||||
rjmp overflow |
||||
|
||||
|
||||
unstuff0: ;[8] this is the branch delay of breq unstuffX |
||||
andi x1, USBMASK ;[9] do an se0 check here (if the last crc byte ends with 5 one's we might end up here |
||||
breq didunstuff0 ;[10] event tough the message is complete -> jump back and store the byte |
||||
ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing |
||||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) |
||||
eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x1, USBMASK ;[3] mask the interesting bits |
||||
breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong |
||||
mov x1, x2 ;[5] the next bit expects the last state to be in x1 |
||||
rjmp didunstuff0 ;[6] |
||||
;[7] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff1: ;[11] this is the jump delay of breq unstuffX |
||||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) |
||||
eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x2, USBMASK ;[4] mask the interesting bits |
||||
breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong |
||||
mov x2, x1 ;[6] the next bit expects the last state to be in x2 |
||||
nop2 ;[7] |
||||
;[8] |
||||
rjmp didunstuff1 ;[9] |
||||
;[10] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff2: ;[9] this is the jump delay of breq unstuffX |
||||
ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x1, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||
nop2 ;[5] |
||||
;[6] |
||||
rjmp didunstuff2 ;[7] |
||||
;[8] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff3: ;[9] this is the jump delay of breq unstuffX |
||||
ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x2, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||
nop2 ;[5] |
||||
;[6] |
||||
rjmp didunstuff3 ;[7] |
||||
;[8] jump delay of rjmp didunstuffX |
||||
|
||||
|
||||
|
||||
; the include has to be here due to branch distance restirctions |
||||
#define __USE_CRC__ |
||||
#include "asmcommon.inc" |
||||
|
||||
|
||||
|
||||
; USB spec says: |
||||
; idle = J |
||||
; J = (D+ = 0), (D- = 1) |
||||
; K = (D+ = 1), (D- = 0) |
||||
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||
; 7.5 bit times is 90 cycles. ...there is plenty of time |
||||
|
||||
|
||||
sendNakAndReti: |
||||
ldi x3, USBPID_NAK ;[-18] |
||||
rjmp sendX3AndReti ;[-17] |
||||
sendAckAndReti: |
||||
ldi cnt, USBPID_ACK ;[-17] |
||||
sendCntAndReti: |
||||
mov x3, cnt ;[-16] |
||||
sendX3AndReti: |
||||
ldi YL, 20 ;[-15] x3==r20 address is 20 |
||||
ldi YH, 0 ;[-14] |
||||
ldi cnt, 2 ;[-13] |
||||
; rjmp usbSendAndReti fallthrough |
||||
|
||||
;usbSend: |
||||
;pointer to data in 'Y' |
||||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||
;uses: x1...x4, btcnt, shift, cnt, Y |
||||
;Numbers in brackets are time since first bit of sync pattern is sent |
||||
|
||||
usbSendAndReti: ; 12 cycles until SOP |
||||
in x2, USBDDR ;[-12] |
||||
ori x2, USBMASK ;[-11] |
||||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||
in x1, USBOUT ;[-8] port mirror for tx loop |
||||
out USBDDR, x2 ;[-6] <- acquire bus |
||||
ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 |
||||
ldi x4, USBMASK ;[-5] exor mask |
||||
ldi shift, 0x80 ;[-4] sync byte is first byte sent |
||||
txByteLoop: |
||||
ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 |
||||
txBitLoop: ; the loop sends the first 7 bits of the byte |
||||
sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state |
||||
eor x1, x4 ;[-1]=[11] |
||||
out USBOUT, x1 ;[0] |
||||
ror shift ;[1] |
||||
ror x2 ;[2] transfers the last sent bit to the stuffing history |
||||
didStuffN: |
||||
nop ;[3] |
||||
nop ;[4] |
||||
cpi x2, 0xfc ;[5] if we sent six consecutive ones |
||||
brcc bitstuffN ;[6] |
||||
lsr bitcnt ;[7] |
||||
brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount |
||||
|
||||
; transmit bit 7 |
||||
sbrs shift, 0 ;[9] |
||||
eor x1, x4 ;[10] |
||||
didStuff7: |
||||
ror shift ;[11] |
||||
out USBOUT, x1 ;[0] transfer bit 7 to the pins |
||||
ror x2 ;[1] move the bit into the stuffing history |
||||
cpi x2, 0xfc ;[2] |
||||
brcc bitstuff7 ;[3] |
||||
ld shift, y+ ;[4] get next byte to transmit |
||||
dec cnt ;[5] decrement byte counter |
||||
brne txByteLoop ;[7] if we have more bytes start next one |
||||
;[8] branch delay |
||||
|
||||
;make SE0: |
||||
cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] |
||||
lds x2, usbNewDeviceAddr;[9] |
||||
lsl x2 ;[11] we compare with left shifted address |
||||
out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle |
||||
subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 |
||||
sbci YH, 0 ;[2] |
||||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||
;set address only after data packet was sent, not after handshake |
||||
breq skipAddrAssign ;[3] |
||||
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer |
||||
skipAddrAssign: |
||||
;end of usbDeviceAddress transfer |
||||
ldi x2, 1<<USB_INTR_PENDING_BIT;[5] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[6] |
||||
ori x1, USBIDLE ;[7] |
||||
in x2, USBDDR ;[8] |
||||
cbr x2, USBMASK ;[9] set both pins to input |
||||
mov x3, x1 ;[10] |
||||
cbr x3, USBMASK ;[11] configure no pullup on both pins |
||||
ldi x4, 4 ;[12] |
||||
se0Delay: |
||||
dec x4 ;[13] [16] [19] [22] |
||||
brne se0Delay ;[14] [17] [20] [23] |
||||
out USBOUT, x1 ;[24] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[25] <-- release bus now |
||||
out USBOUT, x3 ;[26] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
|
||||
bitstuffN: |
||||
eor x1, x4 ;[8] generate a zero |
||||
ldi x2, 0 ;[9] reset the bit stuffing history |
||||
nop2 ;[10] |
||||
out USBOUT, x1 ;[0] <-- send the stuffing bit |
||||
rjmp didStuffN ;[1] |
||||
|
||||
bitstuff7: |
||||
eor x1, x4 ;[5] |
||||
ldi x2, 0 ;[6] reset bit stuffing history |
||||
clc ;[7] fill a zero into the shift register |
||||
rol shift ;[8] compensate for ror shift at branch destination |
||||
rjmp didStuff7 ;[9] |
||||
;[10] jump delay |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; receives data bytes and calculates the crc |
||||
; second half of the data byte receiver loop |
||||
; most parts of the crc algorithm are here |
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
|
||||
nOverflow2: |
||||
rjmp overflow |
||||
|
||||
rxDataBit4: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] check for se0 |
||||
breq nOverflow2 ;[2] |
||||
eor x2, x1 ;[3] |
||||
bst x2, USBMINUS ;[4] |
||||
bld shift, 4 ;[5] |
||||
mov x2, shift ;[6] |
||||
andi x2, 0x9F ;[7] |
||||
breq unstuff4 ;[8] |
||||
didunstuff4: |
||||
nop2 ;[9][10] |
||||
nop ;[11] |
||||
|
||||
; bit5 |
||||
in x2, USBIN ;[0] sample line state |
||||
ldi ZH, hi8(usbCrcTableHigh);[1] use the table for the higher byte |
||||
eor x1, x2 ;[2] |
||||
bst x1, USBMINUS ;[3] |
||||
bld shift, 5 ;[4] |
||||
mov x1, shift ;[5] |
||||
andi x1, 0x3F ;[6] |
||||
breq unstuff5 ;[7] |
||||
didunstuff5: |
||||
lpm x4, Z ;[8] load the higher crc xor-byte and store it for later use |
||||
;[9] lpm needs 3 cycles |
||||
;[10] |
||||
ldi ZH, hi8(usbCrcTableLow);[11] load the lower crc xor byte adress |
||||
|
||||
; bit6 |
||||
in x1, USBIN ;[0] sample line state |
||||
eor x2, x1 ;[1] |
||||
bst x2, USBMINUS ;[2] |
||||
bld shift, 6 ;[3] |
||||
mov x2, shift ;[4] |
||||
andi x2, 0x7E ;[5] |
||||
breq unstuff6 ;[6] |
||||
didunstuff6: |
||||
lpm ZL, Z ;[7] load the lower xor crc byte |
||||
;[8] lpm needs 3 cycles |
||||
;[9] |
||||
eor ZL, x3 ;[10] xor the old high crc byte with the low xor-byte |
||||
mov x3, x4 ;[11] move the new high order crc value from temp to its destination |
||||
|
||||
; bit7 |
||||
in x2, USBIN ;[0] sample line state |
||||
eor x1, x2 ;[1] |
||||
bst x1, USBMINUS ;[2] |
||||
bld shift, 7 ;[3] now shift holds the complete but inverted data byte |
||||
mov x1, shift ;[4] |
||||
andi x1, 0xFC ;[5] |
||||
breq unstuff7 ;[6] |
||||
didunstuff7: |
||||
eor x5, shift ;[7] x5 marks all bits which have not been inverted by the unstuffing subs |
||||
mov x4, x5 ;[8] keep a copy of the data byte it will be stored during next bit0 |
||||
eor ZL, x4 ;[9] feed the actual byte into the crc algorithm |
||||
rjmp rxDataStart ;[10] next byte |
||||
;[11] during the reception of the next byte this one will be fed int the crc algorithm |
||||
|
||||
unstuff4: ;[9] this is the jump delay of rjmp unstuffX |
||||
ori shift, 0x10 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xEF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x1, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||
nop2 ;[5] |
||||
;[6] |
||||
rjmp didunstuff4 ;[7] |
||||
;[8] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff5: ;[8] this is the jump delay of rjmp unstuffX |
||||
nop ;[9] |
||||
ori shift, 0x20 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xDF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x2, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||
nop ;[5] |
||||
rjmp didunstuff5 ;[6] |
||||
;[7] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff6: ;[7] this is the jump delay of rjmp unstuffX |
||||
nop2 ;[8] |
||||
;[9] |
||||
ori shift, 0x40 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0xBF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x1, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||
rjmp didunstuff6 ;[5] |
||||
;[6] jump delay of rjmp didunstuffX |
||||
|
||||
unstuff7: ;[7] this is the jump delay of rjmp unstuffX |
||||
nop ;[8] |
||||
nop ;[9] |
||||
ori shift, 0x80 ;[10] invert the last received bit to prevent furhter unstuffing |
||||
andi x5, 0x7F ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||
andi x2, USBMASK ;[2] mask the interesting bits |
||||
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||
rjmp didunstuff7 ;[5] |
||||
;[6] jump delay of rjmp didunstuff7 |
||||
|
||||
; local copy of the stuffErr desitnation for the second half of the receiver loop |
||||
stuffErr2: |
||||
rjmp stuffErr |
||||
|
||||
;-------------------------------------------------------------------------------------------------------------- |
||||
; The crc table follows. It has to be aligned to enable a fast loading of the needed bytes. |
||||
; There are two tables of 256 entries each, the low and the high byte table. |
||||
; Table values were generated with the following C code: |
||||
/* |
||||
#include <stdio.h> |
||||
int main (int argc, char **argv) |
||||
{ |
||||
int i, j; |
||||
for (i=0; i<512; i++){ |
||||
unsigned short crc = i & 0xff; |
||||
for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); |
||||
if((i & 7) == 0) printf("\n.byte "); |
||||
printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); |
||||
if(i == 255) printf("\n"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// Use the following algorithm to compute CRC values: |
||||
ushort computeCrc(uchar *msg, uchar msgLen) |
||||
{ |
||||
uchar i; |
||||
ushort crc = 0xffff; |
||||
for(i = 0; i < msgLen; i++) |
||||
crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); |
||||
return crc; |
||||
} |
||||
*/ |
||||
|
||||
.balign 256 |
||||
usbCrcTableLow: |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||
|
||||
; .balign 256 |
||||
usbCrcTableHigh: |
||||
.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 |
||||
.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 |
||||
.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E |
||||
.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 |
||||
.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A |
||||
.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC |
||||
.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 |
||||
.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 |
||||
.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 |
||||
.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 |
||||
.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE |
||||
.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 |
||||
.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA |
||||
.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C |
||||
.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 |
||||
.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 |
||||
.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 |
||||
.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 |
||||
.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE |
||||
.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 |
||||
.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA |
||||
.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C |
||||
.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 |
||||
.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 |
||||
.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 |
||||
.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 |
||||
.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E |
||||
.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 |
||||
.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A |
||||
.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C |
||||
.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 |
||||
.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 |
||||
|
||||
@ -0,0 +1,360 @@ |
||||
/* Name: usbdrvasm20.inc |
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Jeroen Benschop |
||||
* Based on usbdrvasm16.inc from Christian Starkjohann |
||||
* Creation Date: 2008-03-05 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* Revision: $Id$ |
||||
*/ |
||||
|
||||
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||
* appropriate implementation! |
||||
*/ |
||||
|
||||
/* |
||||
General Description: |
||||
This file is the 20 MHz version of the asssembler part of the USB driver. It |
||||
requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||
oscillator). |
||||
|
||||
See usbdrv.h for a description of the entire driver. |
||||
|
||||
Since almost all of this code is timing critical, don't change unless you |
||||
really know what you are doing! Many parts require not only a maximum number |
||||
of CPU cycles, but even an exact number of cycles! |
||||
*/ |
||||
|
||||
#define leap2 x3 |
||||
#ifdef __IAR_SYSTEMS_ASM__ |
||||
#define nextInst $+2 |
||||
#else |
||||
#define nextInst .+0 |
||||
#endif |
||||
|
||||
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes |
||||
;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte |
||||
; Numbers in brackets are clocks counted from center of last sync bit |
||||
; when instruction starts |
||||
;register use in receive loop: |
||||
; shift assembles the byte currently being received |
||||
; x1 holds the D+ and D- line state |
||||
; x2 holds the previous line state |
||||
; x4 (leap) is used to add a leap cycle once every three bytes received |
||||
; X3 (leap2) is used to add a leap cycle once every three stuff bits received |
||||
; bitcnt is used to determine when a stuff bit is due |
||||
; cnt holds the number of bytes left in the receive buffer |
||||
|
||||
USB_INTR_VECTOR: |
||||
;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt |
||||
push YL ;[-28] push only what is necessary to sync with edge ASAP |
||||
in YL, SREG ;[-26] |
||||
push YL ;[-25] |
||||
push YH ;[-23] |
||||
;---------------------------------------------------------------------------- |
||||
; Synchronize with sync pattern: |
||||
;---------------------------------------------------------------------------- |
||||
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||
;waitForJ, ensure that this prerequisite is met. |
||||
waitForJ: |
||||
inc YL |
||||
sbis USBIN, USBMINUS |
||||
brne waitForJ ; just make sure we have ANY timeout |
||||
waitForK: |
||||
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||
sbis USBIN, USBMINUS ;[-19] |
||||
rjmp foundK ;[-18] |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
sbis USBIN, USBMINUS |
||||
rjmp foundK |
||||
#if USB_COUNT_SOF |
||||
lds YL, usbSofCount |
||||
inc YL |
||||
sts usbSofCount, YL |
||||
#endif /* USB_COUNT_SOF */ |
||||
#ifdef USB_SOF_HOOK |
||||
USB_SOF_HOOK |
||||
#endif |
||||
rjmp sofError |
||||
foundK: ;[-16] |
||||
;{3, 5} after falling D- edge, average delay: 4 cycles |
||||
;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample |
||||
;use 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||
;are cycles from center of first sync (double K) bit after the instruction |
||||
push bitcnt ;[-16] |
||||
; [---] ;[-15] |
||||
lds YL, usbInputBufOffset;[-14] |
||||
; [---] ;[-13] |
||||
clr YH ;[-12] |
||||
subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] |
||||
sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] |
||||
push shift ;[-9] |
||||
; [---] ;[-8] |
||||
ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected |
||||
nop2 ;[-6] |
||||
; [---] ;[-5] |
||||
ldi bitcnt, 5 ;[-4] [rx loop init] |
||||
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) |
||||
rjmp haveTwoBitsK ;[-2] |
||||
pop shift ;[-1] undo the push from before |
||||
pop bitcnt ;[1] |
||||
rjmp waitForK ;[3] this was not the end of sync, retry |
||||
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||
; bit times (= 27 cycles). |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; push more registers and initialize values while we sample the first bits: |
||||
;---------------------------------------------------------------------------- |
||||
haveTwoBitsK: |
||||
push x1 ;[0] |
||||
push x2 ;[2] |
||||
push x3 ;[4] (leap2) |
||||
ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit |
||||
push x4 ;[7] == leap |
||||
ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received |
||||
push cnt ;[10] |
||||
ldi cnt, USB_BUFSIZE ;[12] [rx loop init] |
||||
ldi x2, 1<<USBPLUS ;[13] current line state is K state. D+=="1", D-=="0" |
||||
bit0: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
rjmp handleBit ;[2] make bit0 14 cycles long |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Process bit7. However, bit 6 still may need unstuffing. |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
b6checkUnstuff: |
||||
dec bitcnt ;[9] |
||||
breq unstuff6 ;[10] |
||||
bit7: |
||||
subi cnt, 1 ;[11] cannot use dec becaus it does not affect the carry flag |
||||
brcs overflow ;[12] Too many bytes received. Ignore packet |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
cpse x1, x2 ;[2] when previous line state equals current line state, handle "1" |
||||
rjmp b7handle0 ;[3] when line state differs, handle "0" |
||||
sec ;[4] |
||||
ror shift ;[5] shift "1" into the data |
||||
st y+, shift ;[6] store the data into the buffer |
||||
ldi shift, 0x40 ;[7] reset data for receiving the next byte |
||||
subi leap, 0x55 ;[9] trick to introduce a leap cycle every 3 bytes |
||||
brcc nextInst ;[10 or 11] it will fail after 85 bytes. However low speed can only receive 11 |
||||
dec bitcnt ;[11 or 12] |
||||
brne bit0 ;[12 or 13] |
||||
ldi x1, 1 ;[13 or 14] unstuffing bit 7 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
rjmp unstuff ;[1] |
||||
|
||||
b7handle0: |
||||
mov x2,x1 ;[5] Set x2 to current line state |
||||
ldi bitcnt, 6 ;[6] |
||||
lsr shift ;[7] shift "0" into the data |
||||
st y+, shift ;[8] store data into the buffer |
||||
ldi shift, 0x40 ;[10] reset data for receiving the next byte |
||||
subi leap, 0x55 ;[11] trick to introduce a leap cycle every 3 bytes |
||||
brcs bit0 ;[12] it will fail after 85 bytes. However low speed can only receive 11 |
||||
rjmp bit0 ;[13] |
||||
|
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Handle unstuff |
||||
; x1==0xFF indicate unstuffing bit6 |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
unstuff6: |
||||
ldi x1,0xFF ;[12] indicate unstuffing bit 6 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
nop ;[1] fix timing |
||||
unstuff: ;b0-5 b6 b7 |
||||
mov x2,bitcnt ;[3] [2] [3] Set x2 to match line state |
||||
subi leap2, 0x55 ;[4] [3] [4] delay loop |
||||
brcs nextInst ;[5] [4] [5] add one cycle every three stuff bits |
||||
sbci leap2,0 ;[6] [5] [6] |
||||
ldi bitcnt,6 ;[7] [6] [7] reset bit stuff counter |
||||
andi x2, USBMASK ;[8] [7] [8] only keep D+ and D- |
||||
cpi x1,0 ;[9] [8] [9] |
||||
brmi bit7 ;[10] [9] [10] finished unstuffing bit6 When x1<0 |
||||
breq bitloop ;[11] --- [11] finished unstuffing bit0-5 when x1=0 |
||||
nop ;--- --- [12] |
||||
in x1, USBIN ;--- --- [0] sample line state for bit0 |
||||
andi x1, USBMASK ;--- --- [1] filter only D+ and D- bits |
||||
rjmp handleBit ;--- --- [2] make bit0 14 cycles long |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||
;---------------------------------------------------------------------------- |
||||
bitloop: |
||||
in x1, USBIN ;[0] sample line state |
||||
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||
breq se0 ;[2] both lines are low so handle se0 |
||||
handleBit: |
||||
cpse x1, x2 ;[3] when previous line state equals current line state, handle "1" |
||||
rjmp handle0 ;[4] when line state differs, handle "0" |
||||
sec ;[5] |
||||
ror shift ;[6] shift "1" into the data |
||||
brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7 |
||||
nop2 ;[8] |
||||
dec bitcnt ;[10] |
||||
brne bitloop ;[11] |
||||
ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7 |
||||
in bitcnt, USBIN ;[0] sample stuff bit |
||||
rjmp unstuff ;[1] |
||||
|
||||
handle0: |
||||
mov x2, x1 ;[6] Set x2 to current line state |
||||
ldi bitcnt, 6 ;[7] reset unstuff counter. |
||||
lsr shift ;[8] shift "0" into the data |
||||
brcs bit7 ;[9] When after shift C is set, next bit is bit7 |
||||
nop ;[10] |
||||
rjmp bitloop ;[11] |
||||
|
||||
;---------------------------------------------------------------------------- |
||||
; End of receive loop. Now start handling EOP |
||||
;---------------------------------------------------------------------------- |
||||
|
||||
macro POP_STANDARD ; 14 cycles |
||||
pop cnt |
||||
pop x4 |
||||
pop x3 |
||||
pop x2 |
||||
pop x1 |
||||
pop shift |
||||
pop bitcnt |
||||
endm |
||||
macro POP_RETI ; 7 cycles |
||||
pop YH |
||||
pop YL |
||||
out SREG, YL |
||||
pop YL |
||||
endm |
||||
|
||||
|
||||
|
||||
#include "asmcommon.inc" |
||||
|
||||
; USB spec says: |
||||
; idle = J |
||||
; J = (D+ = 0), (D- = 1) |
||||
; K = (D+ = 1), (D- = 0) |
||||
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||
; 7.5 bit times is 100 cycles. This implementation arrives a bit later at se0 |
||||
; then specified in the include file but there is plenty of time |
||||
|
||||
bitstuffN: |
||||
eor x1, x4 ;[8] |
||||
ldi x2, 0 ;[9] |
||||
nop2 ;[10] |
||||
out USBOUT, x1 ;[12] <-- out |
||||
rjmp didStuffN ;[0] |
||||
|
||||
bitstuff7: |
||||
eor x1, x4 ;[6] |
||||
ldi x2, 0 ;[7] Carry is zero due to brcc |
||||
rol shift ;[8] compensate for ror shift at branch destination |
||||
nop2 ;[9] |
||||
rjmp didStuff7 ;[11] |
||||
|
||||
sendNakAndReti: |
||||
ldi x3, USBPID_NAK ;[-18] |
||||
rjmp sendX3AndReti ;[-17] |
||||
sendAckAndReti: |
||||
ldi cnt, USBPID_ACK ;[-17] |
||||
sendCntAndReti: |
||||
mov x3, cnt ;[-16] |
||||
sendX3AndReti: |
||||
ldi YL, 20 ;[-15] x3==r20 address is 20 |
||||
ldi YH, 0 ;[-14] |
||||
ldi cnt, 2 ;[-13] |
||||
; rjmp usbSendAndReti fallthrough |
||||
|
||||
;usbSend: |
||||
;pointer to data in 'Y' |
||||
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||
;uses: x1...x4, btcnt, shift, cnt, Y |
||||
;Numbers in brackets are time since first bit of sync pattern is sent |
||||
;We don't match the transfer rate exactly (don't insert leap cycles every third |
||||
;byte) because the spec demands only 1.5% precision anyway. |
||||
usbSendAndReti: ; 12 cycles until SOP |
||||
in x2, USBDDR ;[-12] |
||||
ori x2, USBMASK ;[-11] |
||||
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||
in x1, USBOUT ;[-8] port mirror for tx loop |
||||
out USBDDR, x2 ;[-7] <- acquire bus |
||||
; need not init x2 (bitstuff history) because sync starts with 0 |
||||
ldi x4, USBMASK ;[-6] exor mask |
||||
ldi shift, 0x80 ;[-5] sync byte is first byte sent |
||||
txByteLoop: |
||||
ldi bitcnt, 0x49 ;[-4] [10] binary 01001001 |
||||
txBitLoop: |
||||
sbrs shift, 0 ;[-3] [10] [11] |
||||
eor x1, x4 ;[-2] [11] [12] |
||||
out USBOUT, x1 ;[-1] [12] [13] <-- out N |
||||
ror shift ;[0] [13] [14] |
||||
ror x2 ;[1] |
||||
didStuffN: |
||||
nop2 ;[2] |
||||
nop ;[4] |
||||
cpi x2, 0xfc ;[5] |
||||
brcc bitstuffN ;[6] |
||||
lsr bitcnt ;[7] |
||||
brcc txBitLoop ;[8] |
||||
brne txBitLoop ;[9] |
||||
|
||||
sbrs shift, 0 ;[10] |
||||
eor x1, x4 ;[11] |
||||
didStuff7: |
||||
out USBOUT, x1 ;[-1] [13] <-- out 7 |
||||
ror shift ;[0] [14] |
||||
ror x2 ;[1] |
||||
nop ;[2] |
||||
cpi x2, 0xfc ;[3] |
||||
brcc bitstuff7 ;[4] |
||||
ld shift, y+ ;[5] |
||||
dec cnt ;[7] |
||||
brne txByteLoop ;[8] |
||||
;make SE0: |
||||
cbr x1, USBMASK ;[9] prepare SE0 [spec says EOP may be 25 to 30 cycles] |
||||
lds x2, usbNewDeviceAddr;[10] |
||||
lsl x2 ;[12] we compare with left shifted address |
||||
out USBOUT, x1 ;[13] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
||||
subi YL, 20 + 2 ;[0] Only assign address on data packets, not ACK/NAK in x3 |
||||
sbci YH, 0 ;[1] |
||||
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||
;set address only after data packet was sent, not after handshake |
||||
breq skipAddrAssign ;[2] |
||||
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
||||
skipAddrAssign: |
||||
;end of usbDeviceAddress transfer |
||||
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag |
||||
USB_STORE_PENDING(x2) ;[5] |
||||
ori x1, USBIDLE ;[6] |
||||
in x2, USBDDR ;[7] |
||||
cbr x2, USBMASK ;[8] set both pins to input |
||||
mov x3, x1 ;[9] |
||||
cbr x3, USBMASK ;[10] configure no pullup on both pins |
||||
ldi x4, 5 ;[11] |
||||
se0Delay: |
||||
dec x4 ;[12] [15] [18] [21] [24] |
||||
brne se0Delay ;[13] [16] [19] [22] [25] |
||||
out USBOUT, x1 ;[26] <-- out J (idle) -- end of SE0 (EOP signal) |
||||
out USBDDR, x2 ;[27] <-- release bus now |
||||
out USBOUT, x3 ;[28] <-- ensure no pull-up resistors are active |
||||
rjmp doReturn |
||||
@ -0,0 +1,144 @@ |
||||
/* Name: usbportability.h
|
||||
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||
* Author: Christian Starkjohann |
||||
* Creation Date: 2008-06-17 |
||||
* Tabsize: 4 |
||||
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||
* This Revision: $Id$ |
||||
*/ |
||||
|
||||
/*
|
||||
General Description: |
||||
This header is intended to contain all (or at least most of) the compiler |
||||
and library dependent stuff. The C code is written for avr-gcc and avr-libc. |
||||
The API of other development environments is converted to gcc's and avr-libc's |
||||
API by means of defines. |
||||
|
||||
This header also contains all system includes since they depend on the |
||||
development environment. |
||||
|
||||
Thanks to Oleg Semyonov for his help with the IAR tools port! |
||||
*/ |
||||
|
||||
#ifndef __usbportability_h_INCLUDED__ |
||||
#define __usbportability_h_INCLUDED__ |
||||
|
||||
/* We check explicitly for IAR and CodeVision. Default is avr-gcc/avr-libc. */ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ /* check for IAR */ |
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#ifndef ENABLE_BIT_DEFINITIONS |
||||
# define ENABLE_BIT_DEFINITIONS 1 /* Enable bit definitions */ |
||||
#endif |
||||
|
||||
/* Include IAR headers */ |
||||
#include <ioavr.h> |
||||
#ifndef __IAR_SYSTEMS_ASM__ |
||||
# include <inavr.h> |
||||
#endif |
||||
|
||||
#define __attribute__(arg) /* not supported on IAR */ |
||||
|
||||
#ifdef __IAR_SYSTEMS_ASM__ |
||||
# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ |
||||
#endif |
||||
|
||||
#ifdef __HAS_ELPM__ |
||||
# define PROGMEM __farflash |
||||
#else |
||||
# define PROGMEM __flash |
||||
#endif |
||||
|
||||
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) |
||||
|
||||
/* The following definitions are not needed by the driver, but may be of some
|
||||
* help if you port a gcc based project to IAR. |
||||
*/ |
||||
#define cli() __disable_interrupt() |
||||
#define sei() __enable_interrupt() |
||||
#define wdt_reset() __watchdog_reset() |
||||
#define _BV(x) (1 << (x)) |
||||
|
||||
/* assembler compatibility macros */ |
||||
#define nop2 rjmp $+2 /* jump to next instruction */ |
||||
#define XL r26 |
||||
#define XH r27 |
||||
#define YL r28 |
||||
#define YH r29 |
||||
#define ZL r30 |
||||
#define ZH r31 |
||||
#define lo8(x) LOW(x) |
||||
#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ |
||||
|
||||
/* Depending on the device you use, you may get problems with the way usbdrv.h
|
||||
* handles the differences between devices. Since IAR does not use #defines |
||||
* for MCU registers, we can't check for the existence of a particular |
||||
* register with an #ifdef. If the autodetection mechanism fails, include |
||||
* definitions for the required USB_INTR_* macros in your usbconfig.h. See |
||||
* usbconfig-prototype.h and usbdrv.h for details. |
||||
*/ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ |
||||
/* ------------------------------------------------------------------------- */ |
||||
/* This port is not working (yet) */ |
||||
|
||||
/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ |
||||
|
||||
#include <io.h> |
||||
#include <delay.h> |
||||
|
||||
#define __attribute__(arg) /* not supported on IAR */ |
||||
|
||||
#define PROGMEM __flash |
||||
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) |
||||
|
||||
#ifndef __ASSEMBLER__ |
||||
static inline void cli(void) |
||||
{ |
||||
#asm("cli"); |
||||
} |
||||
static inline void sei(void) |
||||
{ |
||||
#asm("sei"); |
||||
} |
||||
#endif |
||||
#define _delay_ms(t) delay_ms(t) |
||||
#define _BV(x) (1 << (x)) |
||||
#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ |
||||
|
||||
#define macro .macro |
||||
#define endm .endmacro |
||||
#define nop2 rjmp .+0 /* jump to next instruction */ |
||||
|
||||
/* ------------------------------------------------------------------------- */ |
||||
#else /* default development environment is avr-gcc/avr-libc */ |
||||
/* ------------------------------------------------------------------------- */ |
||||
|
||||
#include <avr/io.h> |
||||
#ifdef __ASSEMBLER__ |
||||
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ |
||||
#else |
||||
# include <avr/pgmspace.h> |
||||
#endif |
||||
|
||||
#if USB_CFG_DRIVER_FLASH_PAGE |
||||
# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) |
||||
#else |
||||
# define USB_READ_FLASH(addr) pgm_read_byte(addr) |
||||
#endif |
||||
|
||||
#define macro .macro |
||||
#define endm .endm |
||||
#define nop2 rjmp .+0 /* jump to next instruction */ |
||||
|
||||
#endif /* development environment */ |
||||
|
||||
/* for conveniecne, ensure that PRG_RDB exists */ |
||||
#ifndef PRG_RDB |
||||
# define PRG_RDB(addr) USB_READ_FLASH(addr) |
||||
#endif |
||||
#endif /* __usbportability_h_INCLUDED__ */ |
||||
Loading…
Reference in new issue