/Designs/Tools/i2c_AVR_USB/SW/firmware/Makefile-avrusb.mega8 |
---|
16,6 → 16,7 |
DEFINES += -DDEBUG |
DEFINES += -DDEBUG_LEVEL=1 |
DEFINES += -DF_CPU=12000000 |
COMPILE = avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega8 $(DEFINES) |
OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o |
72,10 → 73,10 |
# on Windows with WinAVR where the Unix commands will fail. |
program: firmware.hex |
avrdude -c usbasp -p atmega8 -U lfuse:w:0x9f:m -U hfuse:w:0xc9:m -U flash:w:firmware.hex |
avrdude -P /dev/ttyUSB0 -c stk500v2 -B 50 -p atmega8 -U lfuse:w:0x9f:m -U hfuse:w:0xc9:m -U flash:w:firmware.hex |
program-nodep: |
avrdude -c usbasp -p atmega8 -U lfuse:w:0x9f:m -U hfuse:w:0xc9:m -U flash:w:firmware.hex |
avrdude -P /dev/ttyUSB0 -c stk500v2 -B 50 -p atmega8 -U lfuse:w:0x9f:m -U hfuse:w:0xc9:m -U flash:w:firmware.hex |
disasm: firmware.bin |
avr-objdump -d firmware.bin |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbconfig.h |
---|
38,6 → 38,23 |
#define USB_CFG_IOPORTNAME B |
#define USB_CFG_DMINUS_BIT 0 |
#define USB_CFG_DPLUS_BIT 2 |
/*#define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, |
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code |
* require no crystal, they tolerate +/- 1% deviation from the nominal |
* frequency. All other rates require a precision of 2000 ppm and thus a |
* crystal! |
* Since F_CPU should be defined to your actual clock rate anyway, you should |
* not need to modify this setting. |
*/ |
/*#define USB_CFG_CHECK_CRC 0 |
/* Define this to 1 if you want that the driver checks integrity of incoming |
* data packets (CRC checks). CRC checks cost quite a bit of code size and are |
* currently only available for 18 MHz crystal clock. You must choose |
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option. |
*/ |
#endif |
/* --------------------------- Functional Range ---------------------------- */ |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/Changelog.txt |
---|
132,3 → 132,198 |
itself, PLEASE grant us a royalty free license to your changes for our |
commercial license. |
* Release 2007-03-29 |
- New configuration option "USB_PUBLIC" in usbconfig.h. |
- Set USB version number to 1.10 instead of 1.01. |
- Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and |
USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences |
to USB_CFG_DESCR_PROPS_STRING_PRODUCT. |
- New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver |
code. |
- New assembler module for 16 MHz crystal. |
- usbdrvasm.S contains common code only, clock-specific parts have been moved |
to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. |
* Release 2007-06-25 |
- 16 MHz module: Do SE0 check in stuffed bits as well. |
* Release 2007-07-07 |
- Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary |
for negative values. |
- Added 15 MHz module contributed by V. Bosch. |
- Interrupt vector name can now be configured. This is useful if somebody |
wants to use a different hardware interrupt than INT0. |
* Release 2007-08-07 |
- Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is |
not exceeded. |
- More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, |
USB_COUNT_SOF |
- USB_INTR_PENDING can now be a memory address, not just I/O |
* Release 2007-09-19 |
- Split out common parts of assembler modules into separate include file |
- Made endpoint numbers configurable so that given interface definitions |
can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. |
- Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() |
can handle any number of endpoints. |
- Define usbDeviceConnect() and usbDeviceDisconnect() even if no |
USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this |
case. |
* Release 2007-12-01 |
- Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size |
when USB_CFG_PULLUP_IOPORTNAME is not defined. |
* Release 2007-12-13 |
- Renamed all include-only assembler modules from *.S to *.inc so that |
people don't add them to their project sources. |
- Distribute leap bits in tx loop more evenly for 16 MHz module. |
- Use "macro" and "endm" instead of ".macro" and ".endm" for IAR |
- Avoid compiler warnings for constant expr range by casting some values in |
USB descriptors. |
* Release 2008-01-21 |
- Fixed bug in 15 and 16 MHz module where the new address set with |
SET_ADDRESS was already accepted at the next NAK or ACK we send, not at |
the next data packet we send. This caused problems when the host polled |
too fast. Thanks to Alexander Neumann for his help and patience debugging |
this issue! |
* Release 2008-02-05 |
- Fixed bug in 16.5 MHz module where a register was used in the interrupt |
handler before it was pushed. This bug was introduced with version |
2007-09-19 when common parts were moved to a separate file. |
- Optimized CRC routine (thanks to Reimar Doeffinger). |
* Release 2008-02-16 |
- Removed outdated IAR compatibility stuff (code sections). |
- Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). |
- Added optional routine usbMeasureFrameLength() for calibration of the |
internal RC oscillator. |
* Release 2008-02-28 |
- USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we |
start with sending USBPID_DATA0. |
- Changed defaults in usbconfig-prototype.h |
- Added free USB VID/PID pair for MIDI class devices |
- Restructured AVR-USB as separate package, not part of PowerSwitch any more. |
* Release 2008-04-18 |
- Restructured usbdrv.c so that it is easier to read and understand. |
- Better code optimization with gcc 4. |
- If a second interrupt in endpoint is enabled, also add it to config |
descriptor. |
- Added config option for long transfers (above 254 bytes), see |
USB_CFG_LONG_TRANSFERS in usbconfig.h. |
- Added 20 MHz module contributed by Jeroen Benschop. |
* Release 2008-05-13 |
- Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length |
was not incremented, pointer to length was incremented instead. |
- Added code to command line tool(s) which claims an interface. This code |
is disabled by default, but may be necessary on newer Linux kernels. |
- Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". |
- New header "usbportability.h" prepares ports to other development |
environments. |
- Long transfers (above 254 bytes) did not work when usbFunctionRead() was |
used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] |
- In hiddata.c (example code for sending/receiving data over HID), use |
USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so |
that we need not claim the interface. |
- in usbPoll() loop 20 times polling for RESET state instead of 10 times. |
This accounts for the higher clock rates we now support. |
- Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. |
- Added hook to SOF code so that oscillator can be tuned to USB frame clock. |
- Added timeout to waitForJ loop. Helps preventing unexpected hangs. |
- Added example code for oscillator tuning to libs-device (thanks to |
Henrik Haftmann for the idea to this routine). |
- Implemented option USB_CFG_SUPPRESS_INTR_CODE. |
* Release 2008-10-22 |
- Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and |
similar, not offset of 0x20 needs to be added. |
- Allow distribution under GPLv3 for those who have to link against other |
code distributed under GPLv3. |
* Release 2008-11-26 |
- Removed libusb-win32 dependency for hid-data example in Makefile.windows. |
It was never required and confused many people. |
- Added extern uchar usbRxToken to usbdrv.h. |
- Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. |
* Release 2009-03-23 |
- Hid-mouse example used settings from hid-data example, fixed that. |
- Renamed project to V-USB due to a trademark issue with Atmel(r). |
- Changed CommercialLicense.txt and USBID-License.txt to make the |
background of USB ID registration clearer. |
* Release 2009-04-15 |
- Changed CommercialLicense.txt to reflect the new range of PIDs from |
Jason Kotzin. |
- Removed USBID-License.txt in favor of USB-IDs-for-free.txt and |
USB-ID-FAQ.txt |
- Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in |
the center between bit 0 and 1 of each byte. This is where the data lines |
are expected to change and the sampled data may therefore be nonsense. |
We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. |
- Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, |
the unstuffing code in the receiver routine was 1 cycle too long. If |
multiple bytes had the unstuffing in bit 6, the error summed up until the |
receiver was out of sync. |
- Included option for faster CRC routine. |
Thanks to Slawomir Fras (BoskiDialer) for this code! |
- Updated bits in Configuration Descriptor's bmAttributes according to |
USB 1.1 (in particular bit 7, it is a must-be-set bit now). |
* Release 2009-08-22 |
- Moved first DBG1() after odDebugInit() in all examples. |
- Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes |
V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). |
- USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any |
more). |
- New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with |
more than 64 kB flash. |
- Built-in configuration descriptor allows custom definition for second |
endpoint now. |
* Release 2010-07-15 |
- Fixed bug in usbDriverSetup() which prevented descriptor sizes above 255 |
bytes. |
- Avoid a compiler warning for unused parameter in usbHandleResetHook() when |
compiler option -Wextra is enabled. |
- Fixed wrong hex value for some IDs in USB-IDs-for-free.txt. |
- Keep a define for USBATTR_BUSPOWER, although the flag does not exist |
in USB 1.1 any more. Set it to 0. This is for backward compatibility. |
* Release 2012-01-09 |
- Define a separate (defined) type for usbMsgPtr so that projects using a |
tiny memory model can define it to an 8 bit type in usbconfig.h. This |
change also saves a couple of bytes when using a scalar 16 bit type. |
- Inserted "const" keyword for all PROGMEM declarations because new GCC |
requires it. |
- Fixed problem with dependence of usbportability.h on usbconfig.h. This |
problem occurred with IAR CC only. |
- Prepared repository for github.com. |
* Release 2012-12-06 |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/CommercialLicense.txt |
---|
1,5 → 1,5 |
AVR-USB Driver Software License Agreement |
Version 2006-07-24 |
V-USB Driver Software License Agreement |
Version 2012-07-09 |
THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN |
ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING |
13,32 → 13,43 |
1.2 "You" shall mean the Licensee. |
1.3 "AVR-USB" shall mean the firmware-only USB device implementation for |
Atmel AVR microcontrollers distributed by OBJECTIVE DEVELOPMENT and |
consisting of the files usbdrv.c, usbdrv.h, usbdrvasm.S, oddebug.c, |
oddebug.h, usbdrvasm.asm, iarcompat.h and usbconfig-prototype.h. |
1.3 "V-USB" shall mean all files included in the package distributed under |
the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) |
unless otherwise noted. This includes the firmware-only USB device |
implementation for Atmel AVR microcontrollers, some simple device examples |
and host side software examples and libraries. |
2 LICENSE GRANTS |
2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source |
code of AVR-USB. |
code of V-USB. |
2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the |
non-exclusive right to use and distribute AVR-USB with your hardware |
non-exclusive right to use, copy and distribute V-USB with your hardware |
product(s), restricted by the limitations in section 3 below. |
2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify |
your copy of AVR-USB according to your needs. |
the source code and your copy of V-USB according to your needs. |
2.4 USB IDs. OBJECTIVE DEVELOPMENT grants you the exclusive rights to use |
USB Product ID(s) sent to you in e-mail after receiving your payment in |
conjunction with USB Vendor ID 5824. OBJECTIVE DEVELOPMENT has acquired an |
exclusive license for this pair of USB identifiers from Wouter van Ooijen |
(www.voti.nl), who has licensed the VID from the USB Implementers Forum, |
Inc. (www.usb.org). |
2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB |
Product ID(s), sent to you in e-mail. These Product IDs are reserved |
exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID |
ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen |
Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from |
Jason Kotzin (now flirc.tv, Inc.). Both owners of the Vendor IDs have |
obtained these IDs from the USB Implementers Forum, Inc. (www.usb.org). |
OBJECTIVE DEVELOPMENT disclaims all liability which might arise from the |
assignment of USB IDs. |
2.5 USB Certification. Although not part of this agreement, we want to make |
it clear that you cannot become USB certified when you use V-USB or a USB |
Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't |
meet the electrical specifications required by the USB specification and |
the USB Implementers Forum certifies only members who bought a Vendor ID of |
their own. |
3 LICENSE RESTRICTIONS |
3.1 Number of Units. Only one of the following three definitions is |
45,13 → 56,13 |
applicable. Which one is determined by the amount you pay to OBJECTIVE |
DEVELOPMENT, see section 4 ("Payment") below. |
Hobby License: You may use AVR-USB according to section 2 above in no more |
Hobby License: You may use V-USB according to section 2 above in no more |
than 5 hardware units. These units must not be sold for profit. |
Entry Level License: You may use AVR-USB according to section 2 above in no |
Entry Level License: You may use V-USB according to section 2 above in no |
more than 150 hardware units. |
Professional License: You may use AVR-USB according to section 2 above in |
Professional License: You may use V-USB according to section 2 above in |
any number of hardware units, except for large scale production ("unlimited |
fair use"). Quantities below 10,000 units are not considered large scale |
production. If your reach quantities which are obviously large scale |
58,8 → 69,8 |
production, you must pay a license fee of 0.10 EUR per unit for all units |
above 10,000. |
3.2 Rental. You may not rent, lease, or lend AVR-USB or otherwise encumber |
any copy of AVR-USB, or any of the rights granted herein. |
3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber |
any copy of V-USB, or any of the rights granted herein. |
3.3 Transfer. You may not transfer your rights under this Agreement to |
another party without OBJECTIVE DEVELOPMENT's prior written consent. If |
77,7 → 88,7 |
by third parties. In particular, you are not allowed to use the USB logo or |
other trademarks owned by the USB Implementers Forum, Inc. without their |
consent. Since such consent depends on USB certification, it should be |
noted that AVR-USB will not pass certification because it does not |
noted that V-USB will not pass certification because it does not |
implement checksum verification and the microcontroller ports do not meet |
the electrical specifications. |
87,7 → 98,7 |
The payment amount depends on the variation of this agreement (according to |
section 3.1) into which you want to enter. Concrete prices are listed on |
OBJECTIVE DEVELOPMENT's web site, usually at |
http://www.obdev.at/avrusb/license.html. You agree to pay the amount listed |
http://www.obdev.at/vusb/license.html. You agree to pay the amount listed |
there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor |
or reseller. |
94,8 → 105,8 |
5 COPYRIGHT AND OWNERSHIP |
AVR-USB is protected by copyright laws and international copyright |
treaties, as well as other intellectual property laws and treaties. AVR-USB |
V-USB is protected by copyright laws and international copyright |
treaties, as well as other intellectual property laws and treaties. V-USB |
is licensed, not sold. |
111,12 → 122,12 |
7 DISCLAIMER OF WARRANTY AND LIABILITY |
LIMITED WARRANTY. AVR-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE |
DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER |
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND |
NON-INFRINGEMENT, WITH REGARD TO AVR-USB, AND THE PROVISION OF OR FAILURE |
NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE |
TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL |
RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO |
STATE/JURISDICTION. |
126,11 → 137,11 |
SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER |
(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, |
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY |
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE AVR-USB OR THE |
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE |
PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE |
DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY |
CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS |
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR AVR-USB. |
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. |
8 MISCELLANEOUS TERMS |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/License.txt |
---|
1,9 → 1,11 |
OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the |
terms and conditions of the GNU GPL version 2, see the text below. In addition |
to the requirements in the GPL, we STRONGLY ENCOURAGE you to do the following: |
OBJECTIVE DEVELOPMENT GmbH's V-USB driver software is distributed under the |
terms and conditions of the GNU GPL version 2 or the GNU GPL version 3. It is |
your choice whether you apply the terms of version 2 or version 3. The full |
text of GPLv2 is included below. In addition to the requirements in the GPL, |
we STRONGLY ENCOURAGE you to do the following: |
(1) Publish your entire project on a web site and drop us a note with the URL. |
Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. |
Use the form at http://www.obdev.at/vusb/feedback.html for your submission. |
(2) Adhere to minimum publication standards. Please include AT LEAST: |
- a circuit diagram in PDF, PNG or GIF format |
10,7 → 12,7 |
- full source code for the host software |
- a Readme.txt file in ASCII format which describes the purpose of the |
project and what can be found in which directories and which files |
- a reference to http://www.obdev.at/avrusb/ |
- a reference to http://www.obdev.at/vusb/ |
(3) If you improve the driver firmware itself, please give us a free license |
to your modifications for our commercial license offerings. |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/Readme.txt |
---|
1,18 → 1,101 |
This is the Readme file to Objective Development's firmware-only USB driver |
for Atmel AVR microcontrollers. For more information please visit |
http://www.obdev.at/avrusb/ |
http://www.obdev.at/vusb/ |
This directory contains the USB firmware only. Copy it as-is to your own |
project and add your own version of "usbconfig.h". A template for your own |
"usbconfig.h" can be found in "usbconfig-prototype.h" in this directory. |
project and add all .c and .S files to your project (these files are marked |
with an asterisk in the list below). Then copy usbconfig-prototype.h as |
usbconfig.h to your project and edit it according to your configuration. |
TECHNICAL DOCUMENTATION |
======================= |
The technical documentation for the firmware driver is contained in the file |
"usbdrv.h". Please read all of it carefully! |
The technical documentation (API) for the firmware driver is contained in the |
file "usbdrv.h". Please read all of it carefully! Configuration options are |
documented in "usbconfig-prototype.h". |
The driver consists of the following files: |
Readme.txt ............. The file you are currently reading. |
Changelog.txt .......... Release notes for all versions of the driver. |
usbdrv.h ............... Driver interface definitions and technical docs. |
* usbdrv.c ............... High level language part of the driver. Link this |
module to your code! |
* usbdrvasm.S ............ Assembler part of the driver. This module is mostly |
a stub and includes one of the usbdrvasm*.S files |
depending on processor clock. Link this module to |
your code! |
usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. |
Included by usbdrvasm.S, don't link it directly! |
asmcommon.inc .......... Common assembler routines. Included by |
usbdrvasm*.inc, don't link it directly! |
usbconfig-prototype.h .. Prototype for your own usbdrv.h file. |
* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is |
defined to a value greater than 0. Link this module |
to your code! |
oddebug.h .............. Interface definitions of the debug module. |
usbportability.h ....... Header with compiler-dependent stuff. |
usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this |
module instead of usbdrvasm.S when you assembler |
with IAR's tools. |
License.txt ............ Open Source license for this driver. |
CommercialLicense.txt .. Optional commercial license for this driver. |
USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. |
USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. |
(*) ... These files should be linked to your project. |
CPU CORE CLOCK FREQUENCY |
======================== |
We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, |
16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The |
actual clock rate must be configured in usbconfig.h. |
12 MHz Clock |
This is the traditional clock rate of V-USB because it's the lowest clock |
rate where the timing constraints of the USB spec can be met. |
15 MHz Clock |
Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock |
rate allows for some loops which make the resulting code size somewhat smaller |
than the 12 MHz version. |
16 MHz Clock |
This clock rate has been added for users of the Arduino board and other |
ready-made boards which come with a fixed 16 MHz crystal. It's also an option |
if you need the slightly higher clock rate for performance reasons. Since |
16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code |
is somewhat tricky and has to insert a leap cycle every third byte. |
12.8 MHz and 16.5 MHz Clock |
The assembler modules for these clock rates differ from the other modules |
because they have been built for an RC oscillator with only 1% precision. The |
receiver code inserts leap cycles to compensate for clock deviations. 1% is |
also the precision which can be achieved by calibrating the internal RC |
oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL |
oscillator can reach 16.5 MHz with the RC oscillator. This includes the very |
popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost |
all AVRs can reach 12.8 MHz, although this is outside the specified range. |
See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for |
code which calibrates the RC oscillator based on the USB frame clock. |
18 MHz Clock |
This module is closer to the USB specification because it performs an on the |
fly CRC check for incoming packets. Packets with invalid checksum are |
discarded as required by the spec. If you also implement checks for data |
PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING |
in usbconfig.h for more info), this ensures data integrity. Due to the CRC |
tables and alignment requirements, this code is bigger than modules for other |
clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 |
and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. |
20 MHz Clock |
This module is for people who won't do it with less than the maximum. Since |
20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code |
uses similar tricks as the 16 MHz module to insert leap cycles. |
USB IDENTIFIERS |
=============== |
Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs |
20,49 → 103,54 |
can assign PIDs at will. |
Since an entry level cost of 1,500 USD is too high for most small companies |
and hobbyists, we provide a single VID/PID pair for free. If you want to use |
your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" |
and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". |
and hobbyists, we provide some VID/PID pairs for free. See the file |
USB-IDs-for-free.txt for details. |
To use our predefined VID/PID pair, you MUST conform to a couple of |
requirements. See the file "USBID-License.txt" for details. |
Objective Development also has some license offerings which include product |
IDs. See http://www.obdev.at/vusb/ for details. |
Objective Development also has some offerings which include product IDs. See |
http://www.obdev.at/avrusb/ for details. |
HOST DRIVER |
=========== |
You have received this driver together with an example device implementation |
and an example host driver. The host driver is based on libusb and compiles |
on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on |
Windows using MinGW (see www.mingw.org) and libusb-win32 (see |
libusb-win32.sourceforge.net). The "Automator" project contains a native |
Windows host driver (not based on libusb) for Human Interface Devices. |
DEVELOPMENT SYSTEM |
================== |
This driver has been developed and optimized for the GNU compiler version 3 |
(gcc 3). It does work well with gcc 4 and future versions will probably be |
optimized for gcc 4. We recommend that you use the GNU compiler suite because |
it is freely available. AVR-USB has also been ported to the IAR compiler and |
assembler. It has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 |
with the "small" and "tiny" memory model. Please note that gcc is more |
efficient for usbdrv.c because this module has been deliberately optimized |
for gcc. |
and 4. We recommend that you use the GNU compiler suite because it is freely |
available. V-USB has also been ported to the IAR compiler and assembler. It |
has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the |
"small" and "tiny" memory model. Not every release is tested with IAR CC and |
the driver may therefore fail to compile with IAR. Please note that gcc is |
more efficient for usbdrv.c because this module has been deliberately |
optimized for gcc. |
Gcc version 3 produces smaller code than version 4 due to new optimizing |
capabilities which don't always improve things on 8 bit CPUs. The code size |
generated by gcc 4 can be reduced with the compiler options |
-fno-move-loop-invariants, -fno-tree-scev-cprop and |
-fno-inline-small-functions in addition to -Os. On devices with more than |
8k of flash memory, we also recommend the linker option --relax (written as |
-Wl,--relax for gcc) to convert absolute calls into relative where possible. |
USING AVR-USB FOR FREE |
====================== |
For more information about optimizing options see: |
http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html |
These optimizations are good for gcc 4.x. Version 3.x of gcc does not support |
most of these options and produces good code anyway. |
USING V-USB FOR FREE |
==================== |
The AVR firmware driver is published under the GNU General Public License |
Version 2 (GPL2). See the file "License.txt" for details. |
Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is |
your choice whether you apply the terms of version 2 or version 3. |
If you decide for the free GPL2, we STRONGLY ENCOURAGE you to do the following |
things IN ADDITION to the obligations from the GPL2: |
If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the |
following things IN ADDITION to the obligations from the GPL: |
(1) Publish your entire project on a web site and drop us a note with the URL. |
Use the form at http://www.obdev.at/avrusb/feedback.html for your submission. |
Use the form at http://www.obdev.at/vusb/feedback.html for your submission. |
If you don't have a web site, you can publish the project in obdev's |
documentation wiki at |
http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. |
(2) Adhere to minimum publication standards. Please include AT LEAST: |
- a circuit diagram in PDF, PNG or GIF format |
69,16 → 157,16 |
- full source code for the host software |
- a Readme.txt file in ASCII format which describes the purpose of the |
project and what can be found in which directories and which files |
- a reference to http://www.obdev.at/avrusb/ |
- a reference to http://www.obdev.at/vusb/ |
(3) If you improve the driver firmware itself, please give us a free license |
to your modifications for our commercial license offerings. |
COMMERCIAL LICENSES FOR AVR-USB |
=============================== |
If you don't want to publish your source code under the terms of the GPL2, |
you can simply pay money for AVR-USB. As an additional benefit you get |
USB PIDs for free, licensed exclusively to you. See the file |
COMMERCIAL LICENSES FOR V-USB |
============================= |
If you don't want to publish your source code under the terms of the GPL, |
you can simply pay money for V-USB. As an additional benefit you get |
USB PIDs for free, reserved exclusively to you. See the file |
"CommercialLicense.txt" for details. |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/oddebug.c |
---|
4,8 → 4,7 |
* Creation Date: 2005-01-16 |
* Tabsize: 4 |
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: oddebug.c,v 1.2 2007/05/19 12:30:11 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
#include "oddebug.h" |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/oddebug.h |
---|
4,8 → 4,7 |
* Creation Date: 2005-01-16 |
* Tabsize: 4 |
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: oddebug.h,v 1.2 2007/05/19 12:30:11 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
#ifndef __oddebug_h_included__ |
29,10 → 28,7 |
#endif |
/* make sure we have the UART defines: */ |
#include "iarcompat.h" |
#ifndef __IAR_SYSTEMS_ICC__ |
# include <avr/io.h> |
#endif |
#include "usbportability.h" |
#ifndef uchar |
# define uchar unsigned char |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/usbconfig-prototype.h |
---|
1,11 → 1,10 |
/* Name: usbconfig.h |
* Project: AVR USB driver |
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
* Author: Christian Starkjohann |
* Creation Date: 2005-04-01 |
* Tabsize: 4 |
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: usbconfig-prototype.h 275 2007-03-20 09:58:28Z cs $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
#ifndef __usbconfig_h_included__ |
14,12 → 13,14 |
/* |
General Description: |
This file is an example configuration (with inline documentation) for the USB |
driver. It configures AVR-USB for an ATMega8 with USB D+ connected to Port D |
bit 2 (which is also hardware interrupt 0) and USB D- to Port D bit 0. You may |
wire the lines to any other port, as long as D+ is also wired to INT0. |
To create your own usbconfig.h file, copy this file to the directory |
containing "usbdrv" (that is your project firmware source directory) and |
rename it to "usbconfig.h". Then edit it accordingly. |
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is |
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may |
wire the lines to any other port, as long as D+ is also wired to INT0 (or any |
other hardware interrupt, as long as it is the highest level interrupt, see |
section at the end of this file). |
+ To create your own usbconfig.h file, copy this file to your project's |
+ firmware source directory) and rename it to "usbconfig.h". |
+ Then edit it accordingly. |
*/ |
/* ---------------------------- Hardware Config ---------------------------- */ |
28,7 → 29,7 |
/* This is the port where the USB bus is connected. When you configure it to |
* "B", the registers PORTB, PINB and DDRB will be used. |
*/ |
#define USB_CFG_DMINUS_BIT 0 |
#define USB_CFG_DMINUS_BIT 4 |
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. |
* This may be any bit in the port. |
*/ |
35,8 → 36,27 |
#define USB_CFG_DPLUS_BIT 2 |
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. |
* This may be any bit in the port. Please note that D+ must also be connected |
* to interrupt pin INT0! |
* to interrupt pin INT0! [You can also use other interrupts, see section |
* "Optional MCU Description" below, or you can connect D- to the interrupt, as |
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the |
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame |
* markers every millisecond.] |
*/ |
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, |
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code |
* require no crystal, they tolerate +/- 1% deviation from the nominal |
* frequency. All other rates require a precision of 2000 ppm and thus a |
* crystal! |
* Since F_CPU should be defined to your actual clock rate anyway, you should |
* not need to modify this setting. |
*/ |
#define USB_CFG_CHECK_CRC 0 |
/* Define this to 1 if you want that the driver checks integrity of incoming |
* data packets (CRC checks). CRC checks cost quite a bit of code size and are |
* currently only available for 18 MHz crystal clock. You must choose |
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option. |
*/ |
/* ----------------------- Optional Hardware Config ------------------------ */ |
54,15 → 74,27 |
/* --------------------------- Functional Range ---------------------------- */ |
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 |
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 |
/* Define this to 1 if you want to compile a version with two endpoints: The |
* default control endpoint 0 and an interrupt-in endpoint 1. |
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint |
* number). |
*/ |
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 |
/* Define this to 1 if you want to compile a version with three endpoints: The |
* default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in |
* endpoint 3. You must also enable endpoint 1 above. |
* default control endpoint 0, an interrupt-in endpoint 3 (or the number |
* configured below) and a catch-all default interrupt-in endpoint as above. |
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. |
*/ |
#define USB_CFG_EP3_NUMBER 3 |
/* If the so-called endpoint 3 is used, it can now be configured to any other |
* endpoint number (except 0) with this macro. Default if undefined is 3. |
*/ |
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ |
/* The above macro defines the startup condition for data toggling on the |
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. |
* Since the token is toggled BEFORE sending any data, the first packet is |
* sent with the oposite value of this configuration! |
*/ |
#define USB_CFG_IMPLEMENT_HALT 0 |
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature |
* for endpoint 1 (interrupt endpoint). Although you may not need this feature, |
69,7 → 101,15 |
* it is required by the standard. We have made it a config option because it |
* bloats the code considerably. |
*/ |
#define USB_CFG_INTR_POLL_INTERVAL 20 |
#define USB_CFG_SUPPRESS_INTR_CODE 0 |
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't |
* want to send any data over them. If this macro is defined to 1, functions |
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if |
* you need the interrupt-in endpoints in order to comply to an interface |
* (e.g. HID), but never want to send any data. This option saves a couple |
* of bytes in flash memory and the transmit buffers in RAM. |
*/ |
#define USB_CFG_INTR_POLL_INTERVAL 10 |
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll |
* interval. The value is in milliseconds and must not be less than 10 ms for |
* low speed devices. |
95,9 → 135,10 |
* usbFunctionSetup(). This saves a couple of bytes. |
*/ |
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 |
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1. |
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. |
* You must implement the function usbFunctionWriteOut() which receives all |
* interrupt/bulk data sent to endpoint 1. |
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number |
* can be found in 'usbRxToken'. |
*/ |
#define USB_CFG_HAVE_FLOWCONTROL 0 |
/* Define this to 1 if you want flowcontrol over USB data. See the definition |
104,43 → 145,117 |
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in |
* usbdrv.h. |
*/ |
#define USB_CFG_DRIVER_FLASH_PAGE 0 |
/* If the device has more than 64 kBytes of flash, define this to the 64 k page |
* where the driver's constants (descriptors) are located. Or in other words: |
* Define this to 1 for boot loaders on the ATMega128. |
*/ |
#define USB_CFG_LONG_TRANSFERS 0 |
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes |
* in a single control-in or control-out transfer. Note that the capability |
* for long transfers increases the driver size. |
*/ |
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ |
/* This macro is a hook if you want to do unconventional things. If it is |
* defined, it's inserted at the beginning of received message processing. |
* If you eat the received message and don't want default processing to |
* proceed, do a return after doing your things. One possible application |
* (besides debugging) is to flash a status LED on each packet. |
*/ |
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ |
/* This macro is a hook if you need to know when an USB RESET occurs. It has |
* one parameter which distinguishes between the start of RESET state and its |
* end. |
*/ |
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ |
/* This macro (if defined) is executed when a USB SET_ADDRESS request was |
* received. |
*/ |
#define USB_COUNT_SOF 0 |
/* define this macro to 1 if you need the global variable "usbSofCount" which |
* counts SOF packets. This feature requires that the hardware interrupt is |
* connected to D- instead of D+. |
*/ |
/* #ifdef __ASSEMBLER__ |
* macro myAssemblerMacro |
* in YL, TCNT0 |
* sts timer0Snapshot, YL |
* endm |
* #endif |
* #define USB_SOF_HOOK myAssemblerMacro |
* This macro (if defined) is executed in the assembler module when a |
* Start Of Frame condition is detected. It is recommended to define it to |
* the name of an assembler macro which is defined here as well so that more |
* than one assembler instruction can be used. The macro may use the register |
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages |
* immediately after an SOF pulse may be lost and must be retried by the host. |
* What can you do with this hook? Since the SOF signal occurs exactly every |
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in |
* designs running on the internal RC oscillator. |
* Please note that Start Of Frame detection works only if D- is wired to the |
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! |
*/ |
#define USB_CFG_CHECK_DATA_TOGGLING 0 |
/* define this macro to 1 if you want to filter out duplicate data packets |
* sent by the host. Duplicates occur only as a consequence of communication |
* errors, when the host does not receive an ACK. Please note that you need to |
* implement the filtering yourself in usbFunctionWriteOut() and |
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable |
* for each control- and out-endpoint to check for duplicate packets. |
*/ |
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 |
/* define this macro to 1 if you want the function usbMeasureFrameLength() |
* compiled in. This function can be used to calibrate the AVR's RC oscillator. |
*/ |
#define USB_USE_FAST_CRC 0 |
/* The assembler module has two implementations for the CRC algorithm. One is |
* faster, the other is smaller. This CRC routine is only used for transmitted |
* messages where timing is not critical. The faster routine needs 31 cycles |
* per byte while the smaller one needs 61 to 69 cycles. The faster routine |
* may be worth the 32 bytes bigger code size if you transmit lots of data and |
* run the AVR close to its limit. |
*/ |
/* -------------------------- Device Description --------------------------- */ |
#define USB_CFG_VENDOR_ID 0xc0, 0x16 |
#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ |
/* USB vendor ID for the device, low byte first. If you have registered your |
* own Vendor ID, define it here. Otherwise you use obdev's free shared |
* VID/PID pair. Be sure to read USBID-License.txt for rules! |
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. |
* Use this VID/PID pair ONLY if you understand the implications! |
* own Vendor ID, define it here. Otherwise you may use one of obdev's free |
* shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! |
* *** IMPORTANT NOTE *** |
* This template uses obdev's shared VID/PID pair for Vendor Class devices |
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand |
* the implications! |
*/ |
#define USB_CFG_DEVICE_ID 0xdf, 0x05 |
#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */ |
/* This is the ID of the product, low byte first. It is interpreted in the |
* scope of the vendor ID. If you have registered your own VID with usb.org |
* or if you have licensed a PID from somebody else, define it here. Otherwise |
* you use obdev's free shared VID/PID pair. Be sure to read the rules in |
* USBID-License.txt! |
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. |
* Use this VID/PID pair ONLY if you understand the implications! |
* you may use one of obdev's free shared VID/PID pairs. See the file |
* USB-IDs-for-free.txt for details! |
* *** IMPORTANT NOTE *** |
* This template uses obdev's shared VID/PID pair for Vendor Class devices |
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand |
* the implications! |
*/ |
#define USB_CFG_DEVICE_VERSION 0x00, 0x01 |
/* Version number of the device: Minor number first, then major number. |
*/ |
#define USB_CFG_VENDOR_NAME 'w', 'w', 'w', '.', 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' |
#define USB_CFG_VENDOR_NAME_LEN 12 |
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' |
#define USB_CFG_VENDOR_NAME_LEN 8 |
/* These two values define the vendor name returned by the USB device. The name |
* must be given as a list of characters under single quotes. The characters |
* are interpreted as Unicode (UTF-16) entities. |
* If you don't want a vendor name string, undefine these macros. |
* ALWAYS define a vendor name containing your Internet domain name if you use |
* obdev's free shared VID/PID pair. See the file USBID-License.txt for |
* obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for |
* details. |
*/ |
#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' |
#define USB_CFG_DEVICE_NAME_LEN 8 |
/* Same as above for the device name. If you don't want a device name, undefine |
* the macros. See the file USBID-License.txt before you assign a name if you |
* use a shared VID/PID. |
* the macros. See the file USB-IDs-for-free.txt before you assign a name if |
* you use a shared VID/PID. |
*/ |
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ |
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ |
151,27 → 266,32 |
* to fine tune control over USB descriptors such as the string descriptor |
* for the serial number. |
*/ |
#define USB_CFG_DEVICE_CLASS 0 |
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */ |
#define USB_CFG_DEVICE_SUBCLASS 0 |
/* See USB specification if you want to conform to an existing device class. |
* Class 0xff is "vendor specific". |
*/ |
#define USB_CFG_INTERFACE_CLASS 3 /* HID */ |
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */ |
#define USB_CFG_INTERFACE_SUBCLASS 0 |
#define USB_CFG_INTERFACE_PROTOCOL 0 |
/* See USB specification if you want to conform to an existing device class or |
* protocol. |
* This template defines a HID class device. If you implement a vendor class |
* device, set USB_CFG_INTERFACE_CLASS to 0 and USB_CFG_DEVICE_CLASS to 0xff. |
* protocol. The following classes must be set at interface level: |
* HID class is 3, no subclass and protocol required (but may be useful!) |
* CDC class is 2, use subclass 2 and protocol 1 for ACM |
*/ |
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 /* total length of report descriptor */ |
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */ |
/* Define this to the length of the HID report descriptor, if you implement |
* an HID device. Otherwise don't define it or define it to 0. |
* Since this template defines a HID device, it must also specify a HID |
* report descriptor length. You must add a PROGMEM character array named |
* If you use this define, you must add a PROGMEM character array named |
* "usbHidReportDescriptor" to your code which contains the report descriptor. |
* Don't forget to keep the array and this define in sync! |
*/ |
/* #define USB_PUBLIC static */ |
/* Use the define above if you #include usbdrv.c instead of linking against it. |
* This technique saves a couple of bytes in flash memory. |
*/ |
/* ------------------- Fine Control over USB Descriptors ------------------- */ |
/* If you don't want to use the driver's default USB descriptors, you can |
* provide our own. These can be provided as (1) fixed length static data in |
182,7 → 302,9 |
* no properties are defined or if they are 0, the default descriptor is used. |
* Possible properties are: |
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched |
* at runtime via usbFunctionDescriptor(). |
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is |
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if |
* you want RAM pointers. |
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found |
* in static memory is in RAM, not in flash memory. |
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), |
214,6 → 336,12 |
* USB_CFG_DESCR_PROPS_HID_REPORT |
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) |
* |
* Note about string descriptors: String descriptors are not just strings, they |
* are Unicode strings prefixed with a 2 byte header. Example: |
* int serialNumberDescriptor[] = { |
* USB_STRING_DESCRIPTOR_HEADER(6), |
* 'S', 'e', 'r', 'i', 'a', 'l' |
* }; |
*/ |
#define USB_CFG_DESCR_PROPS_DEVICE 0 |
227,6 → 355,15 |
#define USB_CFG_DESCR_PROPS_HID_REPORT 0 |
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 |
#define usbMsgPtr_t unsigned short |
/* If usbMsgPtr_t is not defined, it defaults to 'uchar *'. We define it to |
* a scalar type here because gcc generates slightly shorter code for scalar |
* arithmetics than for pointer arithmetics. Remove this define for backward |
* type compatibility or define it to an 8 bit type if you use data in RAM only |
* and all RAM is below 256 bytes (tiny memory model in IAR CC). |
*/ |
/* ----------------------- Optional MCU Description ------------------------ */ |
/* The following configurations have working defaults in usbdrv.h. You |
242,5 → 379,6 |
/* #define USB_INTR_ENABLE_BIT INT0 */ |
/* #define USB_INTR_PENDING GIFR */ |
/* #define USB_INTR_PENDING_BIT INTF0 */ |
/* #define USB_INTR_VECTOR INT0_vect */ |
#endif /* __usbconfig_h_included__ */ |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/usbdrv.c |
---|
1,18 → 1,12 |
/* Name: usbdrv.c |
* Project: AVR USB driver |
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
* Author: Christian Starkjohann |
* Creation Date: 2004-12-29 |
* Tabsize: 4 |
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: usbdrv.c,v 1.3 2007/06/07 13:53:47 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
#include "iarcompat.h" |
#ifndef __IAR_SYSTEMS_ICC__ |
# include <avr/io.h> |
# include <avr/pgmspace.h> |
#endif |
#include "usbdrv.h" |
#include "oddebug.h" |
22,14 → 16,6 |
documentation of the entire driver. |
*/ |
#ifndef IAR_SECTION |
#define IAR_SECTION(arg) |
#define __no_init |
#endif |
/* The macro IAR_SECTION is a hack to allow IAR-cc compatibility. On gcc, it |
* is defined to nothing. __no_init is required on IAR. |
*/ |
/* ------------------------------------------------------------------------- */ |
/* raw USB registers / interface to assembler code: */ |
39,38 → 25,40 |
uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ |
uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ |
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ |
uchar usbCurrentTok; /* last token received, if more than 1 rx endpoint: MSb=endpoint */ |
uchar usbRxToken; /* token for data we received; if more than 1 rx endpoint: MSb=endpoint */ |
uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ |
uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ |
uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ |
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ |
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
volatile uchar usbTxLen1 = USBPID_NAK; /* TX count for endpoint 1 */ |
uchar usbTxBuf1[USB_BUFSIZE]; /* TX data for endpoint 1 */ |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
volatile uchar usbTxLen3 = USBPID_NAK; /* TX count for endpoint 1 */ |
uchar usbTxBuf3[USB_BUFSIZE]; /* TX data for endpoint 1 */ |
#if USB_COUNT_SOF |
volatile uchar usbSofCount; /* incremented by assembler module every SOF */ |
#endif |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
usbTxStatus_t usbTxStatus1; |
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
usbTxStatus_t usbTxStatus3; |
# endif |
#endif |
#if USB_CFG_CHECK_DATA_TOGGLING |
uchar usbCurrentDataToken;/* when we check data toggling to ignore duplicate packets */ |
#endif |
/* USB status registers / not shared with asm code */ |
uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ |
static uchar usbMsgFlags; /* flag values see below */ |
usbMsgPtr_t usbMsgPtr; /* data to transmit next -- ROM or RAM address */ |
static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ |
static uchar usbMsgFlags; /* flag values see below */ |
#define USB_FLG_TX_PACKET (1<<0) |
/* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ |
#define USB_FLG_MSGPTR_IS_ROM (1<<6) |
#define USB_FLG_USE_DEFAULT_RW (1<<7) |
#define USB_FLG_USE_USER_RW (1<<7) |
/* |
optimizing hints: |
- do not post/pre inc/dec integer values in operations |
- assign value of PRG_RDB() to register variables and don't use side effects in arg |
- assign value of USB_READ_FLASH() to register variables and don't use side effects in arg |
- use narrow scope for variables which should be in X/Y/Z register |
- assign char sized expressions to variables to force 8 bit arithmetics |
*/ |
/* ------------------------------------------------------------------------- */ |
/* -------------------------- String Descriptors --------------------------- */ |
#if USB_CFG_DESCR_PROPS_STRINGS == 0 |
77,7 → 65,7 |
#if USB_CFG_DESCR_PROPS_STRING_0 == 0 |
#undef USB_CFG_DESCR_PROPS_STRING_0 |
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) |
PROGMEM char usbDescriptorString0[] = { /* language descriptor */ |
PROGMEM const char usbDescriptorString0[] = { /* language descriptor */ |
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ |
3, /* descriptor type */ |
0x09, 0x04, /* language index (0x0409 = US-English) */ |
87,16 → 75,16 |
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN |
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR |
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) |
PROGMEM int usbDescriptorStringVendor[] = { |
PROGMEM const int usbDescriptorStringVendor[] = { |
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), |
USB_CFG_VENDOR_NAME |
}; |
#endif |
#if USB_CFG_DESCR_PROPS_STRING_DEVICE == 0 && USB_CFG_DEVICE_NAME_LEN |
#undef USB_CFG_DESCR_PROPS_STRING_DEVICE |
#define USB_CFG_DESCR_PROPS_STRING_DEVICE sizeof(usbDescriptorStringDevice) |
PROGMEM int usbDescriptorStringDevice[] = { |
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN |
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) |
PROGMEM const int usbDescriptorStringDevice[] = { |
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), |
USB_CFG_DEVICE_NAME |
}; |
105,7 → 93,7 |
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN |
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) |
PROGMEM int usbDescriptorStringSerialNumber[] = { |
PROGMEM const int usbDescriptorStringSerialNumber[] = { |
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), |
USB_CFG_SERIAL_NUMBER |
}; |
113,10 → 101,12 |
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ |
/* --------------------------- Device Descriptor --------------------------- */ |
#if USB_CFG_DESCR_PROPS_DEVICE == 0 |
#undef USB_CFG_DESCR_PROPS_DEVICE |
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) |
PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */ |
PROGMEM const char usbDescriptorDevice[] = { /* USB device descriptor */ |
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ |
USBDESCR_DEVICE, /* descriptor type */ |
0x10, 0x01, /* USB version supported */ |
124,16 → 114,21 |
USB_CFG_DEVICE_SUBCLASS, |
0, /* protocol */ |
8, /* max packet size */ |
USB_CFG_VENDOR_ID, /* 2 bytes */ |
USB_CFG_DEVICE_ID, /* 2 bytes */ |
/* the following two casts affect the first byte of the constant only, but |
* that's sufficient to avoid a warning with the default values. |
*/ |
(char)USB_CFG_VENDOR_ID,/* 2 bytes */ |
(char)USB_CFG_DEVICE_ID,/* 2 bytes */ |
USB_CFG_DEVICE_VERSION, /* 2 bytes */ |
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ |
USB_CFG_DESCR_PROPS_STRING_DEVICE != 0 ? 2 : 0, /* product string index */ |
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ |
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ |
1, /* number of configurations */ |
}; |
#endif |
/* ----------------------- Configuration Descriptor ------------------------ */ |
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 |
#undef USB_CFG_DESCR_PROPS_HID |
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ |
142,18 → 137,19 |
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 |
#undef USB_CFG_DESCR_PROPS_CONFIGURATION |
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) |
PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ |
PROGMEM const char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ |
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ |
USBDESCR_CONFIG, /* descriptor type */ |
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0, |
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT3 + |
(USB_CFG_DESCR_PROPS_HID & 0xff), 0, |
/* total length of data returned (including inlined descriptors) */ |
1, /* number of interfaces in this configuration */ |
1, /* index of this configuration */ |
0, /* configuration name string index */ |
#if USB_CFG_IS_SELF_POWERED |
USBATTR_SELFPOWER, /* attributes */ |
(1 << 7) | USBATTR_SELFPOWER, /* attributes */ |
#else |
USBATTR_BUSPOWER, /* attributes */ |
(1 << 7), /* attributes */ |
#endif |
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ |
/* interface descriptor follows inline: */ |
161,7 → 157,7 |
USBDESCR_INTERFACE, /* descriptor type */ |
0, /* index of this interface */ |
0, /* alternate setting for this interface */ |
USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */ |
USB_CFG_HAVE_INTRIN_ENDPOINT + USB_CFG_HAVE_INTRIN_ENDPOINT3, /* endpoints excl 0: number of endpoint descriptors to follow */ |
USB_CFG_INTERFACE_CLASS, |
USB_CFG_INTERFACE_SUBCLASS, |
USB_CFG_INTERFACE_PROTOCOL, |
178,321 → 174,404 |
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ |
7, /* sizeof(usbDescrEndpoint) */ |
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
0x81, /* IN endpoint number 1 */ |
(char)0x81, /* IN endpoint number 1 */ |
0x03, /* attrib: Interrupt endpoint */ |
8, 0, /* maximum packet size */ |
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
#endif |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 /* endpoint descriptor for endpoint 3 */ |
7, /* sizeof(usbDescrEndpoint) */ |
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
(char)(0x80 | USB_CFG_EP3_NUMBER), /* IN endpoint number 3 */ |
0x03, /* attrib: Interrupt endpoint */ |
8, 0, /* maximum packet size */ |
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
#endif |
}; |
#endif |
/* We don't use prog_int or prog_int16_t for compatibility with various libc |
* versions. Here's an other compatibility hack: |
*/ |
#ifndef PRG_RDB |
#define PRG_RDB(addr) pgm_read_byte(addr) |
/* ------------------------------------------------------------------------- */ |
static inline void usbResetDataToggling(void) |
{ |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
# endif |
#endif |
} |
typedef union{ |
unsigned word; |
uchar *ptr; |
uchar bytes[2]; |
}converter_t; |
/* We use this union to do type conversions. This is better optimized than |
* type casts in gcc 3.4.3 and much better than using bit shifts to build |
* ints from chars. Byte ordering is not a problem on an 8 bit platform. |
*/ |
static inline void usbResetStall(void) |
{ |
#if USB_CFG_IMPLEMENT_HALT && USB_CFG_HAVE_INTRIN_ENDPOINT |
usbTxLen1 = USBPID_NAK; |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
usbTxLen3 = USBPID_NAK; |
#endif |
#endif |
} |
/* ------------------------------------------------------------------------- */ |
#if !USB_CFG_SUPPRESS_INTR_CODE |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
void usbSetInterrupt(uchar *data, uchar len) |
static void usbGenericSetInterrupt(uchar *data, uchar len, usbTxStatus_t *txStatus) |
{ |
uchar *p, i; |
uchar *p; |
char i; |
#if USB_CFG_IMPLEMENT_HALT |
if(usbTxLen1 == USBPID_STALL) |
return; |
#endif |
#if 0 /* No runtime checks! Caller is responsible for valid data! */ |
if(len > 8) /* interrupt transfers are limited to 8 bytes */ |
len = 8; |
#endif |
if(usbTxLen1 & 0x10){ /* packet buffer was empty */ |
usbTxBuf1[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
if(txStatus->len & 0x10){ /* packet buffer was empty */ |
txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
}else{ |
usbTxLen1 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
} |
p = usbTxBuf1 + 1; |
for(i=len;i--;) |
p = txStatus->buffer + 1; |
i = len; |
do{ /* if len == 0, we still copy 1 byte, but that's no problem */ |
*p++ = *data++; |
usbCrc16Append(&usbTxBuf1[1], len); |
usbTxLen1 = len + 4; /* len must be given including sync byte */ |
DBG2(0x21, usbTxBuf1, len + 3); |
}while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ |
usbCrc16Append(&txStatus->buffer[1], len); |
txStatus->len = len + 4; /* len must be given including sync byte */ |
DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); |
} |
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) |
{ |
usbGenericSetInterrupt(data, len, &usbTxStatus1); |
} |
#endif |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
void usbSetInterrupt3(uchar *data, uchar len) |
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) |
{ |
uchar *p, i; |
if(usbTxLen3 & 0x10){ /* packet buffer was empty */ |
usbTxBuf3[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
}else{ |
usbTxLen3 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
} |
p = usbTxBuf3 + 1; |
for(i=len;i--;) |
*p++ = *data++; |
usbCrc16Append(&usbTxBuf3[1], len); |
usbTxLen3 = len + 4; /* len must be given including sync byte */ |
DBG2(0x23, usbTxBuf3, len + 3); |
usbGenericSetInterrupt(data, len, &usbTxStatus3); |
} |
#endif |
#endif /* USB_CFG_SUPPRESS_INTR_CODE */ |
/* ------------------ utilities for code following below ------------------- */ |
static uchar usbRead(uchar *data, uchar len) |
{ |
#if USB_CFG_IMPLEMENT_FN_READ |
if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ |
/* Use defines for the switch statement so that we can choose between an |
* if()else if() and a switch/case based implementation. switch() is more |
* efficient for a LARGE set of sequential choices, if() is better in all other |
* cases. |
*/ |
#if USB_CFG_USE_SWITCH_STATEMENT |
# define SWITCH_START(cmd) switch(cmd){{ |
# define SWITCH_CASE(value) }break; case (value):{ |
# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ |
# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ |
# define SWITCH_DEFAULT }break; default:{ |
# define SWITCH_END }} |
#else |
# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ |
# define SWITCH_CASE(value) }else if(_cmd == (value)){ |
# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ |
# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ |
# define SWITCH_DEFAULT }else{ |
# define SWITCH_END }} |
#endif |
uchar i = len, *r = usbMsgPtr; |
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ |
while(i--){ |
uchar c = PRG_RDB(r); /* assign to char size variable to enforce byte ops */ |
*data++ = c; |
r++; |
} |
}else{ /* RAM data */ |
while(i--) |
*data++ = *r++; |
} |
usbMsgPtr = r; |
return len; |
#if USB_CFG_IMPLEMENT_FN_READ |
}else{ |
if(len != 0) /* don't bother app with 0 sized reads */ |
return usbFunctionRead(data, len); |
return 0; |
} |
#ifndef USB_RX_USER_HOOK |
#define USB_RX_USER_HOOK(data, len) |
#endif |
} |
#ifndef USB_SET_ADDRESS_HOOK |
#define USB_SET_ADDRESS_HOOK() |
#endif |
/* ------------------------------------------------------------------------- */ |
/* We use if() instead of #if in the macro below because #if can't be used |
* in macros and the compiler optimizes constant conditions anyway. |
* This may cause problems with undefined symbols if compiled without |
* optimizing! |
*/ |
#define GET_DESCRIPTOR(cfgProp, staticName) \ |
if(cfgProp){ \ |
if((cfgProp) & USB_PROP_IS_RAM) \ |
flags &= ~USB_FLG_MSGPTR_IS_ROM; \ |
flags = 0; \ |
if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ |
replyLen = usbFunctionDescriptor(rq); \ |
len = usbFunctionDescriptor(rq); \ |
}else{ \ |
replyData = (uchar *)(staticName); \ |
SET_REPLY_LEN((cfgProp) & 0xff); \ |
len = USB_PROP_LENGTH(cfgProp); \ |
usbMsgPtr = (usbMsgPtr_t)(staticName); \ |
} \ |
} |
/* We use if() instead of #if in the macro above because #if can't be used |
* in macros and the compiler optimizes constant conditions anyway. |
/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used |
* internally for all types of descriptors. |
*/ |
static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) |
{ |
usbMsgLen_t len = 0; |
uchar flags = USB_FLG_MSGPTR_IS_ROM; |
SWITCH_START(rq->wValue.bytes[1]) |
SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) |
SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) |
SWITCH_CASE(USBDESCR_STRING) /* 3 */ |
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC |
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) |
flags = 0; |
len = usbFunctionDescriptor(rq); |
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
SWITCH_START(rq->wValue.bytes[0]) |
SWITCH_CASE(0) |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) |
SWITCH_CASE(1) |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) |
SWITCH_CASE(2) |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) |
SWITCH_CASE(3) |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) |
SWITCH_DEFAULT |
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
len = usbFunctionDescriptor(rq); |
} |
SWITCH_END |
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ |
SWITCH_CASE(USBDESCR_HID) /* 0x21 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) |
SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) |
#endif |
SWITCH_DEFAULT |
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
len = usbFunctionDescriptor(rq); |
} |
SWITCH_END |
usbMsgFlags = flags; |
return len; |
} |
/* Don't make this function static to avoid inlining. |
* The entire function would become too large and exceed the range of |
* relative jumps. |
* 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment |
* above was written, or other parts of the code have changed. We now get |
* better results with an inlined function. Test condition: PowerSwitch code. |
/* ------------------------------------------------------------------------- */ |
/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for |
* standard requests instead of class and custom requests. |
*/ |
static void usbProcessRx(uchar *data, uchar len) |
static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) |
{ |
usbMsgLen_t len = 0; |
uchar *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ |
uchar value = rq->wValue.bytes[0]; |
#if USB_CFG_IMPLEMENT_HALT |
uchar index = rq->wIndex.bytes[0]; |
#endif |
dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ |
SWITCH_START(rq->bRequest) |
SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ |
uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ |
if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) |
dataPtr[0] = USB_CFG_IS_SELF_POWERED; |
#if USB_CFG_IMPLEMENT_HALT |
if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ |
dataPtr[0] = usbTxLen1 == USBPID_STALL; |
#endif |
dataPtr[1] = 0; |
len = 2; |
#if USB_CFG_IMPLEMENT_HALT |
SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ |
if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ |
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; |
usbResetDataToggling(); |
} |
#endif |
SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ |
usbNewDeviceAddr = value; |
USB_SET_ADDRESS_HOOK(); |
SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ |
len = usbDriverDescriptor(rq); |
goto skipMsgPtrAssignment; |
SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ |
dataPtr = &usbConfiguration; /* send current configuration value */ |
len = 1; |
SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ |
usbConfiguration = value; |
usbResetStall(); |
SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ |
len = 1; |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ |
usbResetDataToggling(); |
usbResetStall(); |
#endif |
SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ |
/* Should we add an optional hook here? */ |
SWITCH_END |
usbMsgPtr = (usbMsgPtr_t)dataPtr; |
skipMsgPtrAssignment: |
return len; |
} |
/* ------------------------------------------------------------------------- */ |
/* usbProcessRx() is called for every message received by the interrupt |
* routine. It distinguishes between SETUP and DATA packets and processes |
* them accordingly. |
*/ |
static inline void usbProcessRx(uchar *data, uchar len) |
{ |
usbRequest_t *rq = (void *)data; |
uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; |
/* We use if() cascades because the compare is done byte-wise while switch() |
* is int-based. The if() cascades are therefore more efficient. |
*/ |
/* usbRxToken can be: |
* 0x2d 00101101 (USBPID_SETUP for endpoint 0) |
* 0xe1 11100001 (USBPID_OUT for endpoint 0) |
* 0xff 11111111 (USBPID_OUT for endpoint 1) |
* 0x2d 00101101 (USBPID_SETUP for setup data) |
* 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) |
* 0...0x0f for OUT on endpoint X |
*/ |
DBG2(0x10 + ((usbRxToken >> 1) & 3), data, len); /* SETUP0=12; OUT0=10; OUT1=13 */ |
DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ |
USB_RX_USER_HOOK(data, len) |
#if USB_CFG_IMPLEMENT_FN_WRITEOUT |
if(usbRxToken == 0xff){ |
if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ |
usbFunctionWriteOut(data, len); |
return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */ |
return; |
} |
#endif |
if(usbRxToken == (uchar)USBPID_SETUP){ |
usbTxLen = USBPID_NAK; /* abort pending transmit */ |
if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */ |
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; |
if(type == USBRQ_TYPE_STANDARD){ |
#define SET_REPLY_LEN(len) replyLen = (len); usbMsgPtr = replyData |
/* This macro ensures that replyLen and usbMsgPtr are always set in the same way. |
* That allows optimization of common code in if() branches */ |
uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ |
replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ |
if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */ |
uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ |
#if USB_CFG_IS_SELF_POWERED |
if(recipient == USBRQ_RCPT_DEVICE) |
replyData[0] = USB_CFG_IS_SELF_POWERED; |
#endif |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT |
if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */ |
replyData[0] = usbTxLen1 == USBPID_STALL; |
#endif |
replyData[1] = 0; |
SET_REPLY_LEN(2); |
}else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */ |
usbNewDeviceAddr = rq->wValue.bytes[0]; |
}else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ |
flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; |
if(rq->wValue.bytes[1] == USBDESCR_DEVICE){ /* 1 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) |
}else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){ /* 2 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) |
}else if(rq->wValue.bytes[1] == USBDESCR_STRING){ /* 3 */ |
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC |
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) |
flags &= ~USB_FLG_MSGPTR_IS_ROM; |
replyLen = usbFunctionDescriptor(rq); |
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
if(rq->wValue.bytes[0] == 0){ /* descriptor index */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) |
}else if(rq->wValue.bytes[0] == 1){ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) |
}else if(rq->wValue.bytes[0] == 2){ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_DEVICE, usbDescriptorStringDevice) |
}else if(rq->wValue.bytes[0] == 3){ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) |
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
replyLen = usbFunctionDescriptor(rq); |
} |
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
}else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) |
}else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */ |
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) |
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
replyLen = usbFunctionDescriptor(rq); |
} |
}else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */ |
replyData = &usbConfiguration; /* send current configuration value */ |
SET_REPLY_LEN(1); |
}else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */ |
usbConfiguration = rq->wValue.bytes[0]; |
#if USB_CFG_IMPLEMENT_HALT |
usbTxLen1 = USBPID_NAK; |
#endif |
}else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */ |
SET_REPLY_LEN(1); |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
}else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */ |
USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# endif |
# if USB_CFG_IMPLEMENT_HALT |
usbTxLen1 = USBPID_NAK; |
}else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */ |
if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */ |
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; |
USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# endif |
} |
# endif |
#endif |
if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ |
return; |
usbMsgLen_t replyLen; |
usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ |
usbTxLen = USBPID_NAK; /* abort pending transmit */ |
usbMsgFlags = 0; |
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; |
if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ |
replyLen = usbFunctionSetup(data); |
}else{ |
replyLen = usbDriverSetup(rq); |
} |
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE |
if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ |
/* do some conditioning on replyLen, but on IN transfers only */ |
if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ |
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ |
replyLen = rq->wLength.bytes[0]; |
}else{ |
/* the following requests can be ignored, send default reply */ |
/* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ |
/* 12: SYNCH_FRAME */ |
replyLen = rq->wLength.word; |
} |
#undef SET_REPLY_LEN |
}else{ /* not a standard request -- must be vendor or class request */ |
replyLen = usbFunctionSetup(data); |
} |
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE |
if(replyLen == 0xff){ /* use user-supplied read/write function */ |
if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ |
replyLen = rq->wLength.bytes[0]; /* IN transfers only */ |
} |
flags &= ~USB_FLG_USE_DEFAULT_RW; /* we have no valid msg, use user supplied read/write functions */ |
}else /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */ |
usbMsgFlags = USB_FLG_USE_USER_RW; |
}else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ |
#endif |
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ |
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ |
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ |
replyLen = rq->wLength.bytes[0]; |
}else{ |
if(replyLen > rq->wLength.word) /* limit length to max */ |
replyLen = rq->wLength.word; |
} |
/* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ |
}else{ /* DATA packet from out request */ |
usbMsgLen = replyLen; |
}else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ |
#if USB_CFG_IMPLEMENT_FN_WRITE |
if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ |
if(usbMsgFlags & USB_FLG_USE_USER_RW){ |
uchar rval = usbFunctionWrite(data, len); |
replyLen = 0xff; |
if(rval == 0xff){ /* an error occurred */ |
usbMsgLen = 0xff; /* cancel potentially pending data packet for ACK */ |
if(rval == 0xff){ /* an error occurred */ |
usbTxLen = USBPID_STALL; |
}else if(rval != 0){ /* This was the final package */ |
replyLen = 0; /* answer with a zero-sized data packet */ |
usbMsgLen = 0; /* answer with a zero-sized data packet */ |
} |
flags = 0; /* start with a DATA1 package, stay with user supplied write() function */ |
} |
#endif |
} |
usbMsgFlags = flags; |
usbMsgLen = replyLen; |
} |
/* ------------------------------------------------------------------------- */ |
static void usbBuildTxBlock(void) |
/* This function is similar to usbFunctionRead(), but it's also called for |
* data handled automatically by the driver (e.g. descriptor reads). |
*/ |
static uchar usbDeviceRead(uchar *data, uchar len) |
{ |
uchar wantLen, len, txLen, token; |
if(len > 0){ /* don't bother app with 0 sized reads */ |
#if USB_CFG_IMPLEMENT_FN_READ |
if(usbMsgFlags & USB_FLG_USE_USER_RW){ |
len = usbFunctionRead(data, len); |
}else |
#endif |
{ |
uchar i = len; |
usbMsgPtr_t r = usbMsgPtr; |
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ |
do{ |
uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ |
*data++ = c; |
r++; |
}while(--i); |
}else{ /* RAM data */ |
do{ |
*data++ = *((uchar *)r); |
r++; |
}while(--i); |
} |
usbMsgPtr = r; |
} |
} |
return len; |
} |
/* ------------------------------------------------------------------------- */ |
/* usbBuildTxBlock() is called when we have data to transmit and the |
* interrupt routine's transmit buffer is empty. |
*/ |
static inline void usbBuildTxBlock(void) |
{ |
usbMsgLen_t wantLen; |
uchar len; |
wantLen = usbMsgLen; |
if(wantLen > 8) |
wantLen = 8; |
usbMsgLen -= wantLen; |
token = USBPID_DATA1; |
if(usbMsgFlags & USB_FLG_TX_PACKET) |
token = USBPID_DATA0; |
usbMsgFlags++; |
len = usbRead(usbTxBuf + 1, wantLen); |
usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ |
len = usbDeviceRead(usbTxBuf + 1, wantLen); |
if(len <= 8){ /* valid data packet */ |
usbCrc16Append(&usbTxBuf[1], len); |
txLen = len + 4; /* length including sync byte */ |
if(len < 8) /* a partial package identifies end of message */ |
usbMsgLen = 0xff; |
len += 4; /* length including sync byte */ |
if(len < 12) /* a partial package identifies end of message */ |
usbMsgLen = USB_NO_MSG; |
}else{ |
txLen = USBPID_STALL; /* stall the endpoint */ |
usbMsgLen = 0xff; |
len = USBPID_STALL; /* stall the endpoint */ |
usbMsgLen = USB_NO_MSG; |
} |
usbTxBuf[0] = token; |
usbTxLen = txLen; |
DBG2(0x20, usbTxBuf, txLen-1); |
usbTxLen = len; |
DBG2(0x20, usbTxBuf, len-1); |
} |
static inline uchar isNotSE0(void) |
/* ------------------------------------------------------------------------- */ |
static inline void usbHandleResetHook(uchar notResetState) |
{ |
uchar rval; |
/* We want to do |
* return (USBIN & USBMASK); |
* here, but the compiler does int-expansion acrobatics. |
* We can avoid this by assigning to a char-sized variable. |
*/ |
rval = USBIN & USBMASK; |
return rval; |
#ifdef USB_RESET_HOOK |
static uchar wasReset; |
uchar isReset = !notResetState; |
if(wasReset != isReset){ |
USB_RESET_HOOK(isReset); |
wasReset = isReset; |
} |
#else |
notResetState = notResetState; // avoid compiler warning |
#endif |
} |
/* ------------------------------------------------------------------------- */ |
void usbPoll(void) |
USB_PUBLIC void usbPoll(void) |
{ |
uchar len, i; |
schar len; |
uchar i; |
if((len = usbRxLen) > 0){ |
len = usbRxLen - 3; |
if(len >= 0){ |
/* We could check CRC16 here -- but ACK has already been sent anyway. If you |
* need data integrity checks with this driver, check the CRC in your app |
* code and report errors back to the host. Since the ACK was already sent, |
499,7 → 578,7 |
* retries must be handled on application level. |
* unsigned crc = usbCrc16(buffer + 1, usbRxLen - 3); |
*/ |
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len - 3); |
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); |
#if USB_CFG_HAVE_FLOWCONTROL |
if(usbRxLen > 0) /* only mark as available if not inactivated */ |
usbRxLen = 0; |
507,31 → 586,28 |
usbRxLen = 0; /* mark rx buffer as available */ |
#endif |
} |
if(usbTxLen & 0x10){ /* transmit system idle */ |
if(usbMsgLen != 0xff){ /* transmit data pending? */ |
if(usbTxLen & 0x10){ /* transmit system idle */ |
if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ |
usbBuildTxBlock(); |
} |
} |
for(i = 10; i > 0; i--){ |
if(isNotSE0()) |
break; |
for(i = 20; i > 0; i--){ |
uchar usbLineStatus = USBIN & USBMASK; |
if(usbLineStatus != 0) /* SE0 has ended */ |
goto isNotReset; |
} |
if(i == 0){ /* RESET condition, called multiple times during reset */ |
usbNewDeviceAddr = 0; |
usbDeviceAddr = 0; |
#if USB_CFG_IMPLEMENT_HALT |
usbTxLen1 = USBPID_NAK; |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
usbTxLen3 = USBPID_NAK; |
#endif |
#endif |
DBG1(0xff, 0, 0); |
} |
/* RESET condition, called multiple times during reset */ |
usbNewDeviceAddr = 0; |
usbDeviceAddr = 0; |
usbResetStall(); |
DBG1(0xff, 0, 0); |
isNotReset: |
usbHandleResetHook(i); |
} |
/* ------------------------------------------------------------------------- */ |
void usbInit(void) |
USB_PUBLIC void usbInit(void) |
{ |
#if USB_INTR_CFG_SET != 0 |
USB_INTR_CFG |= USB_INTR_CFG_SET; |
540,12 → 616,13 |
USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); |
#endif |
USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
USB_SET_DATATOKEN1(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
USB_SET_DATATOKEN3(USBPID_DATA0); /* reset data toggling for interrupt endpoint */ |
# endif |
usbResetDataToggling(); |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
usbTxLen1 = USBPID_NAK; |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
usbTxLen3 = USBPID_NAK; |
#endif |
#endif |
} |
/* ------------------------------------------------------------------------- */ |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/usbdrv.h |
---|
1,38 → 1,40 |
/* Name: usbdrv.h |
* Project: AVR USB driver |
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
* Author: Christian Starkjohann |
* Creation Date: 2004-12-29 |
* Tabsize: 4 |
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: usbdrv.h,v 1.2 2007/05/19 12:30:11 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
#ifndef __usbdrv_h_included__ |
#define __usbdrv_h_included__ |
#include "usbconfig.h" |
#include "iarcompat.h" |
#include "usbportability.h" |
/* |
Hardware Prerequisites: |
======================= |
USB lines D+ and D- MUST be wired to the same I/O port. D+ must (also) be |
connected to INT0. D- requires a pullup of 1.5k to +3.5V (and the device |
must be powered at 3.5V) to identify as low-speed USB device. A pullup of |
1M SHOULD be connected from D+ to +3.5V to prevent interference when no USB |
master is connected. We use D+ as interrupt source and not D- because it |
does not trigger on keep-alive and RESET states. |
USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ |
triggers the interrupt (best achieved by using INT0 for D+), but it is also |
possible to trigger the interrupt from D-. If D- is used, interrupts are also |
triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the |
device must be powered at 3.5V) to identify as low-speed USB device. A |
pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent |
interference when no USB master is connected. If you use Zener diodes to limit |
the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. |
We use D+ as interrupt source and not D- because it does not trigger on |
keep-alive and RESET states. If you want to count keep-alive events with |
USB_COUNT_SOF, you MUST use D- as an interrupt source. |
As a compile time option, the 1.5k pullup resistor on D- can be made |
As a compile time option, the 1.5k pull-up resistor on D- can be made |
switchable to allow the device to disconnect at will. See the definition of |
usbDeviceConnect() and usbDeviceDisconnect() further down in this file. |
Please adapt the values in usbconfig.h according to your hardware! |
The device MUST be clocked at 12 MHz. This is more than the 10 MHz allowed by |
an AT90S2313 powered at 4.5V. However, if the supply voltage to maximum clock |
relation is interpolated linearly, an ATtiny2313 meets the requirement by |
specification. In practice, the AT90S2313 can be overclocked and works well. |
The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz |
or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. |
Limitations: |
55,17 → 57,27 |
requires detection of asymmetric states at high bit rate for SE0 detection. |
Number of endpoints: |
The driver supports up to four endpoints: One control endpoint (endpoint 0), |
two interrupt-in (or bulk-in) endpoints (endpoint 1 and 3) and one |
interrupt-out (or bulk-out) endpoint (endpoint 1). Please note that the USB |
standard forbids bulk endpoints for low speed devices! Most operating systems |
allow them anyway, but the AVR will spend 90% of the CPU time in the USB |
interrupt polling for bulk data. |
By default, only the control endpoint 0 is enabled. To get the other endpoints, |
define USB_CFG_HAVE_INTRIN_ENDPOINT, USB_CFG_HAVE_INTRIN_ENDPOINT3 and/or |
USB_CFG_IMPLEMENT_FN_WRITEOUT respectively (see usbconfig-prototype.h for |
details). |
The driver supports the following endpoints: |
- Endpoint 0, the default control endpoint. |
- Any number of interrupt- or bulk-out endpoints. The data is sent to |
usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined |
to 1 to activate this feature. The endpoint number can be found in the |
global variable 'usbRxToken'. |
- One default interrupt- or bulk-in endpoint. This endpoint is used for |
interrupt- or bulk-in transfers which are not handled by any other endpoint. |
You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this |
feature and call usbSetInterrupt() to send interrupt/bulk data. |
- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in |
previous versions of this driver but can now be configured to any endpoint |
number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate |
this feature and call usbSetInterrupt3() to send interrupt/bulk data. The |
endpoint number can be set with USB_CFG_EP3_NUMBER. |
Please note that the USB standard forbids bulk endpoints for low speed devices! |
Most operating systems allow them anyway, but the AVR will spend 90% of the CPU |
time in the USB interrupt polling for bulk data. |
Maximum data payload: |
Data payload of control in and out transfers may be up to 254 bytes. In order |
to accept payload data of out transfers, you need to implement |
78,29 → 90,30 |
CPU in sleep mode. The driver does not implement suspend handling by itself. |
However, the application may implement activity monitoring and wakeup from |
sleep. The host sends regular SE0 states on the bus to keep it active. These |
SE0 states can be detected by wiring the INT1 pin to D-. It is not necessary |
to enable the interrupt, checking the interrupt pending flag should suffice. |
Before entering sleep mode, the application should enable INT1 for a wakeup |
on the next bus activity. |
SE0 states can be detected by using D- as the interrupt source. Define |
USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus |
activity. |
Operation without an USB master: |
The driver behaves neutral without connection to an USB master if D- reads |
as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) |
pullup resistor on D+. If D- becomes statically 0, the driver may block in |
the interrupt routine. |
pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, |
use a pull-down. If D- becomes statically 0, the driver may block in the |
interrupt routine. |
Interrupt latency: |
The application must ensure that the USB interrupt is not disabled for more |
than 20 cycles. This implies that all interrupt routines must either be |
declared as "INTERRUPT" instead of "SIGNAL" (see "avr/signal.h") or that they |
are written in assembler with "sei" as the first instruction. |
than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). |
This implies that all interrupt routines must either have the "ISR_NOBLOCK" |
attribute set (see "avr/interrupt.h") or be written in assembler with "sei" |
as the first instruction. |
Maximum interrupt duration / CPU cycle consumption: |
The driver handles all USB communication during the interrupt service |
routine. The routine will not return before an entire USB message is received |
and the reply is sent. This may be up to ca. 1200 cycles = 100us if the host |
conforms to the standard. The driver will consume CPU cycles for all USB |
messages, even if they address another (low-speed) device on the same bus. |
and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if |
the host conforms to the standard. The driver will consume CPU cycles for all |
USB messages, even if they address another (low-speed) device on the same bus. |
*/ |
108,7 → 121,7 |
/* --------------------------- Module Interface ---------------------------- */ |
/* ------------------------------------------------------------------------- */ |
#define USBDRV_VERSION 20070319 |
#define USBDRV_VERSION 20121206 |
/* This define uniquely identifies a driver version. It is a decimal number |
* constructed from the driver's release date in the form YYYYMMDD. If the |
* driver's behavior or interface changes, you can use this constant to |
116,8 → 129,18 |
* older than 2006-01-25. |
*/ |
#ifndef USB_PUBLIC |
#define USB_PUBLIC |
#endif |
/* USB_PUBLIC is used as declaration attribute for all functions exported by |
* the USB driver. The default is no attribute (see above). You may define it |
* to static either in usbconfig.h or from the command line if you include |
* usbdrv.c instead of linking against it. Including the C module of the driver |
* directly in your code saves a couple of bytes in flash memory. |
*/ |
#ifndef __ASSEMBLER__ |
#ifndef uchar |
#define uchar unsigned char |
#endif |
126,13 → 149,39 |
#endif |
/* shortcuts for well defined 8 bit integer types */ |
#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ |
# define usbMsgLen_t unsigned |
#else |
# define usbMsgLen_t uchar |
#endif |
/* usbMsgLen_t is the data type used for transfer lengths. By default, it is |
* defined to uchar, allowing a maximum of 254 bytes (255 is reserved for |
* USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, |
* a 16 bit data type is used, allowing up to 16384 bytes (the rest is used |
* for flags in the descriptor configuration). |
*/ |
#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ |
#ifndef usbMsgPtr_t |
#define usbMsgPtr_t uchar * |
#endif |
/* Making usbMsgPtr_t a define allows the user of this library to define it to |
* an 8 bit type on tiny devices. This reduces code size, especially if the |
* compiler supports a tiny memory model. |
* The type can be a pointer or scalar type, casts are made where necessary. |
* Although it's paradoxical, Gcc 4 generates slightly better code for scalar |
* types than for pointers. |
*/ |
struct usbRequest; /* forward declaration */ |
extern void usbInit(void); |
USB_PUBLIC void usbInit(void); |
/* This function must be called before interrupts are enabled and the main |
* loop is entered. |
* loop is entered. We exepct that the PORT and DDR bits for D+ and D- have |
* not been changed from their default status (which is 0). If you have changed |
* them, set both back to 0 (configure them as input with no internal pull-up). |
*/ |
extern void usbPoll(void); |
USB_PUBLIC void usbPoll(void); |
/* This function must be called at regular intervals from the main loop. |
* Maximum delay between calls is somewhat less than 50ms (USB timeout for |
* accepting a Setup message). Otherwise the device will not be recognized. |
139,12 → 188,12 |
* Please note that debug outputs through the UART take ~ 0.5ms per byte |
* at 19200 bps. |
*/ |
extern uchar *usbMsgPtr; |
extern usbMsgPtr_t usbMsgPtr; |
/* This variable may be used to pass transmit data to the driver from the |
* implementation of usbFunctionWrite(). It is also used internally by the |
* driver for standard control requests. |
*/ |
extern uchar usbFunctionSetup(uchar data[8]); |
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); |
/* This function is called when the driver receives a SETUP transaction from |
* the host which is not answered by the driver itself (in practice: class and |
* vendor requests). All control transfers start with a SETUP transaction where |
157,21 → 206,21 |
* requested data to the driver. There are two ways to transfer this data: |
* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data |
* block and return the length of the data in 'usbFunctionSetup()'. The driver |
* will handle the rest. Or (2) return 0xff in 'usbFunctionSetup()'. The driver |
* will then call 'usbFunctionRead()' when data is needed. See the |
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The |
* driver will then call 'usbFunctionRead()' when data is needed. See the |
* documentation for usbFunctionRead() for details. |
* |
* If the SETUP indicates a control-out transfer, the only way to receive the |
* data from the host is through the 'usbFunctionWrite()' call. If you |
* implement this function, you must return 0xff in 'usbFunctionSetup()' to |
* indicate that 'usbFunctionWrite()' should be used. See the documentation of |
* this function for more information. If you just want to ignore the data sent |
* by the host, return 0 in 'usbFunctionSetup()'. |
* implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' |
* to indicate that 'usbFunctionWrite()' should be used. See the documentation |
* of this function for more information. If you just want to ignore the data |
* sent by the host, return 0 in 'usbFunctionSetup()'. |
* |
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite() |
* are only done if enabled by the configuration in usbconfig.h. |
*/ |
extern uchar usbFunctionDescriptor(struct usbRequest *rq); |
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); |
/* You need to implement this function ONLY if you provide USB descriptors at |
* runtime (which is an expert feature). It is very similar to |
* usbFunctionSetup() above, but it is called only to request USB descriptor |
178,7 → 227,7 |
* data. See the documentation of usbFunctionSetup() above for more info. |
*/ |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
void usbSetInterrupt(uchar *data, uchar len); |
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); |
/* This function sets the message which will be sent during the next interrupt |
* IN transfer. The message is copied to an internal buffer and must not exceed |
* a length of 8 bytes. The message may be 0 bytes long just to indicate the |
185,7 → 234,6 |
* interrupt status to the host. |
* If you need to transfer more bytes, use a control read after the interrupt. |
*/ |
extern volatile uchar usbTxLen1; |
#define usbInterruptIsReady() (usbTxLen1 & 0x10) |
/* This macro indicates whether the last interrupt message has already been |
* sent. If you set a new interrupt message before the old was sent, the |
192,8 → 240,7 |
* message already buffered will be lost. |
*/ |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
void usbSetInterrupt3(uchar *data, uchar len); |
extern volatile uchar usbTxLen3; |
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); |
#define usbInterruptIsReady3() (usbTxLen3 & 0x10) |
/* Same as above for endpoint 3 */ |
#endif |
209,7 → 256,7 |
*/ |
#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ |
#if USB_CFG_IMPLEMENT_FN_WRITE |
extern uchar usbFunctionWrite(uchar *data, uchar len); |
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); |
/* This function is called by the driver to provide a control transfer's |
* payload data (control-out). It is called in chunks of up to 8 bytes. The |
* total count provided in the current control transfer can be obtained from |
227,7 → 274,7 |
*/ |
#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ |
#if USB_CFG_IMPLEMENT_FN_READ |
extern uchar usbFunctionRead(uchar *data, uchar len); |
USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); |
/* This function is called by the driver to ask the application for a control |
* transfer's payload data (control-in). It is called in chunks of up to 8 |
* bytes each. You should copy the data to the location given by 'data' and |
238,27 → 285,37 |
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. |
*/ |
#endif /* USB_CFG_IMPLEMENT_FN_READ */ |
extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ |
#if USB_CFG_IMPLEMENT_FN_WRITEOUT |
extern void usbFunctionWriteOut(uchar *data, uchar len); |
/* This function is called by the driver when data on interrupt-out or bulk- |
* out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT |
* to 1 in usbconfig.h to get this function called. |
USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); |
/* This function is called by the driver when data is received on an interrupt- |
* or bulk-out endpoint. The endpoint number can be found in the global |
* variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in |
* usbconfig.h to get this function called. |
*/ |
#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ |
#ifdef USB_CFG_PULLUP_IOPORTNAME |
#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<<USB_CFG_PULLUP_BIT)), \ |
(USB_PULLUP_OUT |= (1<<USB_CFG_PULLUP_BIT))) |
/* This macro (intended to look like a function) connects the device to the |
* USB bus. It is only available if you have defined the constants |
* USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h. |
*/ |
#define usbDeviceDisconnect() ((USB_PULLUP_DDR &= ~(1<<USB_CFG_PULLUP_BIT)), \ |
(USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT))) |
/* This macro (intended to look like a function) disconnects the device from |
* the USB bus. It is only available if you have defined the constants |
* USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h. |
#else /* USB_CFG_PULLUP_IOPORTNAME */ |
#define usbDeviceConnect() (USBDDR &= ~(1<<USBMINUS)) |
#define usbDeviceDisconnect() (USBDDR |= (1<<USBMINUS)) |
#endif /* USB_CFG_PULLUP_IOPORTNAME */ |
/* The macros usbDeviceConnect() and usbDeviceDisconnect() (intended to look |
* like a function) connect resp. disconnect the device from the host's USB. |
* If the constants USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT are defined |
* in usbconfig.h, a disconnect consists of removing the pull-up resisitor |
* from D-, otherwise the disconnect is done by brute-force pulling D- to GND. |
* This does not conform to the spec, but it works. |
* Please note that the USB interrupt must be disabled while the device is |
* in disconnected state, or the interrupt handler will hang! You can either |
* turn off the USB interrupt selectively with |
* USB_INTR_ENABLE &= ~(1 << USB_INTR_ENABLE_BIT) |
* or use cli() to disable interrupts globally. |
*/ |
#endif /* USB_CFG_PULLUP_IOPORT */ |
extern unsigned usbCrc16(unsigned data, uchar len); |
#define usbCrc16(data, len) usbCrc16((unsigned)(data), len) |
/* This function calculates the binary complement of the data CRC used in |
273,6 → 330,16 |
* the 2 bytes CRC (lowbyte first) in the 'data' buffer after reading 'len' |
* bytes. |
*/ |
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH |
extern unsigned usbMeasureFrameLength(void); |
/* This function MUST be called IMMEDIATELY AFTER USB reset and measures 1/7 of |
* the number of CPU cycles during one USB frame minus one low speed bit |
* length. In other words: return value = 1499 * (F_CPU / 10.5 MHz) |
* Since this is a busy wait, you MUST disable all interrupts with cli() before |
* calling this function. |
* This can be used to calibrate the AVR's RC oscillator. |
*/ |
#endif |
extern uchar usbConfiguration; |
/* This value contains the current configuration set by the host. The driver |
* allows setting and querying of this variable with the USB SET_CONFIGURATION |
280,6 → 347,19 |
* You may want to reflect the "configured" status with a LED on the device or |
* switch on high power parts of the circuit only if the device is configured. |
*/ |
#if USB_COUNT_SOF |
extern volatile uchar usbSofCount; |
/* This variable is incremented on every SOF packet. It is only available if |
* the macro USB_COUNT_SOF is defined to a value != 0. |
*/ |
#endif |
#if USB_CFG_CHECK_DATA_TOGGLING |
extern uchar usbCurrentDataToken; |
/* This variable can be checked in usbFunctionWrite() and usbFunctionWriteOut() |
* to ignore duplicate packets. |
*/ |
#endif |
#define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8)) |
/* This macro builds a descriptor header for a string descriptor given the |
* string's length. See usbdrv.c for an example how to use it. |
305,7 → 385,9 |
#define USB_SET_DATATOKEN1(token) usbTxBuf1[0] = token |
#define USB_SET_DATATOKEN3(token) usbTxBuf3[0] = token |
/* These two macros can be used by application software to reset data toggling |
* for interrupt-in endpoints 1 and 3. |
* for interrupt-in endpoints 1 and 3. Since the token is toggled BEFORE |
* sending data, you must set the opposite value of the token which should come |
* first. |
*/ |
#endif /* __ASSEMBLER__ */ |
318,16 → 400,18 |
* about the various methods to define USB descriptors. If you do nothing, |
* the default descriptors will be used. |
*/ |
#define USB_PROP_IS_DYNAMIC (1 << 8) |
#define USB_PROP_IS_DYNAMIC (1u << 14) |
/* If this property is set for a descriptor, usbFunctionDescriptor() will be |
* used to obtain the particular descriptor. |
* used to obtain the particular descriptor. Data directly returned via |
* usbMsgPtr are FLASH data by default, combine (OR) with USB_PROP_IS_RAM to |
* return RAM data. |
*/ |
#define USB_PROP_IS_RAM (1 << 9) |
#define USB_PROP_IS_RAM (1u << 15) |
/* If this property is set for a descriptor, the data is read from RAM |
* memory instead of Flash. The property is used for all methods to provide |
* external descriptors. |
*/ |
#define USB_PROP_LENGTH(len) ((len) & 0xff) |
#define USB_PROP_LENGTH(len) ((len) & 0x3fff) |
/* If a static external descriptor is used, this is the total length of the |
* descriptor in bytes. |
*/ |
348,8 → 432,8 |
#ifndef USB_CFG_DESCR_PROPS_STRING_VENDOR |
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 |
#endif |
#ifndef USB_CFG_DESCR_PROPS_STRING_DEVICE |
#define USB_CFG_DESCR_PROPS_STRING_DEVICE 0 |
#ifndef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 |
#endif |
#ifndef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 |
376,43 → 460,43 |
#ifndef __ASSEMBLER__ |
extern |
#if !(USB_CFG_DESCR_PROPS_DEVICE & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
char usbDescriptorDevice[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_CONFIGURATION & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
char usbDescriptorConfiguration[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_HID_REPORT & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
char usbDescriptorHidReport[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_STRING_0 & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
char usbDescriptorString0[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_STRING_VENDOR & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
int usbDescriptorStringVendor[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_STRING_PRODUCT & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
int usbDescriptorStringDevice[]; |
extern |
#if !(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER & USB_PROP_IS_RAM) |
PROGMEM |
PROGMEM const |
#endif |
int usbDescriptorStringSerialNumber[]; |
439,22 → 523,22 |
#if !defined __ASSEMBLER__ && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID) |
#warning "You should define USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID in usbconfig.h" |
/* If the user has not defined IDs, we default to obdev's free IDs. |
* See USBID-License.txt for details. |
* See USB-IDs-for-free.txt for details. |
*/ |
#endif |
/* make sure we have a VID and PID defined, byte order is lowbyte, highbyte */ |
#ifndef USB_CFG_VENDOR_ID |
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* 5824 in dec, stands for VOTI */ |
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ |
#endif |
#ifndef USB_CFG_DEVICE_ID |
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH |
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* 1503 in dec, shared PID for HIDs */ |
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* = 0x5df = 1503, shared PID for HIDs */ |
# elif USB_CFG_INTERFACE_CLASS == 2 |
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* 1505 in dec, shared PID for CDC Modems */ |
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* = 0x5e1 = 1505, shared PID for CDC Modems */ |
# else |
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* 1500 in dec, obdev's free PID */ |
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x5dc = 1500, obdev's free PID */ |
# endif |
#endif |
480,7 → 564,14 |
#define USB_CFG_PULLUP_IOPORT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) |
#endif |
#ifndef USB_CFG_EP3_NUMBER /* if not defined in usbconfig.h */ |
#define USB_CFG_EP3_NUMBER 3 |
#endif |
#ifndef USB_CFG_HAVE_INTRIN_ENDPOINT3 |
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 |
#endif |
#define USB_BUFSIZE 11 /* PID, 8 bytes data, 2 bytes CRC */ |
/* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */ |
493,7 → 584,14 |
# endif |
#endif |
#ifndef USB_INTR_CFG_SET /* allow user to override our default */ |
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */ |
# if defined(USB_COUNT_SOF) || defined(USB_SOF_HOOK) |
# define USB_INTR_CFG_SET (1 << ISC01) /* cfg for falling edge */ |
/* If any SOF logic is used, the interrupt must be wired to D- where |
* we better trigger on falling edge |
*/ |
# else |
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */ |
# endif |
#endif |
#ifndef USB_INTR_CFG_CLR /* allow user to override our default */ |
# define USB_INTR_CFG_CLR 0 /* no bits to clear */ |
550,10 → 648,24 |
#define USBPID_NAK 0x5a |
#define USBPID_STALL 0x1e |
#ifndef USB_INITIAL_DATATOKEN |
#define USB_INITIAL_DATATOKEN USBPID_DATA1 |
#endif |
#ifndef __ASSEMBLER__ |
extern uchar usbTxBuf1[USB_BUFSIZE], usbTxBuf3[USB_BUFSIZE]; |
typedef struct usbTxStatus{ |
volatile uchar len; |
uchar buffer[USB_BUFSIZE]; |
}usbTxStatus_t; |
extern usbTxStatus_t usbTxStatus1, usbTxStatus3; |
#define usbTxLen1 usbTxStatus1.len |
#define usbTxBuf1 usbTxStatus1.buffer |
#define usbTxLen3 usbTxStatus3.len |
#define usbTxBuf3 usbTxStatus3.buffer |
typedef union usbWord{ |
unsigned word; |
uchar bytes[2]; |
616,7 → 728,8 |
#define USBDESCR_HID_REPORT 0x22 |
#define USBDESCR_HID_PHYS 0x23 |
#define USBATTR_BUSPOWER 0x80 |
//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more |
#define USBATTR_BUSPOWER 0 |
#define USBATTR_SELFPOWER 0x40 |
#define USBATTR_REMOTEWAKE 0x20 |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/usbdrvasm.S |
---|
1,40 → 1,23 |
/* Name: usbdrvasm.S |
* Project: AVR USB driver |
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
* Author: Christian Starkjohann |
* Creation Date: 2004-12-29 |
* Creation Date: 2007-06-13 |
* Tabsize: 4 |
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: usbdrvasm.S,v 1.2 2007/05/19 12:30:11 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
/* |
General Description: |
This module implements the assembler part of the USB driver. 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! |
Timing constraints according to spec (in bit times): |
timing subject min max CPUcycles |
--------------------------------------------------------------------------- |
EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 |
EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 |
DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 |
This module is the assembler part of the USB driver. This file contains |
general code (preprocessor acrobatics and CRC computation) and then includes |
the file appropriate for the given clock rate. |
*/ |
#include "iarcompat.h" |
#ifndef __IAR_SYSTEMS_ASM__ |
/* configs for io.h */ |
# define __SFR_OFFSET 0 |
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ |
# include <avr/io.h> /* for CPU I/O register definitions and vectors */ |
#endif /* __IAR_SYSTEMS_ASM__ */ |
#include "usbdrv.h" /* for common defs */ |
#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ |
#include "usbportability.h" |
#include "usbdrv.h" /* for common defs */ |
/* register names */ |
#define x1 r16 |
#define x2 r17 |
42,568 → 25,66 |
#define cnt r19 |
#define x3 r20 |
#define x4 r21 |
#define x5 r22 |
#define bitcnt x5 |
#define phase x4 |
#define leap x4 |
/* Some assembler dependent definitions and declarations: */ |
#ifdef __IAR_SYSTEMS_ASM__ |
# 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) /* not HIGH to allow XLINK to make a proper range check */ |
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset |
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen |
extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3 |
extern usbTxBuf, usbTxStatus1, usbTxStatus3 |
# if USB_COUNT_SOF |
extern usbSofCount |
# endif |
public usbCrc16 |
public usbCrc16Append |
COMMON INTVEC |
ORG INT0_vect |
rjmp SIG_INTERRUPT0 |
# ifndef USB_INTR_VECTOR |
ORG INT0_vect |
# else /* USB_INTR_VECTOR */ |
ORG USB_INTR_VECTOR |
# undef USB_INTR_VECTOR |
# endif /* USB_INTR_VECTOR */ |
# define USB_INTR_VECTOR usbInterruptHandler |
rjmp USB_INTR_VECTOR |
RSEG CODE |
#else /* __IAR_SYSTEMS_ASM__ */ |
# define nop2 rjmp .+0 /* jump to next instruction */ |
# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ |
# ifdef INT0_vect |
# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector |
# else |
# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector |
# endif |
# endif |
.text |
.global SIG_INTERRUPT0 |
.type SIG_INTERRUPT0, @function |
.global USB_INTR_VECTOR |
.type USB_INTR_VECTOR, @function |
.global usbCrc16 |
.global usbCrc16Append |
#endif /* __IAR_SYSTEMS_ASM__ */ |
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! |
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled |
;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable |
;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes |
;Numbers in brackets are maximum cycles since SOF. |
SIG_INTERRUPT0: |
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt |
push YL ;2 [35] push only what is necessary to sync with edge ASAP |
in YL, SREG ;1 [37] |
push YL ;2 [39] |
;---------------------------------------------------------------------------- |
; 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 |
;first part has no timeout because it waits for IDLE or SE1 (== disconnected) |
waitForJ: |
sbis USBIN, USBMINUS ;1 [40] wait for D- == 1 |
rjmp waitForJ ;2 |
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 |
rjmp foundK |
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 [2] |
lds YL, usbInputBufOffset;2 [4] |
clr YH ;1 [5] |
subi YL, lo8(-(usbRxBuf));1 [6] |
sbci YH, hi8(-(usbRxBuf));1 [7] |
sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] |
rjmp haveTwoBitsK ;2 [10] |
pop YH ; undo the push from before |
rjmp waitForK ; this was not the end of sync, retry |
haveTwoBitsK: |
;---------------------------------------------------------------------------- |
; push more registers and initialize values while we sample the first bits: |
;---------------------------------------------------------------------------- |
push shift ;2 [16] |
push x1 ;2 [12] |
push x2 ;2 [14] |
in x1, USBIN ;1 [17] <-- sample bit 0 |
ldi shift, 0xff ;1 [18] |
bst x1, USBMINUS ;1 [19] |
bld shift, 0 ;1 [20] |
push x3 ;2 [22] |
push cnt ;2 [24] |
in x2, USBIN ;1 [25] <-- sample bit 1 |
ser x3 ;1 [26] [inserted init instruction] |
eor x1, x2 ;1 [27] |
bst x1, USBMINUS ;1 [28] |
bld shift, 1 ;1 [29] |
ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] |
rjmp rxbit2 ;2 [32] |
;---------------------------------------------------------------------------- |
; Receiver loop (numbers in brackets are cycles within byte after instr) |
;---------------------------------------------------------------------------- |
unstuff0: ;1 (branch taken) |
andi x3, ~0x01 ;1 [15] |
mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit |
in x2, USBIN ;1 [17] <-- sample bit 1 again |
ori shift, 0x01 ;1 [18] |
rjmp didUnstuff0 ;2 [20] |
unstuff1: ;1 (branch taken) |
mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit |
andi x3, ~0x02 ;1 [22] |
ori shift, 0x02 ;1 [23] |
nop ;1 [24] |
in x1, USBIN ;1 [25] <-- sample bit 2 again |
rjmp didUnstuff1 ;2 [27] |
unstuff2: ;1 (branch taken) |
andi x3, ~0x04 ;1 [29] |
ori shift, 0x04 ;1 [30] |
mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit |
nop ;1 [32] |
in x2, USBIN ;1 [33] <-- sample bit 3 |
rjmp didUnstuff2 ;2 [35] |
unstuff3: ;1 (branch taken) |
in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] |
andi x3, ~0x08 ;1 [35] |
ori shift, 0x08 ;1 [36] |
rjmp didUnstuff3 ;2 [38] |
unstuff4: ;1 (branch taken) |
andi x3, ~0x10 ;1 [40] |
in x1, USBIN ;1 [41] <-- sample stuffed bit 4 |
ori shift, 0x10 ;1 [42] |
rjmp didUnstuff4 ;2 [44] |
unstuff5: ;1 (branch taken) |
andi x3, ~0x20 ;1 [48] |
in x2, USBIN ;1 [49] <-- sample stuffed bit 5 |
ori shift, 0x20 ;1 [50] |
rjmp didUnstuff5 ;2 [52] |
unstuff6: ;1 (branch taken) |
andi x3, ~0x40 ;1 [56] |
in x1, USBIN ;1 [57] <-- sample stuffed bit 6 |
ori shift, 0x40 ;1 [58] |
rjmp didUnstuff6 ;2 [60] |
; extra jobs done during bit interval: |
; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] |
; bit 1: se0 check |
; bit 2: overflow check |
; bit 3: recovery from delay [bit 0 tasks took too long] |
; bit 4: none |
; bit 5: none |
; bit 6: none |
; bit 7: jump, eor |
rxLoop: |
eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others |
in x1, USBIN ;1 [1] <-- sample bit 0 |
st y+, x3 ;2 [3] store data |
ser x3 ;1 [4] |
nop ;1 [5] |
eor x2, x1 ;1 [6] |
bst x2, USBMINUS;1 [7] |
bld shift, 0 ;1 [8] |
in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) |
andi x2, USBMASK ;1 [10] |
breq se0 ;1 [11] SE0 check for bit 1 |
andi shift, 0xf9 ;1 [12] |
didUnstuff0: |
breq unstuff0 ;1 [13] |
eor x1, x2 ;1 [14] |
bst x1, USBMINUS;1 [15] |
bld shift, 1 ;1 [16] |
rxbit2: |
in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) |
andi shift, 0xf3 ;1 [18] |
breq unstuff1 ;1 [19] do remaining work for bit 1 |
didUnstuff1: |
subi cnt, 1 ;1 [20] |
brcs overflow ;1 [21] loop control |
eor x2, x1 ;1 [22] |
bst x2, USBMINUS;1 [23] |
bld shift, 2 ;1 [24] |
in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) |
andi shift, 0xe7 ;1 [26] |
breq unstuff2 ;1 [27] |
didUnstuff2: |
eor x1, x2 ;1 [28] |
bst x1, USBMINUS;1 [29] |
bld shift, 3 ;1 [30] |
didUnstuff3: |
andi shift, 0xcf ;1 [31] |
breq unstuff3 ;1 [32] |
in x1, USBIN ;1 [33] <-- sample bit 4 |
eor x2, x1 ;1 [34] |
bst x2, USBMINUS;1 [35] |
bld shift, 4 ;1 [36] |
didUnstuff4: |
andi shift, 0x9f ;1 [37] |
breq unstuff4 ;1 [38] |
nop2 ;2 [40] |
in x2, USBIN ;1 [41] <-- sample bit 5 |
eor x1, x2 ;1 [42] |
bst x1, USBMINUS;1 [43] |
bld shift, 5 ;1 [44] |
didUnstuff5: |
andi shift, 0x3f ;1 [45] |
breq unstuff5 ;1 [46] |
nop2 ;2 [48] |
in x1, USBIN ;1 [49] <-- sample bit 6 |
eor x2, x1 ;1 [50] |
bst x2, USBMINUS;1 [51] |
bld shift, 6 ;1 [52] |
didUnstuff6: |
cpi shift, 0x02 ;1 [53] |
brlo unstuff6 ;1 [54] |
nop2 ;2 [56] |
in x2, USBIN ;1 [57] <-- sample bit 7 |
eor x1, x2 ;1 [58] |
bst x1, USBMINUS;1 [59] |
bld shift, 7 ;1 [60] |
didUnstuff7: |
cpi shift, 0x04 ;1 [61] |
brsh rxLoop ;2 [63] loop control |
unstuff7: |
andi x3, ~0x80 ;1 [63] |
ori shift, 0x80 ;1 [64] |
in x2, USBIN ;1 [65] <-- sample stuffed bit 7 |
nop ;1 [66] |
rjmp didUnstuff7 ;2 [68] |
;---------------------------------------------------------------------------- |
; Processing of received packet (numbers in brackets are cycles after end of SE0) |
;---------------------------------------------------------------------------- |
;This is the only non-error exit point for the software receiver loop |
;we don't check any CRCs here because there is no time left. |
#define token x1 |
se0: ; [0] |
subi cnt, USB_BUFSIZE ;1 [1] |
neg cnt ;1 [2] |
cpi cnt, 3 ;1 [3] |
ldi x2, 1<<USB_INTR_PENDING_BIT ;1 [4] |
out USB_INTR_PENDING, x2;1 [5] clear pending intr and check flag later. SE0 should be over. |
brlo doReturn ;1 [6] this is probably an ACK, NAK or similar packet |
sub YL, cnt ;1 [7] |
sbci YH, 0 ;1 [8] |
ld token, y ;2 [10] |
cpi token, USBPID_DATA0 ;1 [11] |
breq handleData ;1 [12] |
cpi token, USBPID_DATA1 ;1 [13] |
breq handleData ;1 [14] |
ldd x2, y+1 ;2 [16] ADDR and 1 bit endpoint number |
mov x3, x2 ;1 [17] store for endpoint number |
andi x2, 0x7f ;1 [18] x2 is now ADDR |
lds shift, usbDeviceAddr;2 [20] |
cp x2, shift ;1 [21] |
overflow: ; This is a hack: brcs overflow will never have Z flag set |
brne ignorePacket ;1 [22] packet for different address |
cpi token, USBPID_IN ;1 [23] |
breq handleIn ;1 [24] |
cpi token, USBPID_SETUP ;1 [25] |
breq handleSetupOrOut ;1 [26] |
cpi token, USBPID_OUT ;1 [27] |
breq handleSetupOrOut ;1 [28] |
; rjmp ignorePacket ;fallthrough, should not happen anyway. |
ignorePacket: |
clr shift |
sts usbCurrentTok, shift |
doReturn: |
pop cnt |
pop x3 |
pop x2 |
pop x1 |
pop shift |
pop YH |
sofError: |
pop YL |
out SREG, YL |
pop YL |
reti |
#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_HAVE_INTRIN_ENDPOINT3 |
handleIn3: ;1 [38] (branch taken) |
lds cnt, usbTxLen3 ;2 [40] |
sbrc cnt, 4 ;2 [42] |
rjmp sendCntAndReti ;0 43 + 17 = 60 until SOP |
sts usbTxLen3, x1 ;2 [44] x1 == USBPID_NAK from above |
ldi YL, lo8(usbTxBuf3) ;1 [45] |
ldi YH, hi8(usbTxBuf3) ;1 [46] |
rjmp usbSendAndReti ;2 [48] + 13 = 61 until SOP (violates the spec by 1 cycle) |
#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ |
# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING |
# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg |
#else /* It's a memory address, use lds and sts */ |
# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING |
# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg |
#endif |
;Setup and Out are followed by a data packet two bit times (16 cycles) after |
;the end of SE0. The sync code allows up to 40 cycles delay from the start of |
;the sync pattern until the first bit is sampled. That's a total of 56 cycles. |
handleSetupOrOut: ;1 [29] (branch taken) |
#if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we have data for second OUT endpoint, set usbCurrentTok to -1 */ |
sbrc x3, 7 ;1 [30] skip if endpoint 0 |
ldi token, -1 ;1 [31] indicate that this is endpoint 1 OUT |
#endif |
sts usbCurrentTok, token;2 [33] |
pop cnt ;2 [35] |
pop x3 ;2 [37] |
pop x2 ;2 [39] |
pop x1 ;2 [41] |
pop shift ;2 [43] |
pop YH ;2 [45] |
in YL, USB_INTR_PENDING;1 [46] |
sbrc YL, USB_INTR_PENDING_BIT;1 [47] check whether data is already arriving |
rjmp waitForJ ;2 [49] save the pops and pushes -- a new interrupt is aready pending |
rjmp sofError ;2 not an error, but it does the pops and reti we want |
#define usbTxLen1 usbTxStatus1 |
#define usbTxBuf1 (usbTxStatus1 + 1) |
#define usbTxLen3 usbTxStatus3 |
#define usbTxBuf3 (usbTxStatus3 + 1) |
handleData: ;1 [15] (branch taken) |
lds token, usbCurrentTok;2 [17] |
tst token ;1 [18] |
breq doReturn ;1 [19] |
lds x2, usbRxLen ;2 [21] |
tst x2 ;1 [22] |
brne sendNakAndReti ;1 [23] |
; 2006-03-11: The following two lines fix a problem where the device was not |
; recognized if usbPoll() was called less frequently than once every 4 ms. |
cpi cnt, 4 ;1 [24] zero sized data packets are status phase only -- ignore and ack |
brmi sendAckAndReti ;1 [25] keep rx buffer clean -- we must not NAK next SETUP |
sts usbRxLen, cnt ;2 [27] store received data, swap buffers |
sts usbRxToken, token ;2 [29] |
lds x2, usbInputBufOffset;2 [31] swap buffers |
ldi cnt, USB_BUFSIZE ;1 [32] |
sub cnt, x2 ;1 [33] |
sts usbInputBufOffset, cnt;2 [35] buffers now swapped |
rjmp sendAckAndReti ;2 [37] + 19 = 56 until SOP |
handleIn: ;1 [25] (branch taken) |
;We don't send any data as long as the C code has not processed the current |
;input data and potentially updated the output data. That's more efficient |
;in terms of code size than clearing the tx buffers when a packet is received. |
lds x1, usbRxLen ;2 [27] |
cpi x1, 1 ;1 [28] negative values are flow control, 0 means "buffer free" |
brge sendNakAndReti ;1 [29] unprocessed input packet? |
ldi x1, USBPID_NAK ;1 [30] prepare value for usbTxLen |
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
sbrc x3, 7 ;2 [33] x3 contains addr + endpoint |
rjmp handleIn1 ;0 |
#endif |
lds cnt, usbTxLen ;2 [34] |
sbrc cnt, 4 ;2 [36] all handshake tokens have bit 4 set |
rjmp sendCntAndReti ;0 37 + 17 = 54 until SOP |
sts usbTxLen, x1 ;2 [38] x1 == USBPID_NAK from above |
ldi YL, lo8(usbTxBuf) ;1 [39] |
ldi YH, hi8(usbTxBuf) ;1 [40] |
rjmp usbSendAndReti ;2 [42] + 14 = 56 until SOP |
; Comment about when to set usbTxLen to USBPID_NAK: |
; We should set it back when we receive the ACK from the host. This would |
; be simple to implement: One static variable which stores whether the last |
; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the |
; ACK. However, we set it back immediately when we send the package, |
; assuming that no error occurs and the host sends an ACK. We save one byte |
; RAM this way and avoid potential problems with endless retries. The rest of |
; the driver assumes error-free transfers anyway. |
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */ |
handleIn1: ;1 [33] (branch taken) |
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint |
ldd x2, y+2 ;2 [35] |
sbrc x2, 0 ;2 [37] |
rjmp handleIn3 ;0 |
#endif |
lds cnt, usbTxLen1 ;2 [39] |
sbrc cnt, 4 ;2 [41] all handshake tokens have bit 4 set |
rjmp sendCntAndReti ;0 42 + 17 = 59 until SOP |
sts usbTxLen1, x1 ;2 [43] x1 == USBPID_NAK from above |
ldi YL, lo8(usbTxBuf1) ;1 [44] |
ldi YH, hi8(usbTxBuf1) ;1 [45] |
rjmp usbSendAndReti ;2 [47] + 13 = 60 until SOP |
#endif |
;---------------------------------------------------------------------------- |
; Transmitting data |
;---------------------------------------------------------------------------- |
bitstuff0: ;1 (for branch taken) |
eor x1, x4 ;1 |
ldi x2, 0 ;1 |
out USBOUT, x1 ;1 <-- out |
rjmp didStuff0 ;2 branch back 2 cycles earlier |
bitstuff1: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff1 ;2 we know that C is clear, jump back to do OUT and ror 0 into x2 |
bitstuff2: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff2 ;2 jump back 4 cycles earlier and do out and ror 0 into x2 |
bitstuff3: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff3 ;2 jump back earlier and ror 0 into x2 |
bitstuff4: ;1 (for branch taken) |
eor x1, x4 ;1 |
ldi x2, 0 ;1 |
out USBOUT, x1 ;1 <-- out |
rjmp didStuff4 ;2 jump back 2 cycles earlier |
sendNakAndReti: ;0 [-19] 19 cycles until SOP |
ldi x3, USBPID_NAK ;1 [-18] |
rjmp usbSendX3 ;2 [-16] |
sendAckAndReti: ;0 [-19] 19 cycles until SOP |
ldi x3, USBPID_ACK ;1 [-18] |
rjmp usbSendX3 ;2 [-16] |
sendCntAndReti: ;0 [-17] 17 cycles until SOP |
mov x3, cnt ;1 [-16] |
usbSendX3: ;0 [-16] |
ldi YL, 20 ;1 [-15] 'x3' is R20 |
ldi YH, 0 ;1 [-14] |
ldi cnt, 2 ;1 [-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...x4, shift, cnt, Y |
;Numbers in brackets are time since first bit of sync pattern is sent |
usbSendAndReti: ;0 [-13] timing: 13 cycles until SOP |
in x2, USBDDR ;1 [-12] |
ori x2, USBMASK ;1 [-11] |
sbi USBOUT, USBMINUS;2 [-9] prepare idle state; D+ and D- must have been 0 (no pullups) |
in x1, USBOUT ;1 [-8] port mirror for tx loop |
out USBDDR, x2 ;1 [-7] <- acquire bus |
; need not init x2 (bitstuff history) because sync starts with 0 |
push x4 ;2 [-5] |
ldi x4, USBMASK ;1 [-4] exor mask |
ldi shift, 0x80 ;1 [-3] sync byte is first byte sent |
txLoop: ; [62] |
sbrs shift, 0 ;1 [-2] [62] |
eor x1, x4 ;1 [-1] [63] |
out USBOUT, x1 ;1 [0] <-- out bit 0 |
ror shift ;1 [1] |
ror x2 ;1 [2] |
didStuff0: |
cpi x2, 0xfc ;1 [3] |
brsh bitstuff0 ;1 [4] |
sbrs shift, 0 ;1 [5] |
eor x1, x4 ;1 [6] |
ror shift ;1 [7] |
didStuff1: |
out USBOUT, x1 ;1 [8] <-- out bit 1 |
ror x2 ;1 [9] |
cpi x2, 0xfc ;1 [10] |
brsh bitstuff1 ;1 [11] |
sbrs shift, 0 ;1 [12] |
eor x1, x4 ;1 [13] |
ror shift ;1 [14] |
didStuff2: |
ror x2 ;1 [15] |
out USBOUT, x1 ;1 [16] <-- out bit 2 |
cpi x2, 0xfc ;1 [17] |
brsh bitstuff2 ;1 [18] |
sbrs shift, 0 ;1 [19] |
eor x1, x4 ;1 [20] |
ror shift ;1 [21] |
didStuff3: |
ror x2 ;1 [22] |
cpi x2, 0xfc ;1 [23] |
out USBOUT, x1 ;1 [24] <-- out bit 3 |
brsh bitstuff3 ;1 [25] |
nop2 ;2 [27] |
ld x3, y+ ;2 [29] |
sbrs shift, 0 ;1 [30] |
eor x1, x4 ;1 [31] |
out USBOUT, x1 ;1 [32] <-- out bit 4 |
ror shift ;1 [33] |
ror x2 ;1 [34] |
didStuff4: |
cpi x2, 0xfc ;1 [35] |
brsh bitstuff4 ;1 [36] |
sbrs shift, 0 ;1 [37] |
eor x1, x4 ;1 [38] |
ror shift ;1 [39] |
didStuff5: |
out USBOUT, x1 ;1 [40] <-- out bit 5 |
ror x2 ;1 [41] |
cpi x2, 0xfc ;1 [42] |
brsh bitstuff5 ;1 [43] |
sbrs shift, 0 ;1 [44] |
eor x1, x4 ;1 [45] |
ror shift ;1 [46] |
didStuff6: |
ror x2 ;1 [47] |
out USBOUT, x1 ;1 [48] <-- out bit 6 |
cpi x2, 0xfc ;1 [49] |
brsh bitstuff6 ;1 [50] |
sbrs shift, 0 ;1 [51] |
eor x1, x4 ;1 [52] |
ror shift ;1 [53] |
didStuff7: |
ror x2 ;1 [54] |
cpi x2, 0xfc ;1 [55] |
out USBOUT, x1 ;1 [56] <-- out bit 7 |
brsh bitstuff7 ;1 [57] |
mov shift, x3 ;1 [58] |
dec cnt ;1 [59] |
brne txLoop ;1/2 [60/61] |
;make SE0: |
cbr x1, USBMASK ;1 [61] prepare SE0 [spec says EOP may be 15 to 18 cycles] |
pop x4 ;2 [63] |
;brackets are cycles from start of SE0 now |
out USBOUT, x1 ;1 [0] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle |
nop2 ;2 [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 |
lds x2, usbNewDeviceAddr;2 [4] |
subi YL, 20 + 2 ;1 [5] |
sbci YH, 0 ;1 [6] |
breq skipAddrAssign ;2 [8] |
sts usbDeviceAddr, x2;0 if not skipped: SE0 is one cycle longer |
skipAddrAssign: |
;end of usbDeviceAddress transfer |
ldi x2, 1<<USB_INTR_PENDING_BIT;1 [9] int0 occurred during TX -- clear pending flag |
out USB_INTR_PENDING, x2;1 [10] |
ori x1, USBIDLE ;1 [11] |
in x2, USBDDR ;1 [12] |
cbr x2, USBMASK ;1 [13] set both pins to input |
mov x3, x1 ;1 [14] |
cbr x3, USBMASK ;1 [15] configure no pullup on both pins |
out USBOUT, x1 ;1 [16] <-- out J (idle) -- end of SE0 (EOP signal) |
out USBDDR, x2 ;1 [17] <-- release bus now |
out USBOUT, x3 ;1 [18] <-- ensure no pull-up resistors are active |
rjmp doReturn |
bitstuff5: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff5 ;2 same trick as above... |
bitstuff6: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff6 ;2 same trick as above... |
bitstuff7: ;1 (for branch taken) |
eor x1, x4 ;1 |
rjmp didStuff7 ;2 same trick as above... |
;---------------------------------------------------------------------------- |
; Utility functions |
;---------------------------------------------------------------------------- |
661,47 → 142,124 |
#endif |
; extern unsigned usbCrc16(unsigned char *data, unsigned char len); |
; data: r24/25 |
; len: r22 |
#if USB_USE_FAST_CRC |
; This implementation is faster, but has bigger code size |
; Thanks to Slawomir Fras (BoskiDialer) for this code! |
; It implements the following C pseudo-code: |
; unsigned table(unsigned char x) |
; { |
; unsigned value; |
; |
; value = (unsigned)x << 6; |
; value ^= (unsigned)x << 7; |
; if(parity(x)) |
; value ^= 0xc001; |
; return value; |
; } |
; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen) |
; { |
; unsigned crc = 0xffff; |
; |
; while(argLen--) |
; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc); |
; return ~crc; |
; } |
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); |
; argPtr r24+25 / r16+r17 |
; argLen r22 / r18 |
; temp variables: |
; r18: data byte |
; r19: bit counter |
; r20/21: polynomial |
; r23: scratch |
; r24/25: crc-sum |
; r26/27=X: ptr |
; byte r18 / r22 |
; scratch r23 |
; resCrc r24+r25 / r16+r17 |
; ptr X / Z |
usbCrc16: |
mov ptrL, argPtrL |
mov ptrH, argPtrH |
ldi resCrcL, 0xff |
ldi resCrcH, 0xff |
ldi resCrcL, 0xFF |
ldi resCrcH, 0xFF |
rjmp usbCrc16LoopTest |
usbCrc16ByteLoop: |
ld byte, ptr+ |
eor resCrcL, byte ; resCrcL is now 'x' in table() |
mov byte, resCrcL ; compute parity of 'x' |
swap byte |
eor byte, resCrcL |
mov scratch, byte |
lsr byte |
lsr byte |
eor byte, scratch |
inc byte |
lsr byte |
andi byte, 1 ; byte is now parity(x) |
mov scratch, resCrcL |
mov resCrcL, resCrcH |
eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001; |
neg byte |
andi byte, 0xc0 |
mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001; |
clr byte |
lsr scratch |
ror byte |
eor resCrcH, scratch |
eor resCrcL, byte |
lsr scratch |
ror byte |
eor resCrcH, scratch |
eor resCrcL, byte |
usbCrc16LoopTest: |
subi argLen, 1 |
brsh usbCrc16ByteLoop |
com resCrcL |
com resCrcH |
ret |
#else /* USB_USE_FAST_CRC */ |
; This implementation is slower, but has less code size |
; |
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen); |
; argPtr r24+25 / r16+r17 |
; argLen r22 / r18 |
; temp variables: |
; byte r18 / r22 |
; bitCnt r19 |
; poly r20+r21 |
; scratch r23 |
; resCrc r24+r25 / r16+r17 |
; ptr X / Z |
usbCrc16: |
mov ptrL, argPtrL |
mov ptrH, argPtrH |
ldi resCrcL, 0 |
ldi resCrcH, 0 |
ldi polyL, lo8(0xa001) |
ldi polyH, hi8(0xa001) |
crcByteLoop: |
subi argLen, 1 |
brcs crcReady |
com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set |
ldi bitCnt, 0 ; loop counter with starnd condition = end condition |
rjmp usbCrcLoopEntry |
usbCrcByteLoop: |
ld byte, ptr+ |
ldi bitCnt, 8 |
crcBitLoop: |
mov scratch, byte |
eor scratch, resCrcL |
lsr resCrcH |
eor resCrcL, byte |
usbCrcBitLoop: |
ror resCrcH ; carry is always set here (see brcs jumps to here) |
ror resCrcL |
lsr byte |
sbrs scratch, 0 |
rjmp crcNoXor |
brcs usbCrcNoXor |
eor resCrcL, polyL |
eor resCrcH, polyH |
crcNoXor: |
dec bitCnt |
brne crcBitLoop |
rjmp crcByteLoop |
crcReady: |
com resCrcL |
com resCrcH |
usbCrcNoXor: |
subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times |
brcs usbCrcBitLoop |
usbCrcLoopEntry: |
subi argLen, -1 |
brcs usbCrcByteLoop |
usbCrcReady: |
ret |
; Thanks to Reimar Doeffinger for optimizing this CRC routine! |
#endif /* USB_USE_FAST_CRC */ |
; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); |
usbCrc16Append: |
rcall usbCrc16 |
708,3 → 266,127 |
st ptr+, resCrcL |
st ptr+, resCrcH |
ret |
#undef argLen |
#undef argPtrL |
#undef argPtrH |
#undef resCrcL |
#undef resCrcH |
#undef ptrL |
#undef ptrH |
#undef ptr |
#undef byte |
#undef bitCnt |
#undef polyL |
#undef polyH |
#undef scratch |
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH |
#ifdef __IAR_SYSTEMS_ASM__ |
/* Register assignments for usbMeasureFrameLength on IAR cc */ |
/* Calling conventions on IAR: |
* First parameter passed in r16/r17, second in r18/r19 and so on. |
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) |
* Result is passed in r16/r17 |
* In case of the "tiny" memory model, pointers are only 8 bit with no |
* padding. We therefore pass argument 1 as "16 bit unsigned". |
*/ |
# define resL r16 |
# define resH r17 |
# define cnt16L r30 |
# define cnt16H r31 |
# define cntH r18 |
#else /* __IAR_SYSTEMS_ASM__ */ |
/* Register assignments for usbMeasureFrameLength on gcc */ |
/* Calling conventions on gcc: |
* First parameter passed in r24/r25, second in r22/23 and so on. |
* Callee must preserve r1-r17, r28/r29 |
* Result is passed in r24/r25 |
*/ |
# define resL r24 |
# define resH r25 |
# define cnt16L r24 |
# define cnt16H r25 |
# define cntH r26 |
#endif |
# define cnt16 cnt16L |
; extern unsigned usbMeasurePacketLength(void); |
; returns time between two idle strobes in multiples of 7 CPU clocks |
.global usbMeasureFrameLength |
usbMeasureFrameLength: |
ldi cntH, 6 ; wait ~ 10 ms for D- == 0 |
clr cnt16L |
clr cnt16H |
usbMFTime16: |
dec cntH |
breq usbMFTimeout |
usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe) |
sbiw cnt16, 1 ;[0] [6] |
breq usbMFTime16 ;[2] |
sbic USBIN, USBMINUS ;[3] |
rjmp usbMFWaitStrobe ;[4] |
usbMFWaitIdle: ; then wait until idle again |
sbis USBIN, USBMINUS ;1 wait for D- == 1 |
rjmp usbMFWaitIdle ;2 |
ldi cnt16L, 1 ;1 represents cycles so far |
clr cnt16H ;1 |
usbMFWaitLoop: |
in cntH, USBIN ;[0] [7] |
adiw cnt16, 1 ;[1] |
breq usbMFTimeout ;[3] |
andi cntH, USBMASK ;[4] |
brne usbMFWaitLoop ;[5] |
usbMFTimeout: |
#if resL != cnt16L |
mov resL, cnt16L |
mov resH, cnt16H |
#endif |
ret |
#undef resL |
#undef resH |
#undef cnt16 |
#undef cnt16L |
#undef cnt16H |
#undef cntH |
#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ |
;---------------------------------------------------------------------------- |
; Now include the clock rate specific code |
;---------------------------------------------------------------------------- |
#ifndef USB_CFG_CLOCK_KHZ |
# ifdef F_CPU |
# define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
# else |
# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" |
# endif |
#endif |
#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ |
# if USB_CFG_CLOCK_KHZ == 18000 |
# include "usbdrvasm18-crc.inc" |
# else |
# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" |
# endif |
#else /* USB_CFG_CHECK_CRC */ |
# if USB_CFG_CLOCK_KHZ == 12000 |
# include "usbdrvasm12.inc" |
# elif USB_CFG_CLOCK_KHZ == 12800 |
# include "usbdrvasm128.inc" |
# elif USB_CFG_CLOCK_KHZ == 15000 |
# include "usbdrvasm15.inc" |
# elif USB_CFG_CLOCK_KHZ == 16000 |
# include "usbdrvasm16.inc" |
# elif USB_CFG_CLOCK_KHZ == 16500 |
# include "usbdrvasm165.inc" |
# elif USB_CFG_CLOCK_KHZ == 20000 |
# include "usbdrvasm20.inc" |
# else |
# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" |
# endif |
#endif /* USB_CFG_CHECK_CRC */ |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbdrv/usbdrvasm.asm |
---|
1,11 → 1,10 |
/* Name: usbdrvasm.asm |
* Project: AVR USB driver |
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
* Author: Christian Starkjohann |
* Creation Date: 2006-03-01 |
* Tabsize: 4 |
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH |
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
* This Revision: $Id: usbdrvasm.asm,v 1.2 2007/05/19 12:30:11 harbaum Exp $ |
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
*/ |
/* |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/common.mk |
---|
7,21 → 7,24 |
# TARGET_ARCH - gcc -mmcu= option with AVR device type |
# OBJECTS - the objects in addition to the USBtiny objects |
# FLASH_CMD - command to upload main.hex to flash |
# FUSES_CMD - command to program the fuse bytes |
# STACK - maximum stack size (optional) |
# FLASH - flash size (optional) |
# SRAM - SRAM size (optional) |
# SCHEM - Postscript version of the schematic to be generated |
# |
# Copyright (C) 2006 Dick Streefland |
# Copyright 2006-2010 Dick Streefland |
# |
# This is free software, licensed under the terms of the GNU General |
# Public License as published by the Free Software Foundation. |
# ====================================================================== |
check = $(shell $(CC) $1 -c -xc /dev/null -o/dev/null 2>/dev/null && echo $1) |
CC = avr-gcc |
CFLAGS = -Os -g -Wall -I. -I$(USBTINY) |
ASFLAGS = -Os -g -Wall -I. |
LDFLAGS = -g |
OPTIM = -Os -ffunction-sections $(call check,-fno-split-wide-types) |
CFLAGS = -g -Wall -Wattributes -I. -I$(USBTINY) $(OPTIM) |
LDFLAGS = -g -Wl,--relax,--gc-sections |
MODULES = crc.o int.o usb.o $(OBJECTS) |
UTIL = $(USBTINY)/../util |
42,6 → 45,9 |
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM) |
avr-objcopy -j .text -j .data -O ihex main.elf main.hex |
check: main.elf $(UTIL)/check.py |
@python $(UTIL)/check.py main.elf $(STACK) $(FLASH) $(SRAM) |
disasm: main.elf |
avr-objdump -S main.elf |
48,6 → 54,9 |
flash: main.hex |
$(FLASH_CMD) |
fuses: |
$(FUSES_CMD) |
crc.o: $(USBTINY)/crc.S $(USBTINY)/def.h usbtiny.h |
$(COMPILE.c) $(USBTINY)/crc.S |
int.o: $(USBTINY)/int.S $(USBTINY)/def.h usbtiny.h |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/crc.S |
---|
1,19 → 1,19 |
; ====================================================================== |
; Calculate and append CRC |
; |
; The CRC is calculated 4 bits at a time, using a precomputed table of |
; 16 values. Each value is 16 bits, but only the 8 significant bits are |
; stored. The table should not cross a 256-byte page. The check.py script |
; will check for this. |
; There are two versions of the CRC16 calculation, selectable by the |
; USBTINY_FAST_CRC macro. The default implementation calculates one bit |
; at a time, and is compact but relatively slow. The "fast" version |
; processes 4 bits at a time, and is about twice as fast, but 42 bytes |
; larger. |
; |
; A bitwise algorithm would be a little smaller, but takes more time. |
; In fact, it takes too much time for the USB controller in my laptop. |
; The poll frequently is so high, that a lot of time is spent in the |
; interrupt handler, sending NAK packets, leaving little time for the |
; actual checksum calculation. An 8 bit algoritm would be even faster, |
; The fast version calculates 4 bits at a time, using a precomputed table |
; of 16 values. Each value is 16 bits, but only the 8 significant bits |
; are stored. The table should not cross a 256-byte page. The check.py |
; script will check for this. An 8 bit algoritm would be even faster, |
; but requires a lookup table of 512 bytes. |
; |
; Copyright (C) 2006 Dick Streefland |
; Copyright 2006-2010 Dick Streefland |
; |
; This is free software, licensed under the terms of the GNU General |
; Public License as published by the Free Software Foundation. |
21,6 → 21,7 |
#include "def.h" |
#if USBTINY_FAST_CRC |
; ---------------------------------------------------------------------- |
; void crc(unsigned char *data, unsigned char len); |
; ---------------------------------------------------------------------- |
41,30 → 42,42 |
movw XL, r24 |
ldi crc_h, 0xff |
ldi crc_l, 0xff |
lsl len |
breq done |
ldi zl, lo8(crc4tab) |
ldi ZH, hi8(crc4tab) |
rjmp entry |
next_nibble: |
; b = (len & 1 ? b >> 4 : *data++) |
swap b |
sbrs len, 0 |
next_byte: |
; crc ^= b |
ld b, X+ |
eor crc_l, b |
; index = (crc ^ b) & 0x0f |
; index1 = crc & 0x0f |
mov ZL, crc_l |
eor ZL, b |
andi ZL, 0x0f |
; crc >>= 4 |
; tmp = crc4tab[index1] |
add ZL, zl |
lpm tmp, Z+ |
; index2 = (crc >> 4) |
mov ZL, crc_l |
swap ZL |
; crc >>= 8 |
mov crc_l, crc_h |
; index2 = (index2 ^ tmp) & 0xf |
mov crc_h, tmp |
andi tmp, 1 |
eor ZL, tmp |
andi ZL, 0x0f |
; treat upper byte of CRC remainder |
swap crc_h |
swap crc_l |
andi crc_l, 0x0f |
mov tmp, crc_h |
andi tmp, 0xf0 |
or crc_l, tmp |
andi crc_h, 0x0f |
andi tmp, 0xe0 |
eor crc_l, tmp |
; crc ^= crc4tab[index] |
add ZL, zl |
74,9 → 87,10 |
eor crc_h, tmp |
eor crc_l, tmp |
; next nibble |
entry: |
; next byte |
dec len |
brne next_nibble |
brpl next_byte |
done: |
; crc ^= 0xffff |
93,7 → 107,7 |
; CRC table. As bits 1..8 are always zero, omit them. |
; ---------------------------------------------------------------------- |
.section .progmem.crc,"a",@progbits |
;;; .align 4 ; avoid crossing a page boundary |
;;; .align 4 ; crude way to avoid crossing a page boundary |
crc4tab: |
.byte 0x00+0x00 |
.byte 0xcc+0x01 |
121,3 → 135,49 |
crc ^= 0xA001 # X^16 + X^15 + X^2 + 1 (reversed) |
print "\t.byte\t0x%02x+0x%02x" % (crc >> 8, crc & 0xff) |
\* ---------------------------------------------------------------------- */ |
#else |
; ---------------------------------------------------------------------- |
; void crc(unsigned char *data, unsigned char len); |
; ---------------------------------------------------------------------- |
#define data r24 |
#define len r22 |
#define b r18 |
#define con_01 r19 |
#define con_a0 r20 |
#define crc_l r24 |
#define crc_h r25 |
.text |
.global crc |
.type crc, @function |
crc: |
movw XL, r24 |
ldi crc_h, 0xff |
ldi crc_l, 0xff |
tst len |
breq done1 |
ldi con_a0, 0xa0 |
ldi con_01, 0x01 |
next_byte: |
ld b, X+ |
eor crc_l, b |
ldi b, 8 |
next_bit: |
lsr crc_h |
ror crc_l |
brcc noxor |
eor crc_h, con_a0 |
eor crc_l, con_01 |
noxor: |
dec b |
brne next_bit |
dec len |
brne next_byte |
done1: |
com crc_l |
com crc_h |
st X+, crc_l |
st X+, crc_h |
ret |
#endif |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/def.h |
---|
1,7 → 1,7 |
// ====================================================================== |
// Common definitions for the USB driver |
// |
// Copyright (C) 2006 Dick Streefland |
// Copyright 2006-2010 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |
19,12 → 19,12 |
#define CAT3(a,b,c) CAT3EXP(a, b, c) |
#define CAT3EXP(a,b,c) a ## b ## c |
// I/O Ports |
// I/O Ports for USB |
#define USB_IN CAT2(PIN, USBTINY_PORT) |
#define USB_OUT CAT2(PORT, USBTINY_PORT) |
#define USB_DDR CAT2(DDR, USBTINY_PORT) |
// I/O bit masks |
// I/O bit masks for USB |
#define USB_MASK_DMINUS (1 << (USBTINY_DMINUS)) |
#define USB_MASK_DPLUS (1 << (USBTINY_DPLUS)) |
#define USB_MASK (USB_MASK_DMINUS | USB_MASK_DPLUS) |
36,11 → 36,7 |
# define USB_INT_CONFIG MCUCR |
#endif |
#define USB_INT_CONFIG_SET ((1 << CAT3(ISC,USBTINY_INT,1)) | (1 << CAT3(ISC,USBTINY_INT,0))) |
#if defined SIG_INT0 |
# define USB_INT_VECTOR CAT2(SIG_INT, USBTINY_INT) |
#else |
# define USB_INT_VECTOR CAT2(SIG_INTERRUPT, USBTINY_INT) |
#endif |
#define USB_INT_VECTOR CAT3(INT, USBTINY_INT, _vect) |
// Interrupt enable |
#if defined GIMSK |
59,6 → 55,9 |
# define USB_INT_PENDING GIFR |
#endif |
#define USB_INT_PENDING_BIT CAT2(INTF,USBTINY_INT) |
#if defined INF0 && ! defined INTF0 |
# define INTF0 INF0 // fix for incorrect definition in iotn13.h |
#endif |
// USB PID values |
#define USB_PID_SETUP 0x2d |
72,3 → 71,26 |
// Various constants |
#define USB_BUFSIZE 11 // PID + data + CRC |
// Bit manipulation macros |
#define BIT_CLR(reg,bit) { (reg) &= ~ _BV(bit); } |
#define BIT_SET(reg,bit) { (reg) |= _BV(bit); } |
#define BIT_TST(reg,bit) (((reg) & _BV(bit)) != 0) |
// I/O port manipulation macros |
#define DDR_CLR(p,b) BIT_CLR(DDR ## p, b) |
#define DDR_SET(p,b) BIT_SET(DDR ## p, b) |
#define PORT_CLR(p,b) BIT_CLR(PORT ## p, b) |
#define PORT_SET(p,b) BIT_SET(PORT ## p, b) |
#define PORT_TST(p,b) BIT_TST(PORT ## p, b) |
#define PIN_TST(p,b) BIT_TST(PIN ## p, b) |
#define PIN_SET(p,b) BIT_SET(PIN ## p, b) |
// Macros that can be used with an argument of the form (port,bit) |
#define INPUT(bit) DDR_CLR bit |
#define OUTPUT(bit) DDR_SET bit |
#define CLR(bit) PORT_CLR bit |
#define SET(bit) PORT_SET bit |
#define ISSET(bit) PORT_TST bit |
#define TST(bit) PIN_TST bit |
#define TOGGLE(bit) PIN_SET bit |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/int.S |
---|
9,20 → 9,26 |
; When a DATA0/DATA1 packet directly follows a SETUP or OUT packet, while |
; this interrupt handler is not yet finished, there would be no time to |
; return and take another interrupt. In that case, the second packet is |
; decoded directly in the same invocation. |
; decoded directly in the same invocation. A packet immediately following |
; an ignored packet is also decoded directly. |
; |
; This code is *extremely* time critical. For instance, there is not a |
; single spare cycle in the receiver loop, and only two in the transmitter |
; loop. In addition, the various code paths are laid out in such a way that |
; the various USB timeouts are not violated, in particular the maximum time |
; between the reception of a packet and the reply, which is 6.5 bit times |
; for a detachable cable (TRSPIPD1), and 7.5 bit times for a captive cable |
; (TRSPIPD2). The worst-case delay here is 51 cycles, which is just below |
; the 52 cycles for a detachable cable. |
; between the reception of a packet and the reply, which is 7.5 bit times |
; (TRSPIPD2) for a low-speed USB captive cable. The worst-case delay here |
; is 51 cycles, which is well below the 60 cycles limit, and even below the |
; 6.5 bit times limit for a detachable cable (TRSPIPD1). |
; |
; The interrupt handler must be reached within 34 cycles after D+ goes high |
; for the first time, so the interrupts should not be disabled for longer |
; than 34-4-2=28 cycles. |
; for the first time. The interrupt response time is 4 cycles, and the RJMP |
; in the vector table takes 2 cycles. Therefore, the interrupts should not |
; be disabled for longer than: 34 - 4 - 2 = 28 cycles. When the I-bit is |
; reenabled, a single instruction is always executed before a pending |
; interrupt is served, so this instruction should be included in the |
; calculation. For RETI, the next instruction can be anything, so we |
; should assume the worst-case of 4 cycles. |
; |
; The end-of-packet (EOP) is sampled in the second bit, because the USB |
; standard allows the EOP to be delayed by up to one bit. As the EOP |
30,7 → 36,7 |
; |
; Stack usage including the return address: 11 bytes. |
; |
; Copyright (C) 2006 Dick Streefland |
; Copyright 2006-2010 Dick Streefland |
; |
; This is free software, licensed under the terms of the GNU General |
; Public License as published by the Free Software Foundation. |
45,6 → 51,7 |
tx_ack: .byte USB_PID_ACK ; ACK packet |
tx_nak: .byte USB_PID_NAK ; NAK packet |
.lcomm token_pid, 1 ; PID of most recent token packet |
.global __do_copy_data |
; ---------------------------------------------------------------------- |
; register definitions |
291,46 → 298,51 |
; clear pending interrupt (SE0+3) |
ldi byte, 1<<USB_INT_PENDING_BIT |
out USB_INT_PENDING, byte ; clear pending bit at end of packet |
; ignore packets shorter than 3 bytes |
; calculate packet length |
subi count, USB_BUFSIZE |
neg count ; count = packet length |
cpi count, 3 |
brlo ignore |
; get PID |
sub YL, count |
sbci YH, 0 |
ld pid, Y |
; check for DATA0/DATA1 first, as this is the critical path (SE0+12) |
cpi pid, USB_PID_DATA0 |
breq is_data ; handle DATA0 packet |
cpi pid, USB_PID_DATA1 |
breq is_data ; handle DATA1 packet |
; check ADDR (SE0+16) |
; separate out the non-Token packets (SE0+11) |
sbrc pid, 1 |
rjmp is_data_handshake ; jump for Data or Handshake packet |
; check ADDR of Token packet (SE0+13) |
ldd addr, Y+1 |
andi addr, 0x7f |
lds tmp, usb_address |
cp addr, tmp ; is this packet for me? |
brne ignore ; no, ignore |
; check for other PIDs (SE0+23) |
; dispatch Token packets (SE0+20) |
cpi pid, USB_PID_IN |
breq is_in ; handle IN packet |
cpi pid, USB_PID_SETUP |
breq is_setup_out ; handle SETUP packet |
cpi pid, USB_PID_OUT |
breq is_setup_out ; handle OUT packet |
brne is_setup_out ; handle SETUP and OUT packets |
; ---------------------------------------------------------------------- |
; exit point for ignored packets |
; Handle IN (SE0+22) |
; ---------------------------------------------------------------------- |
lds count, usb_tx_len |
tst count ; data ready? |
breq nak ; no, reply with NAK |
lds tmp, usb_rx_len |
tst tmp ; unprocessed input packet? |
brne nak ; yes, don't send old data for new packet |
sts usb_tx_len, tmp ; buffer is available again (after reti) |
lds tmp, usb_new_address |
sts usb_address, tmp ; assign new address at end of transfer |
ldi YL, lo8(usb_tx_buf) |
ldi YH, hi8(usb_tx_buf) |
rjmp send_packet ; SE0+40, SE0 --> SOP <= 51 |
; ---------------------------------------------------------------------- |
; exit point for ignored packets (SE0+21) |
; ---------------------------------------------------------------------- |
ignore: |
clr tmp |
sts token_pid, tmp |
pop even |
pop fixup |
pop byte |
rjmp return |
clr pid |
ignore0: |
; ---------------------------------------------------------------------- |
; Handle SETUP/OUT (SE0+30) |
; Handle SETUP/OUT (SE0+23) |
; ---------------------------------------------------------------------- |
is_setup_out: |
sts token_pid, pid ; save PID of token packet |
339,10 → 351,10 |
pop byte |
in count, USB_INT_PENDING ; next packet already started? |
sbrc count, USB_INT_PENDING_BIT |
rjmp sync ; yes, get it right away (SE0+42) |
rjmp sync ; yes, get it right away (SE0+35) |
; ---------------------------------------------------------------------- |
; restore registers and return from interrupt |
; restore registers and return from interrupt (SE0+34) |
; ---------------------------------------------------------------------- |
return: |
pop count |
355,27 → 367,26 |
reti |
; ---------------------------------------------------------------------- |
; Handle IN (SE0+26) |
; send NAK packet (SE0+31) |
; ---------------------------------------------------------------------- |
is_in: |
lds count, usb_tx_len |
tst count ; data ready? |
breq nak ; no, reply with NAK |
lds tmp, usb_rx_len |
tst tmp ; unprocessed input packet? |
brne nak ; yes, don't send old data for new packet |
sts usb_tx_len, tmp ; buffer is available again (after reti) |
ldi YL, lo8(usb_tx_buf) |
ldi YH, hi8(usb_tx_buf) |
rjmp send_packet ; SE0+40, SE0 --> SOP <= 51 |
nak: |
ldi YL, lo8(tx_nak) |
ldi YH, hi8(tx_nak) |
rjmp send_token |
; ---------------------------------------------------------------------- |
; Handle DATA0/DATA1 (SE0+17) |
; Handle Data and Handshake packets (SE0+14) |
; ---------------------------------------------------------------------- |
is_data: |
is_data_handshake: |
andi pid, 0x01 |
breq ignore0 ; ignore ACK/NAK/STALL |
; ---------------------------------------------------------------------- |
; Handle DATA0/DATA1 (SE0+16) |
; ---------------------------------------------------------------------- |
lds pid, token_pid |
tst pid ; data following our SETUP/OUT |
breq ignore ; no, ignore |
breq ignore0 ; no, ignore |
lds tmp, usb_rx_len |
tst tmp ; buffer free? |
brne nak ; no, reply with NAK |
387,21 → 398,12 |
sts usb_rx_off, tmp |
; ---------------------------------------------------------------------- |
; send ACK packet (SE0+35) |
; send ACK packet (SE0+34) |
; ---------------------------------------------------------------------- |
ack: |
ldi YL, lo8(tx_ack) |
ldi YH, hi8(tx_ack) |
rjmp send_token |
; ---------------------------------------------------------------------- |
; send NAK packet (SE0+36) |
; ---------------------------------------------------------------------- |
nak: |
ldi YL, lo8(tx_nak) |
ldi YH, hi8(tx_nak) |
send_token: |
ldi count, 1 ; SE0+40, SE0 --> SOP <= 51 |
ldi count, 1 ; SE0+37, SE0 --> SOP <= 48 |
; ---------------------------------------------------------------------- |
; acquire the bus and send a packet (11 cycles to SOP) |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.c |
---|
17,10 → 17,10 |
// the callback functions usb_in() and usb_out() will be called for IN |
// and OUT transfers. |
// |
// Maximum stack usage (gcc 3.4.3 & 4.1.0) of usb_poll(): 5 bytes plus |
// Maximum stack usage (gcc 4.1.0 & 4.3.4) of usb_poll(): 5 bytes plus |
// possible additional stack usage in usb_setup(), usb_in() or usb_out(). |
// |
// Copyright (C) 2006 Dick Streefland |
// Copyright 2006-2010 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |
58,7 → 58,8 |
byte_t usb_tx_buf[USB_BUFSIZE]; // output buffer |
byte_t usb_tx_len; // output buffer size, 0 means empty |
byte_t usb_address; // assigned USB address |
byte_t usb_address; // assigned device address |
byte_t usb_new_address; // new device address |
// ---------------------------------------------------------------------- |
// Local data |
75,7 → 76,6 |
static byte_t usb_tx_state; // TX_STATE_*, see enum above |
static byte_t usb_tx_total; // total transmit size |
static byte_t* usb_tx_data; // pointer to data to transmit |
static byte_t new_address; // new device address |
#if defined USBTINY_VENDOR_NAME |
struct |
83,7 → 83,7 |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_VENDOR_NAME)-1]; |
} string_vendor PROGMEM = |
} const string_vendor PROGMEM = |
{ |
2 * sizeof(USBTINY_VENDOR_NAME), |
DESCRIPTOR_TYPE_STRING, |
100,7 → 100,7 |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_DEVICE_NAME)-1]; |
} string_device PROGMEM = |
} const string_device PROGMEM = |
{ |
2 * sizeof(USBTINY_DEVICE_NAME), |
DESCRIPTOR_TYPE_STRING, |
117,7 → 117,7 |
byte_t length; |
byte_t type; |
int string[sizeof(USBTINY_SERIAL)-1]; |
} string_serial PROGMEM = |
} const string_serial PROGMEM = |
{ |
2 * sizeof(USBTINY_SERIAL), |
DESCRIPTOR_TYPE_STRING, |
129,7 → 129,7 |
#endif |
#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID |
static byte_t string_langid [] PROGMEM = |
static byte_t const string_langid [] PROGMEM = |
{ |
4, // bLength |
DESCRIPTOR_TYPE_STRING, // bDescriptorType (string) |
138,11 → 138,11 |
#endif |
// Device Descriptor |
static byte_t descr_device [18] PROGMEM = |
static byte_t const descr_device [18] PROGMEM = |
{ |
18, // bLength |
DESCRIPTOR_TYPE_DEVICE, // bDescriptorType |
LE(0x0110), // bcdUSB |
LE(0x0101), // bcdUSB |
USBTINY_DEVICE_CLASS, // bDeviceClass |
USBTINY_DEVICE_SUBCLASS, // bDeviceSubClass |
USBTINY_DEVICE_PROTOCOL, // bDeviceProtocol |
157,7 → 157,7 |
}; |
// Configuration Descriptor |
static byte_t descr_config [] PROGMEM = |
static byte_t const descr_config [] PROGMEM = |
{ |
9, // bLength |
DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType |
223,7 → 223,10 |
} |
else if ( data[1] == 5 ) // SET_ADDRESS |
{ |
new_address = data[2]; |
usb_new_address = data[2]; |
#ifdef USBTINY_USB_OK_LED |
SET(USBTINY_USB_OK_LED);// LED on |
#endif |
} |
else if ( data[1] == 6 ) // GET_DESCRIPTOR |
{ |
371,6 → 374,13 |
{ |
USB_INT_CONFIG |= USB_INT_CONFIG_SET; |
USB_INT_ENABLE |= (1 << USB_INT_ENABLE_BIT); |
#ifdef USBTINY_USB_OK_LED |
OUTPUT(USBTINY_USB_OK_LED); |
#endif |
#ifdef USBTINY_DMINUS_PULLUP |
SET(USBTINY_DMINUS_PULLUP); |
OUTPUT(USBTINY_DMINUS_PULLUP); // enable pullup on D- |
#endif |
sei(); |
} |
392,16 → 402,9 |
usb_rx_len = 0; // accept next packet |
} |
// refill an empty transmit buffer, when the transmitter is active |
if ( usb_tx_len == 0 ) |
if ( usb_tx_len == 0 && usb_tx_state != TX_STATE_IDLE ) |
{ |
if ( usb_tx_state != TX_STATE_IDLE ) |
{ |
usb_transmit(); |
} |
else |
{ // change the USB address at the end of a transfer |
usb_address = new_address; |
} |
usb_transmit(); |
} |
// check for USB bus reset |
for ( i = 10; i > 0 && ! (USB_IN & USB_MASK_DMINUS); i-- ) |
409,10 → 412,10 |
} |
if ( i == 0 ) |
{ // SE0 for more than 2.5uS is a reset |
cli(); |
usb_tx_len=0; |
usb_rx_len=0; |
new_address = 0; |
sei(); |
usb_new_address = 0; |
usb_address = 0; |
#ifdef USBTINY_USB_OK_LED |
CLR(USBTINY_USB_OK_LED); // LED off |
#endif |
} |
} |
/Designs/Tools/i2c_AVR_USB/SW/firmware/usbtiny/usb.h |
---|
1,7 → 1,7 |
// ====================================================================== |
// Public interface of the USB driver |
// |
// Copyright (C) 2006 Dick Streefland |
// Copyright 2006-2008 Dick Streefland |
// |
// This is free software, licensed under the terms of the GNU General |
// Public License as published by the Free Software Foundation. |