From 661298acc0a2d18daf3e95169409b6163d47569e Mon Sep 17 00:00:00 2001 From: "komatsu@google.com" Date: Wed, 6 Jun 2012 06:06:16 +0000 Subject: [PATCH] Initial upload of the Mozc-morse project. --- COPYING | 339 ++++++++++ Makefile | 121 ++++ Readme.txt | 128 ++++ circuit.png | Bin 0 -> 114449 bytes dit-dah.c | 162 +++++ romaji_dit-dah.txt | 309 ++++++++++ space.c | 66 ++ third_party/usbdrv/Changelog.txt | 318 ++++++++++ third_party/usbdrv/CommercialLicense.txt | 166 +++++ third_party/usbdrv/License.txt | 361 +++++++++++ third_party/usbdrv/Readme.txt | 172 ++++++ third_party/usbdrv/USB-ID-FAQ.txt | 149 +++++ third_party/usbdrv/USB-IDs-for-free.txt | 154 +++++ third_party/usbdrv/asmcommon.inc | 188 ++++++ third_party/usbdrv/oddebug.c | 50 ++ third_party/usbdrv/oddebug.h | 123 ++++ third_party/usbdrv/usbconfig-prototype.h | 376 ++++++++++++ third_party/usbdrv/usbdrv.c | 628 +++++++++++++++++++ third_party/usbdrv/usbdrv.h | 736 ++++++++++++++++++++++ third_party/usbdrv/usbdrvasm.S | 393 ++++++++++++ third_party/usbdrv/usbdrvasm.asm | 21 + third_party/usbdrv/usbdrvasm12.inc | 393 ++++++++++++ third_party/usbdrv/usbdrvasm128.inc | 750 +++++++++++++++++++++++ third_party/usbdrv/usbdrvasm15.inc | 423 +++++++++++++ third_party/usbdrv/usbdrvasm16.inc | 346 +++++++++++ third_party/usbdrv/usbdrvasm165.inc | 453 ++++++++++++++ third_party/usbdrv/usbdrvasm18-crc.inc | 707 +++++++++++++++++++++ third_party/usbdrv/usbdrvasm20.inc | 360 +++++++++++ third_party/usbdrv/usbportability.h | 144 +++++ usb_keyboard.c | 108 ++++ usb_keyboard.h | 32 + usbconfig.h | 382 ++++++++++++ 32 files changed, 9058 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 Readme.txt create mode 100644 circuit.png create mode 100644 dit-dah.c create mode 100644 romaji_dit-dah.txt create mode 100644 space.c create mode 100644 third_party/usbdrv/Changelog.txt create mode 100644 third_party/usbdrv/CommercialLicense.txt create mode 100644 third_party/usbdrv/License.txt create mode 100644 third_party/usbdrv/Readme.txt create mode 100644 third_party/usbdrv/USB-ID-FAQ.txt create mode 100644 third_party/usbdrv/USB-IDs-for-free.txt create mode 100644 third_party/usbdrv/asmcommon.inc create mode 100644 third_party/usbdrv/oddebug.c create mode 100644 third_party/usbdrv/oddebug.h create mode 100644 third_party/usbdrv/usbconfig-prototype.h create mode 100644 third_party/usbdrv/usbdrv.c create mode 100644 third_party/usbdrv/usbdrv.h create mode 100644 third_party/usbdrv/usbdrvasm.S create mode 100644 third_party/usbdrv/usbdrvasm.asm create mode 100644 third_party/usbdrv/usbdrvasm12.inc create mode 100644 third_party/usbdrv/usbdrvasm128.inc create mode 100644 third_party/usbdrv/usbdrvasm15.inc create mode 100644 third_party/usbdrv/usbdrvasm16.inc create mode 100644 third_party/usbdrv/usbdrvasm165.inc create mode 100644 third_party/usbdrv/usbdrvasm18-crc.inc create mode 100644 third_party/usbdrv/usbdrvasm20.inc create mode 100644 third_party/usbdrv/usbportability.h create mode 100644 usb_keyboard.c create mode 100644 usb_keyboard.h create mode 100644 usbconfig.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..06fe18d --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + USB Morse key for Google Japanese Input + Copyright (C) 2012 Google Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..14e3cb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,121 @@ +# +# Copyright 2012 Google Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + + +DEVICE = attiny2313 +F_CPU = 12000000 # in Hz + +AVRDUDE = avrdude -c usbasp -p $(DEVICE) # edit this line for your programmer + +USBDRV_DIR = third_party/usbdrv + +CFLAGS = -I$(USBDRV_DIR) -I. -DDEBUG_LEVEL=0 + +OBJECTS = $(USBDRV_DIR)/usbdrv.o $(USBDRV_DIR)/usbdrvasm.o $(USBDRV_DIR)/oddebug.o usb_keyboard.o + +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE) + +FUSE_L = 0xef +FUSE_H = 0xdb +# ATTiny2313 FUSE_L (Fuse low byte): +# 0xef = 1 1 1 0 1 1 1 1 +# ^ ^ \+/ \--+--/ +# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz) +# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) +# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) +# +-------------------- CKDIV8 (divide clock by 8 -> don't divide) +# ATTiny2313 FUSE_H (Fuse high byte): +# 0xdb = 1 1 0 1 1 0 1 1 +# ^ ^ ^ ^ \-+-/ ^ +# | | | | | +---- RSTDISBL (disable external reset -> enabled) +# | | | | +-------- BODLEVEL 2..0 (brownout trigger level -> 2.7V) +# | | | +-------------- WDTON (watchdog timer always on -> disable) +# | | +---------------- SPIEN (enable serial programming -> enabled) +# | +------------------ EESAVE (preserve EEPROM on Chip Erase -> not preserved) +# +-------------------- DWEN (debug wire enable) + + +# symbolic targets: +help: + @echo "This Makefile has no default rule. Use one of the following:" + @echo "make hex ........... to build dit-dah.hex and space.hex" + @echo "make program ....... to flash fuses and dit-dah firmware" + @echo "make fuse .......... to flash the fuses" + @echo "make flash_dit-dah . to flash the dit-dah firmware" + @echo "make flash_space ... to flash the space key firmware" + @echo "make clean ......... to delete objects and hex file" + +hex: dit-dah.hex space.hex + +program: flash_dit-dah fuse + +# rule for programming fuse bits: +fuse: + @[ "$(FUSE_H)" != "" -a "$(FUSE_L)" != "" ] || \ + { echo "*** Edit Makefile and choose values for FUSE_L and FUSE_H!"; exit 1; } + $(AVRDUDE) -U hfuse:w:$(FUSE_H):m -U lfuse:w:$(FUSE_L):m + +# rule for uploading firmware: +flash_dit-dah: dit-dah.hex + $(AVRDUDE) -U flash:w:dit-dah.hex:i + +flash_space: space.hex + $(AVRDUDE) -U flash:w:space.hex:i + +# rule for deleting dependent files (those which can be built by Make): +clean: + rm -f dit-dah.{hex,lst,obj,cof,list,map,eep.hex,elf,s} space.{hex,lst,obj,cof,list,map,eep.hex,elf,s} *.o $(USBDRV_DIR)/*.o $(USBDRV_DIR)/oddebug.s $(USBDRV_DIR)/usbdrv.s + +# Generic rule for compiling C files: +.c.o: + $(COMPILE) -c $< -o $@ + +dit-dah.o: dit-dah.c usbconfig.h usb_keyboard.h + +space.o: space.c usbconfig.h usb_keyboard.h + +# Generic rule for assembling Assembler source files: +.S.o: + $(COMPILE) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +# Generic rule for compiling C to assembler, used for debugging only. +.c.s: + $(COMPILE) -S $< -o $@ + +# file targets: + +dit-dah.elf: $(OBJECTS) dit-dah.o + $(COMPILE) -o dit-dah.elf $(OBJECTS) dit-dah.o + +space.elf: $(OBJECTS) space.o + $(COMPILE) -o space.elf $(OBJECTS) space.o + +dit-dah.hex: dit-dah.elf + rm -f dit-dah.hex dit-dah.eep.hex + avr-objcopy -j .text -j .data -O ihex dit-dah.elf dit-dah.hex + avr-size dit-dah.hex + +space.hex: space.elf + rm -f space.hex space.eep.hex + avr-objcopy -j .text -j .data -O ihex space.elf space.hex + avr-size space.hex diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..44cf342 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,128 @@ +Copyright 2012 Google Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + + +===================================== + Summary +===================================== + +Circuit diagram and firmwares of Morse key for Google Japanese Input. + + +===================================== + Files +===================================== + +* V-USB library files +third_party/usbdrv/ + +This directory and files are copied from V-USB files available at +http://www.obdev.at/vusb/ + +* Documents +COPYING ...... Open Source license for this software. +Readme.txt ... The file you are currently reading. + +* Circuit diagram +circuit.png + +* Firmware source codes +Makefile +dit-dah.c +space.c +usbconfig.h +usb_keyboard.c +usb_keyboard.h + +* Romaji table for Google Japanese Input +romaji_dit-dah.txt + +===================================== + Building & Wriging Firmware +===================================== + +* Building firmware binariese + +AVR-gcc is required. + +$ make hex + + +* Writing to the program memory + +Choose one of the HEX files to write. +- space.hex: Sends space key as the button is pressed. +- dit-dah.hex: Sends dits, dahs. + +Please refer documents of AVR programmer device you use. +Makefile contains "flash_space" and "flash_dit-dah" rules that writes +a hex file using Avrdude as an example. + + +* Programming the fuse bits + +Fuse bits must be configured in order to run the firmware correctly. + +lower byte: 0xef +higher byte: 0xdb + +# ATTiny2313 FUSE_L (Fuse low byte): +# 0xef = 1 1 1 0 1 1 1 1 +# ^ ^ \+/ \--+--/ +# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz) +# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) +# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) +# +-------------------- CKDIV8 (divide clock by 8 -> don't divide) +# ATTiny2313 FUSE_H (Fuse high byte): +# 0xdb = 1 1 0 1 1 0 1 1 +# ^ ^ ^ ^ \-+-/ ^ +# | | | | | +---- RSTDISBL (disable external reset -> enabled) +# | | | | +-------- BODLEVEL 2..0 (brownout trigger level -> 2.7V) +# | | | +-------------- WDTON (watchdog timer always on -> disable) +# | | +---------------- SPIEN (enable serial programming -> enabled) +# | +------------------ EESAVE (preserve EEPROM on Chip Erase +# | -> not preserved) +# +-------------------- DWEN (debug wire enable) + +Makefile contains "fuse" rule that programs fuse bits using Avrdude as an +example. + + +=================================================== + Using dit-dah firmware with Google Japanese Input +=================================================== + +"dit-dah" firmware is intended to be combined with Google Japanese Input, whose +romaji table is configured by "romaji_dit-dah.txt". + +Note: This process will overwrite existing romaji table. +If you have customized the romaji table, you should backup your data first. + +1. Open Properties +2. Press "Customize..." button +3. Open "Edit - Inport from file..." +4. Open "romaji_dit-dah.txt" in this directory + + +================== + References +================== + +The firmware and circuit diagram has been developed based on V-USB. + +* V-USB +http://www.obdev.at/vusb/ diff --git a/circuit.png b/circuit.png new file mode 100644 index 0000000000000000000000000000000000000000..df82d26c727ea3127074764551ce36f688ee8ce2 GIT binary patch literal 114449 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 literal 0 HcmV?d00001 diff --git a/dit-dah.c b/dit-dah.c new file mode 100644 index 0000000..ed08563 --- /dev/null +++ b/dit-dah.c @@ -0,0 +1,162 @@ +/* + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* + * USB keyboard to input by Morse code with help of special romaji table. + * The device transmits keycodes based on a single button input: + * + * "." for a short tap + * "-" for a long tap + * "/" for a pause after a tap + * [SPACE][ENTER] for a long pause after taps (convert and commit) + */ + +#include +#include +#include /* for _delay_ms() */ +#include "usb_keyboard.h" + +/* + * Thresholds for button pressing duration. + * + * A counter is increased every 10 milliseconds in the main loop. + * These time lengths are specified by the units of this counter. + * + * FYI: WPM = words per minute is commonly used unit of morse code speed. + * 1 word stands for 50 dots. For example, 10 WPM means 1 dot=0.12s + */ + +#define T_DIT 12 /* 10WPM */ + +/* minimum length of button press duration for a "dah" */ +#define THRESHOLD_DIT_DAH (T_DIT + 7) + +/* minimum length of time for a character separator */ +#define THRESHOLD_GAP (T_DIT * 3) + +/* length of time to wait before convert and commit */ +#define THRESHOLD_LONG_GAP 100 /* 1 second */ + + +#define EMPTY 255 + +int __attribute__((noreturn)) main(void) +{ + PORTB = 0xff; + + wdt_enable(WDTO_1S); + initUsbKeyboard(); + + /* sets up timer for button polling interval */ + /* timer 1 CTC mode, counts up at f[clock]/8 [Hz] */ + TCCR1B = (1 << WGM12) | (1 << CS11); + TCNT1 = 0; + OCR1A = (unsigned int)F_CPU / 8 * 100; /* 100Hz, T=10 milliseconds */ + + uchar key = 0; + + /* Keyboard keys to send. + * Since we have only 1 byte buffer, the first key is dropped if the second + * key is pushed before submitting it. + */ + uchar cmdBuf = EMPTY; + + uchar state = 0; + unsigned int count = 0; + while(1) { /* main event loop */ + wdt_reset(); + usbPoll(); + if(usbInterruptIsReady()) { + if (cmdBuf != EMPTY) { + prepareInterruptReport(cmdBuf); + if (cmdBuf == KEYCODE_NONE) { + cmdBuf = EMPTY; + } else { + /* release all keys */ + cmdBuf = KEYCODE_NONE; + } + } + } + /* samples the key input every 10 milliseconds to avoid chattering */ + if (bit_is_clear(TIFR, OCF1A)) { + continue; + } + TIFR |= _BV(OCF1A); /* clear OCF1A */ + + key = bit_is_clear(PINB, PINB0); + switch(state) { + case 0: /* initial state or after a long gap */ + if (key) { + state = 1; + count = 0; + } + break; + case 1: /* key is on */ + if (!key) { + cmdBuf = KEYCODE_PERIOD; /* dot */ + state = 3; + count = 0; + break; + } + if (count++ > THRESHOLD_DIT_DAH) { + state = 2; + count = 0; + } + break; + case 2: /* key has been pressed long enough to input dash */ + if (!key) { + cmdBuf = KEYCODE_HYPHEN; /* dash */ + state = 3; + count = 0; + } + break; + case 3: /* key was released. */ + if (key) { + state = 1; + count = 0; + break; + } + if (count++ > THRESHOLD_GAP) { + cmdBuf = KEYCODE_SLASH; /* word separator */ + state = 4; + } + break; + case 4: /* short gap, or long gap */ + if (key) { + state = 1; + count = 0; + break; + } + if (count++ > THRESHOLD_LONG_GAP) { + cmdBuf = KEYCODE_SPACE; /* start conversion */ + state = 5; + count = 0; + } + break; + case 5: /* long gap. pause before sending Enter key. */ + if (count++ > T_DIT) { + cmdBuf = KEYCODE_ENTER; /* commit conversion */ + state = 0; + count = 0; + } + break; + } + } +} diff --git a/romaji_dit-dah.txt b/romaji_dit-dah.txt new file mode 100644 index 0000000..00ea5d3 --- /dev/null +++ b/romaji_dit-dah.txt @@ -0,0 +1,309 @@ +-..---/ (本文) +.-.-../ (段落) +...-./ (訂正or終了) +.-.-.-/ 、 +--.--/ あ +.-/ い +..-/ う +-.---/ え +.-.../ お +.-../ か +-.-../ き +...-/ く +-.--/ け +----/ こ +-.-.-/ さ +--.-./ し +---.-/ す +.---./ せ +---./ そ +-./ た +..-./ ち +.--./ つ +.-.--/ て +..-../ と +.-./ な +-.-./ に +..../ ぬ +--.-/ ね +..--/ の +-.../ は +--..-/ ひ +--../ ふ +./ へ +-../ ほ +-..-/ ま +..-.-/ み +-/ む +-...-/ め +-..-./ も +.--/ や +-..--/ ゆ +--/ よ +.../ ら +--./ り +-.--./ る +---/ れ +.-.-/ ろ +-.-/ わ +.-..-/ ゐ +.--../ ゑ +.---/ を +.-.-./ ん +../ ゛ +..--./ ゜ +う../ ヴ +か../ が +き../ ぎ +く../ ぐ +け../ げ +こ../ ご +さ../ ざ +し../ じ +す../ ず +せ../ ぜ +そ../ ぞ +た../ だ +ち../ ぢ +つ../ づ +て../ で +と../ ど +は../ ば +ひ../ び +ふ../ ぶ +へ../ べ +ほ../ ぼ +う..--./ ぅ +は..--./ ぱ +ひ..--./ ぴ +ふ..--./ ぷ +へ..--./ ぺ +ほ..--./ ぽ +か..--./ ヵ +け..--./ ヶ +.--.-/ ー +-.--.-/ ( +.-..-./ ) +き- き - +き./ き へ +き.- き .- +き..- き ..- +き... き ... +く- く - +く./ く へ +く.- く .- +く..- く ..- +く... く ... +こ- こ - +こ./ こ へ +こ.- こ .- +こ..- こ ..- +こ... こ ... +さ- さ - +さ./ さ へ +さ.- さ .- +さ..- さ ..- +さ... さ ... +し- し - +し./ し へ +し.- し .- +し..- し ..- +し... し ... +す- す - +す./ す へ +す.- す .- +す..- す ..- +す... す ... +せ- せ - +せ./ せ へ +せ.- せ .- +せ..- せ ..- +せ... せ ... +そ- そ - +そ./ そ へ +そ.- そ .- +そ..- そ ..- +そ... そ ... +た- た - +た./ た へ +た.- た .- +た..- た ..- +た... た ... +ち- ち - +ち./ ち へ +ち.- ち .- +ち..- ち ..- +ち... ち ... +て- て - +て./ て へ +て.- て .- +て..- て ..- +て... て ... +と- と - +と./ と へ +と.- と .- +と..- と ..- +と... と ... +う- う - +う.- う .- +う./ う へ +う... う ... +う..-. う ..-. +う..-/ う う +う..--- う ..--- +う..--/ うの +は- は - +は.- は .- +は./ は へ +は... は ... +は..-. は ..-. +は..-/ は う +は..--- は ..--- +は..--/ はの +ひ- ひ - +ひ.- ひ .- +ひ./ ひ へ +ひ... ひ ... +ひ..-. ひ ..-. +ひ..-/ ひ う +ひ..--- ひ ..--- +ひ..--/ ひの +ふ- ふ - +ふ.- ふ .- +ふ./ ふ へ +ふ... ふ ... +ふ..-. ふ ..-. +ふ..-/ ふ う +ふ..--- ふ ..--- +ふ..--/ ふの +へ- へ - +へ.- へ .- +へ./ へ へ +へ... へ ... +へ..-. へ ..-. +へ..-/ へ う +へ..--- へ ..--- +へ..--/ への +ほ- ほ - +ほ.- ほ .- +ほ./ ほ へ +ほ... ほ ... +ほ..-. ほ ..-. +ほ..-/ ほ う +ほ..--- ほ ..--- +ほ..--/ ほの +つ- つ - +つ.- つ .- +つ./ つ へ +つ... つ ... +つ..-. つ ..-. +つ..-/ つ う +つ..--- つ ..--- +つ..--/ つの +か- か - +か.- か .- +か./ か へ +か... か ... +か..-. か ..-. +か..-/ か う +か..--- か ..--- +か..--/ かの +け- け - +け.- け .- +け./ け へ +け... け ... +け..-. け ..-. +け..-/ け う +け..--- け ..--- +け..--/ けの +つ..--./ っ +や- や - +や.- や .- +や./ や へ +や../ や゛ +や... や ... +や..-. や ..-. +や..-/ や う +や..--- や ..--- +や..--/ やの +や..--./ ゃ +ゆ- ゆ - +ゆ.- ゆ .- +ゆ./ ゆ へ +ゆ../ ゆ゛ +ゆ... ゆ ... +ゆ..-. ゆ ..-. +ゆ..-/ ゆ う +ゆ..--- ゆ ..--- +ゆ..--/ ゆの +ゆ..--./ ゅ +よ- よ - +よ.- よ .- +よ./ よ へ +よ../ よ゛ +よ... よ ... +よ..-. よ ..-. +よ..-/ よ う +よ..--- よ ..--- +よ..--/ よの +よ..--./ ょ +あ- あ - +あ.- あ .- +あ./ あ へ +あ../ あ゛ +あ... あ ... +あ..-. あ ..-. +あ..-/ あ う +あ..--- あ ..--- +あ..--/ あの +あ..--./ ぁ +い- い - +い.- い .- +い./ い へ +い../ い゛ +い... い ... +い..-. い ..-. +い..-/ い う +い..--- い ..--- +い..--/ いの +い..--./ ぃ +え- え - +え.- え .- +え./ え へ +え../ え゛ +え... え ... +え..-. え ..-. +え..-/ え う +え..--- え ..--- +え..--/ えの +え..--./ ぇ +お- お - +お.- お .- +お./ お へ +お../ お゛ +お... お ... +お..-. お ..-. +お..-/ お う +お..--- お ..--- +お..--/ おの +お..--./ ぉ +わ- わ - +わ.- わ .- +わ./ わ へ +わ../ わ゛ +わ... わ ... +わ..-. わ ..-. +わ..-/ わ う +わ..--- わ ..--- +わ..--/ わの +わ..--./ ゎ +.----/ 1 +..---/ 2 +...--/ 3 +....-/ 4 +...../ 5 +-..../ 6 +--.../ 7 +---../ 8 +----./ 9 +-----/ 0 diff --git a/space.c b/space.c new file mode 100644 index 0000000..4c857fa --- /dev/null +++ b/space.c @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* + * USB space key. + * Connect a nomally-open switch to PB0. The device works as a space bar. + */ + +#include +#include +#include /* for _delay_ms() */ +#include "usb_keyboard.h" + +int __attribute__((noreturn)) main(void) +{ + PORTB = 0xff; + + wdt_enable(WDTO_1S); + + initUsbKeyboard(); + + /* sets up timer for button polling interval */ + /* timer 1 CTC mode, counts up at f[clock]/8 [Hz] */ + TCCR1B = (1 << WGM12) | (1 << CS11); + TCNT1 = 0; + OCR1A = (unsigned int)F_CPU / 8 * 100; /* 100Hz, T=10 milliseconds */ + + uchar key = 0; + uchar lastKey = 0; + + while(1) { /* main event loop */ + wdt_reset(); + usbPoll(); + + /* samples the key input every 10 milliseconds to avoid chattering */ + if (bit_is_clear(TIFR, OCF1A)) { + continue; + } + TIFR |= _BV(OCF1A); /* clear OCF1A */ + + if(usbInterruptIsReady()){ + key = bit_is_clear(PINB, PINB0) ? KEYCODE_SPACE : KEYCODE_NONE; + if (key != lastKey) { + lastKey = key; + prepareInterruptReport(key); + } + } + } +} diff --git a/third_party/usbdrv/Changelog.txt b/third_party/usbdrv/Changelog.txt new file mode 100644 index 0000000..5fae8f3 --- /dev/null +++ b/third_party/usbdrv/Changelog.txt @@ -0,0 +1,318 @@ +This file documents changes in the firmware-only USB driver for atmel's AVR +microcontrollers. New entries are always appended to the end of the file. +Scroll down to the bottom to see the most recent changes. + +2005-04-01: + - Implemented endpoint 1 as interrupt-in endpoint. + - Moved all configuration options to usbconfig.h which is not part of the + driver. + - Changed interface for usbVendorSetup(). + - Fixed compatibility with ATMega8 device. + - Various minor optimizations. + +2005-04-11: + - Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() + and usbFunctionWrite() now. Added configuration options to choose which + of these functions to compile in. + - Assembler module delivers receive data non-inverted now. + - Made register and bit names compatible with more AVR devices. + +2005-05-03: + - Allow address of usbRxBuf on any memory page as long as the buffer does + not cross 256 byte page boundaries. + - Better device compatibility: works with Mega88 now. + - Code optimization in debugging module. + - Documentation updates. + +2006-01-02: + - Added (free) default Vendor- and Product-IDs bought from voti.nl. + - Added USBID-License.txt file which defines the rules for using the free + shared VID/PID pair. + - Added Readme.txt to the usbdrv directory which clarifies administrative + issues. + +2006-01-25: + - Added "configured state" to become more standards compliant. + - Added "HALT" state for interrupt endpoint. + - Driver passes the "USB Command Verifier" test from usb.org now. + - Made "serial number" a configuration option. + - Minor optimizations, we now recommend compiler option "-Os" for best + results. + - Added a version number to usbdrv.h + +2006-02-03: + - New configuration variable USB_BUFFER_SECTION for the memory section where + the USB rx buffer will go. This defaults to ".bss" if not defined. Since + this buffer MUST NOT cross 256 byte pages (not even touch a page at the + end), the user may want to pass a linker option similar to + "-Wl,--section-start=.mybuffer=0x800060". + - Provide structure for usbRequest_t. + - New defines for USB constants. + - Prepared for HID implementations. + - Increased data size limit for interrupt transfers to 8 bytes. + - New macro usbInterruptIsReady() to query interrupt buffer state. + +2006-02-18: + - Ensure that the data token which is sent as an ack to an OUT transfer is + always zero sized. This fixes a bug where the host reports an error after + sending an out transfer to the device, although all data arrived at the + device. + - Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). + +* Release 2006-02-20 + + - Give a compiler warning when compiling with debugging turned on. + - Added Oleg Semyonov's changes for IAR-cc compatibility. + - Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() + (also thanks to Oleg!). + - Rearranged tests in usbPoll() to save a couple of instructions in the most + likely case that no actions are pending. + - We need a delay between the SET ADDRESS request until the new address + becomes active. This delay was handled in usbPoll() until now. Since the + spec says that the delay must not exceed 2ms, previous versions required + aggressive polling during the enumeration phase. We have now moved the + handling of the delay into the interrupt routine. + - We must not reply with NAK to a SETUP transaction. We can only achieve this + by making sure that the rx buffer is empty when SETUP tokens are expected. + We therefore don't pass zero sized data packets from the status phase of + a transfer to usbPoll(). This change MAY cause troubles if you rely on + receiving a less than 8 bytes long packet in usbFunctionWrite() to + identify the end of a transfer. usbFunctionWrite() will NEVER be called + with a zero length. + +* Release 2006-03-14 + + - Improved IAR C support: tiny memory model, more devices + - Added template usbconfig.h file under the name usbconfig-prototype.h + +* Release 2006-03-26 + + - Added provision for one more interrupt-in endpoint (endpoint 3). + - Added provision for one interrupt-out endpoint (endpoint 1). + - Added flowcontrol macros for USB. + - Added provision for custom configuration descriptor. + - Allow ANY two port bits for D+ and D-. + - Merged (optional) receive endpoint number into global usbRxToken variable. + - Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the + variable name from the single port letter instead of computing the address + of related ports from the output-port address. + +* Release 2006-06-26 + + - Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the + new features. + - Removed "#warning" directives because IAR does not understand them. Use + unused static variables instead to generate a warning. + - Do not include when compiling with IAR. + - Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each + USB descriptor should be handled. It is now possible to provide descriptor + data in Flash, RAM or dynamically at runtime. + - STALL is now a status in usbTxLen* instead of a message. We can now conform + to the spec and leave the stall status pending until it is cleared. + - Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the + application code to reset data toggling on interrupt pipes. + +* Release 2006-07-18 + + - Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes + an assembler error. + - usbDeviceDisconnect() takes pull-up resistor to high impedance now. + +* Release 2007-02-01 + + - Merged in some code size improvements from usbtiny (thanks to Dick + Streefland for these optimizations!) + - Special alignment requirement for usbRxBuf not required any more. Thanks + again to Dick Streefland for this hint! + - Reverted to "#warning" instead of unused static variables -- new versions + of IAR CC should handle this directive. + - Changed Open Source license to GNU GPL v2 in order to make linking against + other free libraries easier. We no longer require publication of the + circuit diagrams, but we STRONGLY encourage it. If you improve the driver + 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 diff --git a/third_party/usbdrv/CommercialLicense.txt b/third_party/usbdrv/CommercialLicense.txt new file mode 100644 index 0000000..11d07d9 --- /dev/null +++ b/third_party/usbdrv/CommercialLicense.txt @@ -0,0 +1,166 @@ +V-USB Driver Software License Agreement +Version 2009-08-03 + +THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN +ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING +THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. + + +1 DEFINITIONS + +1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, +Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. + +1.2 "You" shall mean the Licensee. + +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 V-USB. + +2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the +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 +the source code and your copy of V-USB according to your needs. + +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 (Clay Logic, www.claylogic.com). 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 +applicable. Which one is determined by the amount you pay to OBJECTIVE +DEVELOPMENT, see section 4 ("Payment") below. + +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 V-USB according to section 2 above in no +more than 150 hardware units. + +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 +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 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 +such consent is obtained, you may permanently transfer this License to +another party. The recipient of such transfer must agree to all terms and +conditions of this Agreement. + +3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not +expressly granted. + +3.5 Non-Exclusive Rights. Your license rights under this Agreement are +non-exclusive. + +3.6 Third Party Rights. This Agreement cannot grant you rights controlled +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 V-USB will not pass certification because it does not +implement checksum verification and the microcontroller ports do not meet +the electrical specifications. + + +4 PAYMENT + +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/vusb/license.html. You agree to pay the amount listed +there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor +or reseller. + + +5 COPYRIGHT AND OWNERSHIP + +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. + + +6 TERM AND TERMINATION + +6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE +DEVELOPMENT may terminate this Agreement and revoke the granted license and +USB-IDs if you fail to comply with any of its terms and conditions. + +6.2 Survival of Terms. All provisions regarding secrecy, confidentiality +and limitation of liability shall survive termination of this agreement. + + +7 DISCLAIMER OF WARRANTY AND LIABILITY + +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 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. + +LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, +IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY +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 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 V-USB. + + +8 MISCELLANEOUS TERMS + +8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing +purposes that you entered into this agreement. + +8.2 Entire Agreement. This document represents the entire agreement between +OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by +an authorized representative of both, OBJECTIVE DEVELOPMENT and you. + +8.3 Severability. In case a provision of these terms and conditions should +be or become partly or entirely invalid, ineffective, or not executable, +the validity of all other provisions shall not be affected. + +8.4 Applicable Law. This agreement is governed by the laws of the Republic +of Austria. + +8.5 Responsible Courts. The responsible courts in Vienna/Austria will have +exclusive jurisdiction regarding all disputes in connection with this +agreement. + diff --git a/third_party/usbdrv/License.txt b/third_party/usbdrv/License.txt new file mode 100644 index 0000000..4460cfb --- /dev/null +++ b/third_party/usbdrv/License.txt @@ -0,0 +1,361 @@ +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/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 + - 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/vusb/ + +(3) If you improve the driver firmware itself, please give us a free license +to your modifications for our commercial license offerings. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/third_party/usbdrv/Readme.txt b/third_party/usbdrv/Readme.txt new file mode 100644 index 0000000..970dc66 --- /dev/null +++ b/third_party/usbdrv/Readme.txt @@ -0,0 +1,172 @@ +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/vusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +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 (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 +are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you +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 some VID/PID pairs for free. See the file +USB-IDs-for-free.txt for details. + +Objective Development also has some license offerings which include product +IDs. See http://www.obdev.at/vusb/ for details. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +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. + +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) 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 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/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 + - 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/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 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. + diff --git a/third_party/usbdrv/USB-ID-FAQ.txt b/third_party/usbdrv/USB-ID-FAQ.txt new file mode 100644 index 0000000..d1de8fb --- /dev/null +++ b/third_party/usbdrv/USB-ID-FAQ.txt @@ -0,0 +1,149 @@ +Version 2009-08-22 + +========================== +WHY DO WE NEED THESE IDs? +========================== + +USB is more than a low level protocol for data transport. It also defines a +common set of requests which must be understood by all devices. And as part +of these common requests, the specification defines data structures, the +USB Descriptors, which are used to describe the properties of the device. + +From the perspective of an operating system, it is therefore possible to find +out basic properties of a device (such as e.g. the manufacturer and the name +of the device) without a device-specific driver. This is essential because +the operating system can choose a driver to load based on this information +(Plug-And-Play). + +Among the most important properties in the Device Descriptor are the USB +Vendor- and Product-ID. Both are 16 bit integers. The most simple form of +driver matching is based on these IDs. The driver announces the Vendor- and +Product-IDs of the devices it can handle and the operating system loads the +appropriate driver when the device is connected. + +It is obvious that this technique only works if the pair Vendor- plus +Product-ID is unique: Only devices which require the same driver can have the +same pair of IDs. + + +===================================================== +HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? +===================================================== + +Since it is so important that USB IDs are unique, the USB Implementers Forum, +Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by +law to build a device and assign it any random numbers as IDs. Usb.org +therefore needs an agreement to regulate the use of USB IDs. The agreement +binds only parties who agreed to it, of course. Everybody else is free to use +any numbers for their IDs. + +So how can usb.org ensure that every manufacturer of USB devices enters into +an agreement with them? They do it via trademark licensing. Usb.org has +registered the trademark "USB", all associated logos and related terms. If +you want to put an USB logo on your product or claim that it is USB +compliant, you must license these trademarks from usb.org. And this is where +you enter into an agreement. See the "USB-IF Trademark License Agreement and +Usage Guidelines for the USB-IF Logo" at +http://www.usb.org/developers/logo_license/. + +Licensing the USB trademarks requires that you buy a USB Vendor-ID from +usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org +(yearly fee of ca. 4,000 USD) and that you meet all the technical +specifications from the USB spec. + +This means that most hobbyists and small companies will never be able to +become USB compliant, just because membership is so expensive. And you can't +be compliant with a driver based on V-USB anyway, because the AVR's port pins +don't meet the electrical specifications for USB. So, in principle, all +hobbyists and small companies are free to choose any random numbers for their +IDs. They have nothing to lose... + +There is one exception worth noting, though: If you use a sub-component which +implements USB, the vendor of the sub-components may guarantee USB +compliance. This might apply to some or all of FTDI's solutions. + + +======================================================================= +WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? +======================================================================= + +You have learned in the previous section that you are free to choose any +numbers for your IDs anyway. So why not do exactly this? There is still the +technical issue. If you choose IDs which are already in use by somebody else, +operating systems will load the wrong drivers and your device won't work. +Even if you choose IDs which are not currently in use, they may be in use in +the next version of the operating system or even after an automatic update. + +So what you need is a pair of Vendor- and Product-IDs for which you have the +guarantee that no USB compliant product uses them. This implies that no +operating system will ever ship with drivers responsible for these IDs. + + +============================================== +HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? +============================================== + +Objective Development gives away pairs of USB-IDs with their V-USB licenses. +In order to ensure that these IDs are unique, Objective Development has an +agreement with the company/person who has bought the USB Vendor-ID from +usb.org. This agreement ensures that a range of USB Product-IDs is reserved +for assignment by Objective Development and that the owner of the Vendor-ID +won't give it to anybody else. + +This means that you have to trust three parties to ensure uniqueness of +your IDs: + + - Objective Development, that they don't give the same PID to more than + one person. + - The owner of the Vendor-ID that they don't assign PIDs from the range + assigned to Objective Development to anybody else. + - Usb.org that they don't assign the same Vendor-ID a second time. + + +================================== +WHO IS THE OWNER OF THE VENDOR-ID? +================================== + +Objective Development has obtained ranges of USB Product-IDs under two +Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen +Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason +Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their +Vendor-ID directly from usb.org. + + +========================================================================= +CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? +========================================================================= + +The short answer is: Yes. All you get is a guarantee that the IDs are never +assigned to anybody else. What more do you need? + + +============================ +WHAT ABOUT SHARED ID PAIRS? +============================ + +Objective Development has reserved some PID/VID pairs for shared use. You +have no guarantee of uniqueness for them, except that no USB compliant device +uses them. In order to avoid technical problems, we must ensure that all +devices with the same pair of IDs use the same driver on kernel level. For +details, see the file USB-IDs-for-free.txt. + + +====================================================== +I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? +====================================================== + +A 16 bit integer number cannot be protected by copyright laws. It is not +sufficiently complex. And since none of the parties involved entered into the +USB-IF Trademark License Agreement, we are not bound by this agreement. So +there is no reason why it should be illegal to sub-license USB-IDs. + + +============================================= +WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? +============================================= + +Objective Development disclaims all liabilities which might arise from the +assignment of IDs. If you guarantee product features to your customers +without proper disclaimer, YOU are liable for that. diff --git a/third_party/usbdrv/USB-IDs-for-free.txt b/third_party/usbdrv/USB-IDs-for-free.txt new file mode 100644 index 0000000..d46517d --- /dev/null +++ b/third_party/usbdrv/USB-IDs-for-free.txt @@ -0,0 +1,154 @@ +Version 2009-08-22 + +=========================== +FREE USB-IDs FOR SHARED USE +=========================== + +Objective Development has reserved a set of USB Product-IDs for use according +to the guidelines outlined below. For more information about the concept of +USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees +that the IDs listed below are not used by any USB compliant devices. + + +==================== +MECHANISM OF SHARING +==================== + +From a technical point of view, two different devices can share the same USB +Vendor- and Product-ID if they require the same driver on operating system +level. We make use of this fact by assigning separate IDs for various device +classes. On application layer, devices must be distinguished by their textual +name or serial number. We offer separate sets of IDs for discrimination by +textual name and for serial number. + +Examples for shared use of USB IDs are included with V-USB in the "examples" +subdirectory. + + +====================================== +IDs FOR DISCRIMINATION BY TEXTUAL NAME +====================================== + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the manufacturer +and product identification. The manufacturer identification MUST be available +at least in USB language 0x0409 (English/US). + +(2) The textual manufacturer identification MUST contain either an Internet +domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail +address under your control (e.g. "myname@gmx.net"). You can embed the domain +name or e-mail address in any string you like, e.g. "Objective Development +http://www.obdev.at/vusb/". + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(4) You may choose any string for the textual product identification, as long +as this string is unique within the scope of your textual manufacturer +identification. + +(5) Application side device look-up MUST be based on the textual manufacturer +and product identification in addition to VID/PID matching. The driver +matching MUST be a comparison of the entire strings, NOT a sub-string match. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +Table if IDs for discrimination by textual name: + +PID dec (hex) | VID dec (hex) | Description of use +==============+===============+============================================ +1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb +--------------+---------------+-------------------------------------------- +1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +--------------+---------------+-------------------------------------------- +1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +--------------+---------------+-------------------------------------------- +1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices +--------------+---------------+-------------------------------------------- + +Note that Windows caches the textual product- and vendor-description for +mice, keyboards and joysticks. Name-bsed discrimination is therefore not +recommended for these device classes. + + +======================================= +IDs FOR DISCRIMINATION BY SERIAL NUMBER +======================================= + +If you use one of the IDs listed below, your device and host-side software +must conform to these rules: + +(1) The USB device MUST provide a textual representation of the serial +number, unless ONLY the operating system's default class driver is used. +The serial number string MUST be available at least in USB language 0x0409 +(English/US). + +(2) The serial number MUST start with either an Internet domain name (e.g. +"mycompany.com") registered and owned by you, or an e-mail address under your +control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. +You MAY append any string you like for further discrimination of your devices. + +(3) You are responsible for retaining ownership of the domain or e-mail +address for as long as any of your products are in use. + +(5) Application side device look-up MUST be based on the serial number string +in addition to VID/PID matching. The matching must start at the first +character of the serial number string and include the colon character +terminating your domain or e-mail address. It MAY stop anywhere after that. + +(6) For devices which implement a particular USB device class (e.g. HID), the +operating system's default class driver MUST be used. If an operating system +driver for Vendor Class devices is needed, this driver must be libusb or +libusb-win32 (see http://libusb.org/ and +http://libusb-win32.sourceforge.net/). + +(7) If ONLY the operating system's default class driver is used, e.g. for +mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an +application is needed, the serial number may be omitted. + + +Table if IDs for discrimination by serial number string: + +PID dec (hex) | VID dec (hex) | Description of use +===============+===============+=========================================== +10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb +---------------+---------------+------------------------------------------- +10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are + | | NOT mice, keyboards or joysticks) +---------------+---------------+------------------------------------------- +10202 (0x27da) | 5824 (0x16c0) | For USB Mice +---------------+---------------+------------------------------------------- +10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards +---------------+---------------+------------------------------------------- +10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks +---------------+---------------+------------------------------------------- +10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) +---------------+---------------+------------------------------------------- +10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices +---------------+---------------+------------------------------------------- + + +================= +ORIGIN OF USB-IDs +================= + +OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed +here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. +Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. +(see www.usb.org). The VID is registered for the company name "Van Ooijen +Technische Informatica". + + +========== +DISCLAIMER +========== + +OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. diff --git a/third_party/usbdrv/asmcommon.inc b/third_party/usbdrv/asmcommon.inc new file mode 100644 index 0000000..07d692b --- /dev/null +++ b/third_party/usbdrv/asmcommon.inc @@ -0,0 +1,188 @@ +/* Name: asmcommon.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-11-05 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file contains assembler code which is shared among the USB driver +implementations for different CPU cocks. Since the code must be inserted +in the middle of the module, it's split out into this file and #included. + +Jump destinations called from outside: + sofError: Called when no start sequence was found. + se0: Called when a package has been successfully received. + overflow: Called when receive buffer overflows. + doReturn: Called after sending data. + +Outside jump destinations used by this module: + waitForJ: Called to receive an already arriving packet. + sendAckAndReti: + sendNakAndReti: + sendCntAndReti: + usbSendAndReti: + +The following macros must be defined before this file is included: + .macro POP_STANDARD + .endm + .macro POP_RETI + .endm +*/ + +#define token x1 + +overflow: + ldi x2, 1< 0 + +#warning "Never compile production devices with debugging enabled" + +static void uartPutc(char c) +{ + while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ + ODDBG_UDR = c; +} + +static uchar hexAscii(uchar h) +{ + h &= 0xf; + if(h >= 10) + h += 'a' - (uchar)10 - '0'; + h += '0'; + return h; +} + +static void printHex(uchar c) +{ + uartPutc(hexAscii(c >> 4)); + uartPutc(hexAscii(c)); +} + +void odDebug(uchar prefix, uchar *data, uchar len) +{ + printHex(prefix); + uartPutc(':'); + while(len--){ + uartPutc(' '); + printHex(*data++); + } + uartPutc('\r'); + uartPutc('\n'); +} + +#endif diff --git a/third_party/usbdrv/oddebug.h b/third_party/usbdrv/oddebug.h new file mode 100644 index 0000000..c56af1c --- /dev/null +++ b/third_party/usbdrv/oddebug.h @@ -0,0 +1,123 @@ +/* Name: oddebug.h + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +#ifndef __oddebug_h_included__ +#define __oddebug_h_included__ + +/* +General Description: +This module implements a function for debug logs on the serial line of the +AVR microcontroller. Debugging can be configured with the define +'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging +calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is +2, DBG1 and DBG2 logs will be printed. + +A debug log consists of a label ('prefix') to indicate which debug log created +the output and a memory block to dump in hex ('data' and 'len'). +*/ + + +#ifndef F_CPU +# define F_CPU 12000000 /* 12 MHz */ +#endif + +/* make sure we have the UART defines: */ +#include "usbportability.h" + +#ifndef uchar +# define uchar unsigned char +#endif + +#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ +# warning "Debugging disabled because device has no UART" +# undef DEBUG_LEVEL +#endif + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 0 +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +# define DBG1(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG1(prefix, data, len) +#endif + +#if DEBUG_LEVEL > 1 +# define DBG2(prefix, data, len) odDebug(prefix, data, len) +#else +# define DBG2(prefix, data, len) +#endif + +/* ------------------------------------------------------------------------- */ + +#if DEBUG_LEVEL > 0 +extern void odDebug(uchar prefix, uchar *data, uchar len); + +/* Try to find our control registers; ATMEL likes to rename these */ + +#if defined UBRR +# define ODDBG_UBRR UBRR +#elif defined UBRRL +# define ODDBG_UBRR UBRRL +#elif defined UBRR0 +# define ODDBG_UBRR UBRR0 +#elif defined UBRR0L +# define ODDBG_UBRR UBRR0L +#endif + +#if defined UCR +# define ODDBG_UCR UCR +#elif defined UCSRB +# define ODDBG_UCR UCSRB +#elif defined UCSR0B +# define ODDBG_UCR UCSR0B +#endif + +#if defined TXEN +# define ODDBG_TXEN TXEN +#else +# define ODDBG_TXEN TXEN0 +#endif + +#if defined USR +# define ODDBG_USR USR +#elif defined UCSRA +# define ODDBG_USR UCSRA +#elif defined UCSR0A +# define ODDBG_USR UCSR0A +#endif + +#if defined UDRE +# define ODDBG_UDRE UDRE +#else +# define ODDBG_UDRE UDRE0 +#endif + +#if defined UDR +# define ODDBG_UDR UDR +#elif defined UDR0 +# define ODDBG_UDR UDR0 +#endif + +static inline void odDebugInit(void) +{ + ODDBG_UCR |= (1<len & 0x10){ /* packet buffer was empty */ + txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ + }else{ + txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ + } + p = txStatus->buffer + 1; + i = len; + do{ /* if len == 0, we still copy 1 byte, but that's no problem */ + *p++ = *data++; + }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 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) +{ + usbGenericSetInterrupt(data, len, &usbTxStatus3); +} +#endif +#endif /* USB_CFG_SUPPRESS_INTR_CODE */ + +/* ------------------ utilities for code following below ------------------- */ + +/* 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 + +#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 = 0; \ + if((cfgProp) & USB_PROP_IS_DYNAMIC){ \ + len = usbFunctionDescriptor(rq); \ + }else{ \ + len = USB_PROP_LENGTH(cfgProp); \ + usbMsgPtr = (uchar *)(staticName); \ + } \ + } + +/* 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; +} + +/* ------------------------------------------------------------------------- */ + +/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for + * standard requests instead of class and custom requests. + */ +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 = 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; + +/* usbRxToken can be: + * 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 & 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 < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ + usbFunctionWriteOut(data, len); + return; + } +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + 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{ + replyLen = rq->wLength.word; + } + } + 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(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; + } + 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_USER_RW){ + uchar rval = usbFunctionWrite(data, len); + if(rval == 0xff){ /* an error occurred */ + usbTxLen = USBPID_STALL; + }else if(rval != 0){ /* This was the final package */ + usbMsgLen = 0; /* answer with a zero-sized data packet */ + } + } +#endif + } +} + +/* ------------------------------------------------------------------------- */ + +/* 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) +{ + 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, *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++ = *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; + usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ + len = usbDeviceRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(&usbTxBuf[1], len); + len += 4; /* length including sync byte */ + if(len < 12) /* a partial package identifies end of message */ + usbMsgLen = USB_NO_MSG; + }else{ + len = USBPID_STALL; /* stall the endpoint */ + usbMsgLen = USB_NO_MSG; + } + usbTxLen = len; + DBG2(0x20, usbTxBuf, len-1); +} + +/* ------------------------------------------------------------------------- */ + +static inline void usbHandleResetHook(uchar notResetState) +{ +#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 +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbPoll(void) +{ +schar len; +uchar i; + + 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, + * retries must be handled on application level. + * unsigned crc = usbCrc16(buffer + 1, usbRxLen - 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; +#else + usbRxLen = 0; /* mark rx buffer as available */ +#endif + } + if(usbTxLen & 0x10){ /* transmit system idle */ + if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ + usbBuildTxBlock(); + } + } + for(i = 20; i > 0; i--){ + uchar usbLineStatus = USBIN & USBMASK; + if(usbLineStatus != 0) /* SE0 has ended */ + goto isNotReset; + } + /* RESET condition, called multiple times during reset */ + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; + usbResetStall(); + DBG1(0xff, 0, 0); +isNotReset: + usbHandleResetHook(i); +} + +/* ------------------------------------------------------------------------- */ + +USB_PUBLIC void usbInit(void) +{ +#if USB_INTR_CFG_SET != 0 + USB_INTR_CFG |= USB_INTR_CFG_SET; +#endif +#if USB_INTR_CFG_CLR != 0 + USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); +#endif + USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); + 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 +} + +/* ------------------------------------------------------------------------- */ diff --git a/third_party/usbdrv/usbdrv.h b/third_party/usbdrv/usbdrv.h new file mode 100644 index 0000000..a478c6f --- /dev/null +++ b/third_party/usbdrv/usbdrv.h @@ -0,0 +1,736 @@ +/* Name: usbdrv.h + * 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), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "usbportability.h" + +/* +Hardware Prerequisites: +======================= +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 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 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: +============ +Robustness with respect to communication errors: +The driver assumes error-free communication. It DOES check for errors in +the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, +token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due +to timing constraints: We must start sending a reply within 7 bit times. +Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU +performance does not permit that. The driver does not check Data0/Data1 +toggling, but application software can implement the check. + +Input characteristics: +Since no differential receiver circuit is used, electrical interference +robustness may suffer. The driver samples only one of the data lines with +an ordinary I/O pin's input characteristics. However, since this is only a +low speed USB implementation and the specification allows for 8 times the +bit rate over the same hardware, we should be on the safe side. Even the spec +requires detection of asymmetric states at high bit rate for SE0 detection. + +Number of endpoints: +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 +'usbFunctionWrite()'. + +USB Suspend Mode supply current: +The USB standard limits power consumption to 500uA when the bus is in suspend +mode. This is not a problem for self-powered devices since they don't need +bus power anyway. Bus-powered devices can achieve this only by putting the +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 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) +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 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 @ 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. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20120109 +/* 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 + * distinguish versions. If it is not defined, the driver's release date is + * 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 +#ifndef schar +#define schar signed char +#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" */ + +struct usbRequest; /* forward declaration */ + +USB_PUBLIC void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * 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). + */ +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. + * Please note that debug outputs through the UART take ~ 0.5ms per byte + * at 19200 bps. + */ +extern uchar *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. + */ +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 + * the host communicates the parameters of the following (optional) data + * transfer. The SETUP data is available in the 'data' parameter which can + * (and should) be casted to 'usbRequest_t *' for a more user-friendly access + * to parameters. + * + * If the SETUP indicates a control-in transfer, you should provide the + * 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 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 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. + */ +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 + * data. See the documentation of usbFunctionSetup() above for more info. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +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 + * interrupt status to the host. + * If you need to transfer more bytes, use a control read after the interrupt. + */ +#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 + * message already buffered will be lost. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT3 +USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); +#define usbInterruptIsReady3() (usbTxLen3 & 0x10) +/* Same as above for endpoint 3 */ +#endif +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ +#define usbHidReportDescriptor usbDescriptorHidReport +/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ +/* If you implement an HID device, you need to provide a report descriptor. + * The HID report descriptor syntax is a bit complex. If you understand how + * report descriptors are constructed, we recommend that you use the HID + * Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/. + * Otherwise you should probably start with a working example. + */ +#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ +#if USB_CFG_IMPLEMENT_FN_WRITE +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 + * the 'length' property in the setup data. If an error occurred during + * processing, return 0xff (== -1). The driver will answer the entire transfer + * with a STALL token in this case. If you have received the entire payload + * successfully, return 1. If you expect more data, return 0. If you don't + * know whether the host will send more data (you should know, the total is + * provided in the usbFunctionSetup() call!), return 1. + * NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called + * for the remaining data. You must continue to return 0xff for STALL in these + * calls. + * In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE + * to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. + */ +#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ +#if USB_CFG_IMPLEMENT_FN_READ +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 + * return the actual number of bytes copied. If you return less than requested, + * the control-in transfer is terminated. If you return 0xff, the driver aborts + * the transfer with a STALL token. + * In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ + * 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 +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<device, 1=device->host + * t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved + * r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other + */ + +/* USB setup recipient values */ +#define USBRQ_RCPT_MASK 0x1f +#define USBRQ_RCPT_DEVICE 0 +#define USBRQ_RCPT_INTERFACE 1 +#define USBRQ_RCPT_ENDPOINT 2 + +/* USB request type values */ +#define USBRQ_TYPE_MASK 0x60 +#define USBRQ_TYPE_STANDARD (0<<5) +#define USBRQ_TYPE_CLASS (1<<5) +#define USBRQ_TYPE_VENDOR (2<<5) + +/* USB direction values: */ +#define USBRQ_DIR_MASK 0x80 +#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) +#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) + +/* USB Standard Requests */ +#define USBRQ_GET_STATUS 0 +#define USBRQ_CLEAR_FEATURE 1 +#define USBRQ_SET_FEATURE 3 +#define USBRQ_SET_ADDRESS 5 +#define USBRQ_GET_DESCRIPTOR 6 +#define USBRQ_SET_DESCRIPTOR 7 +#define USBRQ_GET_CONFIGURATION 8 +#define USBRQ_SET_CONFIGURATION 9 +#define USBRQ_GET_INTERFACE 10 +#define USBRQ_SET_INTERFACE 11 +#define USBRQ_SYNCH_FRAME 12 + +/* USB descriptor constants */ +#define USBDESCR_DEVICE 1 +#define USBDESCR_CONFIG 2 +#define USBDESCR_STRING 3 +#define USBDESCR_INTERFACE 4 +#define USBDESCR_ENDPOINT 5 +#define USBDESCR_HID 0x21 +#define USBDESCR_HID_REPORT 0x22 +#define USBDESCR_HID_PHYS 0x23 + +//#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 + +/* USB HID Requests */ +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_GET_IDLE 0x02 +#define USBRQ_HID_GET_PROTOCOL 0x03 +#define USBRQ_HID_SET_REPORT 0x09 +#define USBRQ_HID_SET_IDLE 0x0a +#define USBRQ_HID_SET_PROTOCOL 0x0b + +/* ------------------------------------------------------------------------- */ + +#endif /* __usbdrv_h_included__ */ diff --git a/third_party/usbdrv/usbdrvasm.S b/third_party/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..4e6158c --- /dev/null +++ b/third_party/usbdrv/usbdrvasm.S @@ -0,0 +1,393 @@ +/* Name: usbdrvasm.S + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2007-06-13 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id$ + */ + +/* +General Description: +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. +*/ + +#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 +#define shift r18 +#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__ + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset + extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen + extern usbTxBuf, usbTxStatus1, usbTxStatus3 +# if USB_COUNT_SOF + extern usbSofCount +# endif + public usbCrc16 + public usbCrc16Append + + COMMON INTVEC +# 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__ */ + +# 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 USB_INTR_VECTOR + .type USB_INTR_VECTOR, @function + .global usbCrc16 + .global usbCrc16Append +#endif /* __IAR_SYSTEMS_ASM__ */ + + +#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 + +#define usbTxLen1 usbTxStatus1 +#define usbTxBuf1 (usbTxStatus1 + 1) +#define usbTxLen3 usbTxStatus3 +#define usbTxBuf3 (usbTxStatus3 + 1) + + +;---------------------------------------------------------------------------- +; Utility functions +;---------------------------------------------------------------------------- + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbCrc16 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". + */ +RTMODEL "__rt_version", "3" +/* The line above will generate an error if cc calling conventions change. + * The value "3" above is valid for IAR 4.10B/W32 + */ +# define argLen r18 /* argument 2 */ +# define argPtrL r16 /* argument 1 */ +# define argPtrH r17 /* argument 1 */ + +# define resCrcL r16 /* result */ +# define resCrcH r17 /* result */ + +# define ptrL ZL +# define ptrH ZH +# define ptr Z +# define byte r22 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbCrc16 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 argLen r22 /* argument 2 */ +# define argPtrL r24 /* argument 1 */ +# define argPtrH r25 /* argument 1 */ + +# define resCrcL r24 /* result */ +# define resCrcH r25 /* result */ + +# define ptrL XL +# define ptrH XH +# define ptr x +# define byte r18 +# define bitCnt r19 +# define polyL r20 +# define polyH r21 +# define scratch r23 + +#endif + +#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: +; 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 + 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) + 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+ + eor resCrcL, byte +usbCrcBitLoop: + ror resCrcH ; carry is always set here (see brcs jumps to here) + ror resCrcL + brcs usbCrcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +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 + 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 */ diff --git a/third_party/usbdrv/usbdrvasm.asm b/third_party/usbdrv/usbdrvasm.asm new file mode 100644 index 0000000..9cc4e4d --- /dev/null +++ b/third_party/usbdrv/usbdrvasm.asm @@ -0,0 +1,21 @@ +/* Name: usbdrvasm.asm + * 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), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +/* +General Description: +The IAR compiler/assembler system prefers assembler files with file extension +".asm". We simply provide this file as an alias for usbdrvasm.S. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#include "usbdrvasm.S" + +end diff --git a/third_party/usbdrv/usbdrvasm12.inc b/third_party/usbdrv/usbdrvasm12.inc new file mode 100644 index 0000000..cb74ceb --- /dev/null +++ b/third_party/usbdrv/usbdrvasm12.inc @@ -0,0 +1,393 @@ +/* Name: usbdrvasm12.inc + * 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) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! + + +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 +*/ + +;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. +USB_INTR_VECTOR: +;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 +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: +;{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 ;2 [11] undo the push from before + rjmp waitForK ;2 [13] 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] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [59] + brcc doExorN1 ;[-4] [60] + subi x4, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: ;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...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-12] 12 cycles until SOP + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-8] <--- acquire bus + in x1, USBOUT ;[-7] port mirror for tx loop + ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-5] + push x4 ;[-4] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x4, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x4, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x4, 6 ;[05] [13] +commonN2: + nop ;[06] [14] + subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[08] [16] <--- set bit + brcs txBitloop ;[09] [25] [41] + +stuff6Delay: + ror shift ;[42] [50] + brcc doExor6 ;[43] + subi x4, 1 ;[44] + brne common6 ;[45] + lsl shift ;[46] compensate ror after rjmp stuffDelay + nop ;[47] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[48] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[45] [53] + ldi x4, 6 ;[46] +common6: +stuff7Delay: + ror shift ;[47] [55] + out USBOUT, x1 ;[48] <--- set bit + brcc doExor7 ;[49] + subi x4, 1 ;[50] + brne common7 ;[51] + lsl shift ;[52] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[53] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[51] [59] + ldi x4, 6 ;[52] +common7: + ld shift, y+ ;[53] + tst cnt ;[55] + out USBOUT, x1 ;[56] <--- set bit + brne txByteLoop ;[57] + +;make SE0: + cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[59] + lsl x2 ;[61] we compare with left shifted address + subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[63] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12.5625 MHz +max frequency: 69.286 cycles for 8 bit -> 12.99 MHz +nominal frequency: 12.77 MHz ( = sqrt(min * max)) + +sampling positions: (next even number in range [+/- 0.5]) +cycle index range: 0 ... 66 +bits: +.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 +[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] + +bit number: 0 1 2 3 4 5 6 7 +spare cycles 1 2 1 2 1 1 1 0 + +operations to perform: duration cycle + ---------------- + eor fix, shift 1 -> 00 + andi phase, USBMASK 1 -> 08 + breq se0 1 -> 16 (moved to 11) + st y+, data 2 -> 24, 25 + mov data, fix 1 -> 33 + ser data 1 -> 41 + subi cnt, 1 1 -> 49 + brcs overflow 1 -> 50 + +layout of samples and operations: +[##] = sample bit +<##> = sample phase +*##* = operation + +0: *00* [01] 02 03 04 <05> 06 07 +1: *08* [09] 10 11 12 <13> 14 15 *16* +2: [17] 18 19 20 <21> 22 23 +3: *24* *25* [26] 27 28 29 <30> 31 32 +4: *33* [34] 35 36 37 <38> 39 40 +5: *41* [42] 43 44 45 <46> 47 48 +6: *49* *50* [51] 52 53 54 <55> 56 57 58 +7: [59] 60 61 62 <63> 64 65 66 +*****************************************************************************/ + +/* we prefer positive expressions (do if condition) instead of negative + * (skip if condition), therefore use defines for skip instructions: + */ +#define ifioclr sbis +#define ifioset sbic +#define ifrclr sbrs +#define ifrset sbrc + +/* The registers "fix" and "data" swap their meaning during the loop. Use + * defines to keep their name constant. + */ +#define fix x2 +#define data x1 +#undef phase /* phase has a default definition to x4 */ +#define phase x3 + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS ;[0] + rjmp foundK ;[1] +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError + +foundK: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push YH ;[2] + lds YL, usbInputBufOffset;[4] + clr YH ;[6] + subi YL, lo8(-(usbRxBuf));[7] + sbci YH, hi8(-(usbRxBuf));[8] + + sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] + rjmp haveTwoBitsK ;[10] + pop YH ;[11] undo the push from before + rjmp waitForK ;[13] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +#define fix x2 +#define data x1 + + push shift ;[12] + push x1 ;[14] + push x2 ;[16] + ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 + ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] + ori shift, 1<<0 ;[02] + push x3 ;[03] + push cnt ;[05] + push r0 ;[07] + ifioset USBIN, USBMINUS ;[09] <--- bit 1 + ori shift, 1<<1 ;[10] + ser fix ;[11] + ldi cnt, USB_BUFSIZE ;[12] + mov data, shift ;[13] + lsl shift ;[14] + nop2 ;[15] + ifioset USBIN, USBMINUS ;[17] <--- bit 2 + ori data, 3<<2 ;[18] store in bit 2 AND bit 3 + eor shift, data ;[19] do nrzi decoding + andi data, 1<<3 ;[20] + in phase, USBIN ;[21] <- phase + brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 + nop ;[23] + rjmp entryAfterClr ;[24] +jumpToEntryAfterSet: + rjmp entryAfterSet ;[24] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +#undef fix +#define fix x1 +#undef data +#define data x2 + +bit7IsSet: + ifrclr phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump +bit0AfterSet: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioclr USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsClr ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0s ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterSet ;[06] +unstuff0s: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsClr ;[02] executed if first expr false or second true +se0AndStore: ; executed only if both bits 0 + st y+, x1 ;[15/17] cycles after start of byte + rjmp se0 ;[17/19] + +bit0IsClr: + ifrset phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterClr: + andi phase, USBMASK ;[08] + ifioset USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsSet ;[10] + breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 + andi shift, ~(7 << 1) ;[12] + in phase, USBIN ;[13] <- phase + breq unstuff1c ;[14] + rjmp bit2AfterClr ;[15] +unstuff1c: + andi fix, ~(1 << 1) ;[16] + nop2 ;[08] + nop2 ;[10] +bit1IsSet: + ifrclr phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterSet: + ifioclr USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsClr ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2s ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterSet ;[22] +unstuff2s: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsClr: + ifrset phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterClr: + st y+, data ;[24] +entryAfterClr: + ifioset USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsSet ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3c ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterClr ;[31] +unstuff3c: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsSet: + ifrclr phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterSet: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioclr USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsClr ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4s ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterSet ;[39] +unstuff4s: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsClr: + ifrset phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterClr: + ser data ;[41] + ifioset USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsSet ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5c ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterClr ;[47] +unstuff5c: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsSet: + ifrclr phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterSet: + subi cnt, 1 ;[49] + brcs jumpToOverflow ;[50] + ifioclr USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsClr ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6s ;[56] + rjmp bit7AfterSet ;[57] + +jumpToOverflow: + rjmp overflow + +unstuff6s: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsClr: + ifrset phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] + nop ;[58] +bit7AfterClr: + ifioset USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsSet ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7c ;[64] + rjmp bit0AfterClr ;[65] -> [00] == [67] +unstuff7c: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsSet ;[60] + +bit7IsClr: + ifrset phase, USBMINUS ;[62] check phase only if D- changed + lpm ;[63] + in phase, USBIN ;[64] <- phase (one cycle too late) + ori shift, 1 << 7 ;[65] + nop ;[66] +;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump +bit0AfterClr: + eor fix, shift ;[00] +#undef fix +#define fix x2 +#undef data +#define data x1 /* we now have result in data, fix is reset to 0xff */ + ifioset USBIN, USBMINUS ;[01] <--- sample 0 + rjmp bit0IsSet ;[02] + andi shift, ~(7 << 0) ;[03] + breq unstuff0c ;[04] + in phase, USBIN ;[05] <- phase + rjmp bit1AfterClr ;[06] +unstuff0c: + in phase, USBIN ;[06] <- phase (one cycle too late) + andi fix, ~(1 << 0) ;[07] + ifioclr USBIN, USBMINUS ;[00] + ifioset USBIN, USBPLUS ;[01] + rjmp bit0IsSet ;[02] executed if first expr false or second true + rjmp se0AndStore ;[03] executed only if both bits 0 +bit0IsSet: + ifrclr phase, USBMINUS ;[04] check phase only if D- changed + lpm ;[05] + in phase, USBIN ;[06] <- phase (one cycle too late) + ori shift, 1 << 0 ;[07] +bit1AfterSet: + andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr + ifioclr USBIN, USBMINUS ;[09] <--- sample 1 + rjmp bit1IsClr ;[10] + breq unstuff1s ;[11] + nop2 ;[12] do not check for SE0 if bit 0 was 1 + in phase, USBIN ;[14] <- phase (one cycle too late) + rjmp bit2AfterSet ;[15] +unstuff1s: + in phase, USBIN ;[13] <- phase + andi fix, ~(1 << 1) ;[14] + lpm ;[07] + nop2 ;[10] +bit1IsClr: + ifrset phase, USBMINUS ;[12] check phase only if D- changed + lpm ;[13] + in phase, USBIN ;[14] <- phase (one cycle too late) + ori shift, 1 << 1 ;[15] + nop ;[16] +bit2AfterClr: + ifioset USBIN, USBMINUS ;[17] <--- sample 2 + rjmp bit2IsSet ;[18] + andi shift, ~(7 << 2) ;[19] + breq unstuff2c ;[20] + in phase, USBIN ;[21] <- phase + rjmp bit3AfterClr ;[22] +unstuff2c: + in phase, USBIN ;[22] <- phase (one cycle too late) + andi fix, ~(1 << 2) ;[23] + nop2 ;[16] + nop2 ;[18] +bit2IsSet: + ifrclr phase, USBMINUS ;[20] check phase only if D- changed + lpm ;[21] + in phase, USBIN ;[22] <- phase (one cycle too late) + ori shift, 1 << 2 ;[23] +bit3AfterSet: + st y+, data ;[24] +entryAfterSet: + ifioclr USBIN, USBMINUS ;[26] <--- sample 3 + rjmp bit3IsClr ;[27] + andi shift, ~(7 << 3) ;[28] + breq unstuff3s ;[29] + in phase, USBIN ;[30] <- phase + rjmp bit4AfterSet ;[31] +unstuff3s: + in phase, USBIN ;[31] <- phase (one cycle too late) + andi fix, ~(1 << 3) ;[32] + nop2 ;[25] + nop2 ;[27] +bit3IsClr: + ifrset phase, USBMINUS ;[29] check phase only if D- changed + lpm ;[30] + in phase, USBIN ;[31] <- phase (one cycle too late) + ori shift, 1 << 3 ;[32] +bit4AfterClr: + mov data, fix ;[33] undo this move by swapping defines +#undef fix +#define fix x1 +#undef data +#define data x2 + ifioset USBIN, USBMINUS ;[34] <--- sample 4 + rjmp bit4IsSet ;[35] + andi shift, ~(7 << 4) ;[36] + breq unstuff4c ;[37] + in phase, USBIN ;[38] <- phase + rjmp bit5AfterClr ;[39] +unstuff4c: + in phase, USBIN ;[39] <- phase (one cycle too late) + andi fix, ~(1 << 4) ;[40] + nop2 ;[33] + nop2 ;[35] +bit4IsSet: + ifrclr phase, USBMINUS ;[37] check phase only if D- changed + lpm ;[38] + in phase, USBIN ;[39] <- phase (one cycle too late) + ori shift, 1 << 4 ;[40] +bit5AfterSet: + ser data ;[41] + ifioclr USBIN, USBMINUS ;[42] <--- sample 5 + rjmp bit5IsClr ;[43] + andi shift, ~(7 << 5) ;[44] + breq unstuff5s ;[45] + in phase, USBIN ;[46] <- phase + rjmp bit6AfterSet ;[47] +unstuff5s: + in phase, USBIN ;[47] <- phase (one cycle too late) + andi fix, ~(1 << 5) ;[48] + nop2 ;[41] + nop2 ;[43] +bit5IsClr: + ifrset phase, USBMINUS ;[45] check phase only if D- changed + lpm ;[46] + in phase, USBIN ;[47] <- phase (one cycle too late) + ori shift, 1 << 5 ;[48] +bit6AfterClr: + subi cnt, 1 ;[49] + brcs overflow ;[50] + ifioset USBIN, USBMINUS ;[51] <--- sample 6 + rjmp bit6IsSet ;[52] + andi shift, ~(3 << 6) ;[53] + cpi shift, 2 ;[54] + in phase, USBIN ;[55] <- phase + brlt unstuff6c ;[56] + rjmp bit7AfterClr ;[57] +unstuff6c: + andi fix, ~(1 << 6) ;[50] + lpm ;[51] +bit6IsSet: + ifrclr phase, USBMINUS ;[54] check phase only if D- changed + lpm ;[55] + in phase, USBIN ;[56] <- phase (one cycle too late) + ori shift, 1 << 6 ;[57] +bit7AfterSet: + ifioclr USBIN, USBMINUS ;[59] <--- sample 7 + rjmp bit7IsClr ;[60] + andi shift, ~(1 << 7) ;[61] + cpi shift, 4 ;[62] + in phase, USBIN ;[63] <- phase + brlt unstuff7s ;[64] + rjmp bit0AfterSet ;[65] -> [00] == [67] +unstuff7s: + andi fix, ~(1 << 7) ;[58] + nop ;[59] + rjmp bit7IsClr ;[60] + +macro POP_STANDARD ; 14 cycles + pop r0 + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [63] + brcc doExorN1 ;[-4] [64] + subi x3, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-10] 10 cycles until SOP + ori x2, USBMASK ;[-9] + sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) + out USBDDR, x2 ;[-6] <--- acquire bus + in x1, USBOUT ;[-5] port mirror for tx loop + ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-3] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x3, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x3, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x3, 6 ;[05] [13] +commonN2: + nop2 ;[06] [14] + subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[09] [17] <--- set bit + brcs txBitloop ;[10] [27] [44] + +stuff6Delay: + ror shift ;[45] [53] + brcc doExor6 ;[46] + subi x3, 1 ;[47] + brne common6 ;[48] + lsl shift ;[49] compensate ror after rjmp stuffDelay + nop ;[50] stuffing consists of just waiting 8 cycles + rjmp stuff6Delay ;[51] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[48] [56] + ldi x3, 6 ;[49] +common6: +stuff7Delay: + ror shift ;[50] [58] + out USBOUT, x1 ;[51] <--- set bit + brcc doExor7 ;[52] + subi x3, 1 ;[53] + brne common7 ;[54] + lsl shift ;[55] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[56] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[54] [62] + ldi x3, 6 ;[55] +common7: + ld shift, y+ ;[56] + nop ;[58] + tst cnt ;[59] + out USBOUT, x1 ;[60] [00]<--- set bit + brne txByteLoop ;[61] [01] +;make SE0: + cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[03] + lsl x2 ;[05] we compare with left shifted address + subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[07] + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[01] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 0) + echo "$s\n"; + } +} + +function printBit($isAfterSet, $bitNum) +{ + ob_start(); + if($isAfterSet){ +?> + ifioclr USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsClr ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#s ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterSet ;[05] +unstuff#s: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsClr: + ifrset phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + + ifioset USBIN, USBMINUS ;[00] <--- sample + rjmp bit#IsSet ;[01] + andi shift, ~(7 << #) ;[02] + breq unstuff#c ;[03] + in phase, USBIN ;[04] <- phase + rjmp bit@AfterClr ;[05] +unstuff#c: + in phase, USBIN ;[05] <- phase (one cycle too late) + andi fix, ~(1 << #) ;[06] + nop2 ;[-1] + nop2 ;[01] +bit#IsSet: + ifrclr phase, USBMINUS ;[03] check phase only if D- changed + lpm ;[04] + in phase, USBIN ;[05] <- phase (one cycle too late) + ori shift, 1 << # ;[06] + +*****************************************************************************/ diff --git a/third_party/usbdrv/usbdrvasm15.inc b/third_party/usbdrv/usbdrvasm15.inc new file mode 100644 index 0000000..ce10391 --- /dev/null +++ b/third_party/usbdrv/usbdrvasm15.inc @@ -0,0 +1,423 @@ +/* Name: usbdrvasm15.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: contributed by V. Bosch + * Creation Date: 2007-08-06 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 15 MHz version of the asssembler part of the USB driver. It +requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +;---------------------------------------------------------------------------- +; order of registers pushed: +; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 +;---------------------------------------------------------------------------- +USB_INTR_VECTOR: + push YL ;2 push only what is necessary to sync with edge ASAP + in YL, SREG ;1 + push YL ;2 +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +; +; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +; sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +;------------------------------------------------------------------------------- +; The following code results in a sampling window of < 1/4 bit +; which meets the spec. +;------------------------------------------------------------------------------- +waitForK: ;- + sbis USBIN, USBMINUS ;1 [00] <-- sample + rjmp foundK ;2 [01] + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK + sbis USBIN, USBMINUS ; <-- sample + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +;------------------------------------------------------------------------------ +; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 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 +;------------------------------------------------------------------------------ +foundK: ;- [02] + lds YL, usbInputBufOffset;2 [03+04] tx loop + push YH ;2 [05+06] + clr YH ;1 [07] + subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] + sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] + push shift ;2 [10+11] + ser shift ;1 [12] + sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) + rjmp haveTwoBitsK ;2 [00] [14] + pop shift ;2 [15+16] undo the push from before + pop YH ;2 [17+18] undo the push from before + rjmp waitForK ;2 [19+20] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 20 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;- [01] + push x1 ;2 [02+03] + push x2 ;2 [04+05] + push x3 ;2 [06+07] + push bitcnt ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + bst x1, USBMINUS ;1 [01] + bld shift, 0 ;1 [02] + push cnt ;2 [03+04] + ldi cnt, USB_BUFSIZE ;1 [05] + push x4 ;2 [06+07] tx loop + rjmp rxLoop ;2 [08] +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +unstuff0: ;- [07] (branch taken) + andi x3, ~0x01 ;1 [08] + mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [00] [10] <-- sample bit 1 again + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 1 + ori shift, 0x01 ;1 [03] 0b00000001 + nop ;1 [04] + rjmp didUnstuff0 ;2 [05] +;----------------------------------------------------- +unstuff1: ;- [05] (branch taken) + mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [07] + ori shift, 0x02 ;1 [08] 0b00000010 + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 again + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + rjmp didUnstuff1 ;2 [03] +;----------------------------------------------------- +unstuff2: ;- [05] (branch taken) + andi x3, ~0x04 ;1 [06] + ori shift, 0x04 ;1 [07] 0b00000100 + mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit + nop ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + rjmp didUnstuff2 ;2 [03] +;----------------------------------------------------- +unstuff3: ;- [00] [10] (branch taken) + in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late + andi x2, USBMASK ;1 [02] + breq se0Hop ;1 [03] SE0 check for stuffed bit 3 + andi x3, ~0x08 ;1 [04] + ori shift, 0x08 ;1 [05] 0b00001000 + rjmp didUnstuff3 ;2 [06] +;---------------------------------------------------------------------------- +; extra jobs done during bit interval: +; +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], +; overflow check, jump to the head of rxLoop +; bit 1: SE0 check +; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] +; bit 4: SE0 check, none +; bit 5: SE0 check, none +; bit 6: SE0 check, none +; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others +;---------------------------------------------------------------------------- +rxLoop: ;- [09] + in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [01] + brne SkipSe0Hop ;1 [02] +se0Hop: ;- [02] + rjmp se0 ;2 [03] SE0 check for bit 1 +SkipSe0Hop: ;- [03] + ser x3 ;1 [04] + andi shift, 0xf9 ;1 [05] 0b11111001 + breq unstuff0 ;1 [06] +didUnstuff0: ;- [06] + eor x1, x2 ;1 [07] + bst x1, USBMINUS ;1 [08] + bld shift, 1 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 2 + andi shift, 0xf3 ;1 [03] 0b11110011 + breq unstuff1 ;1 [04] do remaining work for bit 1 +didUnstuff1: ;- [04] + eor x2, x1 ;1 [05] + bst x2, USBMINUS ;1 [06] + bld shift, 2 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) + andi x2, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 3 + andi shift, 0xe7 ;1 [03] 0b11100111 + breq unstuff2 ;1 [04] +didUnstuff2: ;- [04] + eor x1, x2 ;1 [05] + bst x1, USBMINUS ;1 [06] + bld shift, 3 ;1 [07] +didUnstuff3: ;- [07] + andi shift, 0xcf ;1 [08] 0b11001111 + breq unstuff3 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 4 + andi x1, USBMASK ;1 [01] + breq se0Hop ;1 [02] SE0 check for bit 4 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 4 ;1 [05] +didUnstuff4: ;- [05] + andi shift, 0x9f ;1 [06] 0b10011111 + breq unstuff4 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 5 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 5 ;1 [05] +didUnstuff5: ;- [05] + andi shift, 0x3f ;1 [06] 0b00111111 + breq unstuff5 ;1 [07] + nop2 ;2 [08+09] + in x1, USBIN ;1 [00] [10] <-- sample bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 6 + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 6 ;1 [05] +didUnstuff6: ;- [05] + cpi shift, 0x02 ;1 [06] 0b00000010 + brlo unstuff6 ;1 [07] + nop2 ;2 [08+09] + in x2, USBIN ;1 [00] [10] <-- sample bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for bit 7 + eor x1, x2 ;1 [03] + bst x1, USBMINUS ;1 [04] + bld shift, 7 ;1 [05] +didUnstuff7: ;- [05] + cpi shift, 0x04 ;1 [06] 0b00000100 + brlo unstuff7 ;1 [07] + eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others + nop ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample bit 0 + st y+, x3 ;2 [01+02] store data + eor x2, x1 ;1 [03] + bst x2, USBMINUS ;1 [04] + bld shift, 0 ;1 [05] + subi cnt, 1 ;1 [06] + brcs overflow ;1 [07] + rjmp rxLoop ;2 [08] +;----------------------------------------------------- +unstuff4: ;- [08] + andi x3, ~0x10 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 4 + ori shift, 0x10 ;1 [03] + rjmp didUnstuff4 ;2 [04] +;----------------------------------------------------- +unstuff5: ;- [08] + ori shift, 0x20 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 5 + andi x3, ~0x20 ;1 [03] + rjmp didUnstuff5 ;2 [04] +;----------------------------------------------------- +unstuff6: ;- [08] + andi x3, ~0x40 ;1 [09] + in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 + andi x1, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 6 + ori shift, 0x40 ;1 [03] + rjmp didUnstuff6 ;2 [04] +;----------------------------------------------------- +unstuff7: ;- [08] + andi x3, ~0x80 ;1 [09] + in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 + andi x2, USBMASK ;1 [01] + breq se0 ;1 [02] SE0 check for stuffed bit 7 + ori shift, 0x80 ;1 [03] + rjmp didUnstuff7 ;2 [04] + +macro POP_STANDARD ; 16 cycles + pop x4 + pop cnt + pop bitcnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;--------------------------------------------------------------------------- +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +;--------------------------------------------------------------------------- +bitstuffN: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + nop ;1 [07] + rjmp didStuffN ;1 [08] +;--------------------------------------------------------------------------- +bitstuff6: ;- [04] + eor x1, x4 ;1 [05] + clr x2 ;1 [06] + rjmp didStuff6 ;1 [07] +;--------------------------------------------------------------------------- +bitstuff7: ;- [02] + eor x1, x4 ;1 [03] + clr x2 ;1 [06] + nop ;1 [05] + rjmp didStuff7 ;1 [06] +;--------------------------------------------------------------------------- +sendNakAndReti: ;- [-19] + ldi x3, USBPID_NAK ;1 [-18] + rjmp sendX3AndReti ;1 [-17] +;--------------------------------------------------------------------------- +sendAckAndReti: ;- [-17] + ldi cnt, USBPID_ACK ;1 [-16] +sendCntAndReti: ;- [-16] + mov x3, cnt ;1 [-15] +sendX3AndReti: ;- [-15] + ldi YL, 20 ;1 [-14] x3==r20 address is 20 + ldi YH, 0 ;1 [-13] + ldi cnt, 2 ;1 [-12] +; rjmp usbSendAndReti fallthrough +;--------------------------------------------------------------------------- +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We need not to match the transfer rate exactly because the spec demands +;only 1.5% precision anyway. +usbSendAndReti: ;- [-13] 13 cycles until SOP + in x2, USBDDR ;1 [-12] + ori x2, USBMASK ;1 [-11] + sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;1 [-08] port mirror for tx loop + out USBDDR, x2 ;1 [-07] <- acquire bus + ; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;1 [-06] exor mask + ldi shift, 0x80 ;1 [-05] sync byte is first byte sent + ldi bitcnt, 6 ;1 [-04] +txBitLoop: ;- [-04] [06] + sbrs shift, 0 ;1 [-03] [07] + eor x1, x4 ;1 [-02] [08] + ror shift ;1 [-01] [09] +didStuffN: ;- [09] + out USBOUT, x1 ;1 [00] [10] <-- out N + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuffN ;1 [03] + dec bitcnt ;1 [04] + brne txBitLoop ;1 [05] + sbrs shift, 0 ;1 [06] + eor x1, x4 ;1 [07] + ror shift ;1 [08] +didStuff6: ;- [08] + nop ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 6 + ror x2 ;1 [01] + cpi x2, 0xfc ;1 [02] + brcc bitstuff6 ;1 [03] + sbrs shift, 0 ;1 [04] + eor x1, x4 ;1 [05] + ror shift ;1 [06] + ror x2 ;1 [07] +didStuff7: ;- [07] + ldi bitcnt, 6 ;1 [08] + cpi x2, 0xfc ;1 [09] + out USBOUT, x1 ;1 [00] [10] <-- out 7 + brcc bitstuff7 ;1 [01] + ld shift, y+ ;2 [02+03] + dec cnt ;1 [04] + brne txBitLoop ;1 [05] +makeSE0: + cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] + lds x2, usbNewDeviceAddr;2 [07+08] + lsl x2 ;1 [09] we compare with left shifted address +;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 + out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle + subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;1 [02] + breq skipAddrAssign ;1 [03] + sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer +;---------------------------------------------------------------------------- +;end of usbDeviceAddress transfer +skipAddrAssign: ;- [03/04] + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 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 bitcnt ;[-12] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[-4] + ldi bitcnt, 0x55 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[1] + push x2 ;[3] + push x3 ;[5] + ldi shift, 0 ;[7] + ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9] == leap + + in x1, USBIN ;[11] <-- sample bit 0 + andi x1, USBMASK ;[12] + bst x1, USBMINUS ;[13] + bld shift, 7 ;[14] + push cnt ;[15] + ldi leap, 0 ;[17] [rx loop init] + ldi cnt, USB_BUFSIZE;[18] [rx loop init] + rjmp rxbit1 ;[19] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" +; accordingly to approximate this value in the long run. + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + rjmp didStuff6 ;[8] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + rjmp didStuff7 ;[3] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< max 52 cycles interrupt disable +;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 16.5 MHz -> 11 cycles per bit +; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt + push YL ;[-23] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-21] + push YL ;[-20] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-15] + rjmp foundK ;[-14] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 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 r0 ;[-12] +; [---] ;[-11] + push YH ;[-10] +; [---] ;[-9] + lds YL, usbInputBufOffset;[-8] +; [---] ;[-7] + clr YH ;[-6] + subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] + mov r0, x2 ;[-3] [rx loop init] + sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop YH ;[0] undo the pushes from before + pop r0 ;[2] + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 22 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: ;[1] + push shift ;[1] + push x1 ;[3] + push x2 ;[5] + push x3 ;[7] + ldi shift, 0xff ;[9] [rx loop init] + ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag + + in x1, USBIN ;[11] <-- sample bit 0 + bst x1, USBMINUS ;[12] + bld shift, 0 ;[13] + push x4 ;[14] == phase +; [---] ;[15] + push cnt ;[16] +; [---] ;[17] + ldi phase, 0 ;[18] [rx loop init] + ldi cnt, USB_BUFSIZE;[19] [rx loop init] + rjmp rxbit1 ;[20] +; [---] ;[21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- +/* +byte oriented operations done during loop: +bit 0: store data +bit 1: SE0 check +bit 2: overflow check +bit 3: catch up +bit 4: rjmp to achieve conditional jump range +bit 5: PLL +bit 6: catch up +bit 7: jump, fixup bitstuff +; 87 [+ 2] cycles +------------------------------------------------------------------ +*/ +continueWithBit5: + in x2, USBIN ;[055] <-- bit 5 + eor r0, x2 ;[056] + or phase, r0 ;[057] + sbrc phase, USBMINUS ;[058] + lpm ;[059] optional nop3; modifies r0 + in phase, USBIN ;[060] <-- phase + eor x1, x2 ;[061] + bst x1, USBMINUS ;[062] + bld shift, 5 ;[063] + andi shift, 0x3f ;[064] + in x1, USBIN ;[065] <-- bit 6 + breq unstuff5 ;[066] *** unstuff escape + eor phase, x1 ;[067] + eor x2, x1 ;[068] + bst x2, USBMINUS ;[069] + bld shift, 6 ;[070] +didUnstuff6: ;[ ] + in r0, USBIN ;[071] <-- phase + cpi shift, 0x02 ;[072] + brlo unstuff6 ;[073] *** unstuff escape +didUnstuff5: ;[ ] + nop2 ;[074] +; [---] ;[075] + in x2, USBIN ;[076] <-- bit 7 + eor x1, x2 ;[077] + bst x1, USBMINUS ;[078] + bld shift, 7 ;[079] +didUnstuff7: ;[ ] + eor r0, x2 ;[080] + or phase, r0 ;[081] + in r0, USBIN ;[082] <-- phase + cpi shift, 0x04 ;[083] + brsh rxLoop ;[084] +; [---] ;[085] +unstuff7: ;[ ] + andi x3, ~0x80 ;[085] + ori shift, 0x80 ;[086] + in x2, USBIN ;[087] <-- sample stuffed bit 7 + nop ;[088] + rjmp didUnstuff7 ;[089] +; [---] ;[090] + ;[080] + +unstuff5: ;[067] + eor phase, x1 ;[068] + andi x3, ~0x20 ;[069] + ori shift, 0x20 ;[070] + in r0, USBIN ;[071] <-- phase + mov x2, x1 ;[072] + nop ;[073] + nop2 ;[074] +; [---] ;[075] + in x1, USBIN ;[076] <-- bit 6 + eor r0, x1 ;[077] + or phase, r0 ;[078] + eor x2, x1 ;[079] + bst x2, USBMINUS ;[080] + bld shift, 6 ;[081] no need to check bitstuffing, we just had one + in r0, USBIN ;[082] <-- phase + rjmp didUnstuff5 ;[083] +; [---] ;[084] + ;[074] + +unstuff6: ;[074] + andi x3, ~0x40 ;[075] + in x1, USBIN ;[076] <-- bit 6 again + ori shift, 0x40 ;[077] + nop2 ;[078] +; [---] ;[079] + rjmp didUnstuff6 ;[080] +; [---] ;[081] + ;[071] + +unstuff0: ;[013] + eor r0, x2 ;[014] + or phase, r0 ;[015] + andi x2, USBMASK ;[016] check for SE0 + in r0, USBIN ;[017] <-- phase + breq didUnstuff0 ;[018] direct jump to se0 would be too long + andi x3, ~0x01 ;[019] + ori shift, 0x01 ;[020] + mov x1, x2 ;[021] mov existing sample + in x2, USBIN ;[022] <-- bit 1 again + rjmp didUnstuff0 ;[023] +; [---] ;[024] + ;[014] + +unstuff1: ;[024] + eor r0, x1 ;[025] + or phase, r0 ;[026] + andi x3, ~0x02 ;[027] + in r0, USBIN ;[028] <-- phase + ori shift, 0x02 ;[029] + mov x2, x1 ;[030] + rjmp didUnstuff1 ;[031] +; [---] ;[032] + ;[022] + +unstuff2: ;[035] + eor r0, x2 ;[036] + or phase, r0 ;[037] + andi x3, ~0x04 ;[038] + in r0, USBIN ;[039] <-- phase + ori shift, 0x04 ;[040] + mov x1, x2 ;[041] + rjmp didUnstuff2 ;[042] +; [---] ;[043] + ;[033] + +unstuff3: ;[043] + in x2, USBIN ;[044] <-- bit 3 again + eor r0, x2 ;[045] + or phase, r0 ;[046] + andi x3, ~0x08 ;[047] + ori shift, 0x08 ;[048] + nop ;[049] + in r0, USBIN ;[050] <-- phase + rjmp didUnstuff3 ;[051] +; [---] ;[052] + ;[042] + +unstuff4: ;[053] + andi x3, ~0x10 ;[054] + in x1, USBIN ;[055] <-- bit 4 again + ori shift, 0x10 ;[056] + rjmp didUnstuff4 ;[057] +; [---] ;[058] + ;[048] + +rxLoop: ;[085] + eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;[000] <-- bit 0 + st y+, x3 ;[001] +; [---] ;[002] + eor r0, x1 ;[003] + or phase, r0 ;[004] + eor x2, x1 ;[005] + in r0, USBIN ;[006] <-- phase + ser x3 ;[007] + bst x2, USBMINUS ;[008] + bld shift, 0 ;[009] + andi shift, 0xf9 ;[010] +rxbit1: ;[ ] + in x2, USBIN ;[011] <-- bit 1 + breq unstuff0 ;[012] *** unstuff escape + andi x2, USBMASK ;[013] SE0 check for bit 1 +didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff + breq se0 ;[014] + eor r0, x2 ;[015] + or phase, r0 ;[016] + in r0, USBIN ;[017] <-- phase + eor x1, x2 ;[018] + bst x1, USBMINUS ;[019] + bld shift, 1 ;[020] + andi shift, 0xf3 ;[021] +didUnstuff1: ;[ ] + in x1, USBIN ;[022] <-- bit 2 + breq unstuff1 ;[023] *** unstuff escape + eor r0, x1 ;[024] + or phase, r0 ;[025] + subi cnt, 1 ;[026] overflow check + brcs overflow ;[027] + in r0, USBIN ;[028] <-- phase + eor x2, x1 ;[029] + bst x2, USBMINUS ;[030] + bld shift, 2 ;[031] + andi shift, 0xe7 ;[032] +didUnstuff2: ;[ ] + in x2, USBIN ;[033] <-- bit 3 + breq unstuff2 ;[034] *** unstuff escape + eor r0, x2 ;[035] + or phase, r0 ;[036] + eor x1, x2 ;[037] + bst x1, USBMINUS ;[038] + in r0, USBIN ;[039] <-- phase + bld shift, 3 ;[040] + andi shift, 0xcf ;[041] +didUnstuff3: ;[ ] + breq unstuff3 ;[042] *** unstuff escape + nop ;[043] + in x1, USBIN ;[044] <-- bit 4 + eor x2, x1 ;[045] + bst x2, USBMINUS ;[046] + bld shift, 4 ;[047] +didUnstuff4: ;[ ] + eor r0, x1 ;[048] + or phase, r0 ;[049] + in r0, USBIN ;[050] <-- phase + andi shift, 0x9f ;[051] + breq unstuff4 ;[052] *** unstuff escape + rjmp continueWithBit5;[053] +; [---] ;[054] + +macro POP_STANDARD ; 16 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop YH + pop r0 + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuff7: + eor x1, x4 ;[4] + ldi x2, 0 ;[5] + nop2 ;[6] C is zero (brcc) + rjmp didStuff7 ;[8] + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + lpm ;[7] 3 cycle NOP, modifies r0 + out USBOUT, x1 ;[10] <-- out + rjmp didStuffN ;[0] + +#define bitStatus x3 + +sendNakAndReti: + ldi cnt, USBPID_NAK ;[-19] + rjmp sendCntAndReti ;[-18] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov r0, cnt ;[-16] + ldi YL, 0 ;[-15] R0 address is 0 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent + ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes +byteloop: +bitloop: + sbrs shift, 0 ;[8] [-3] + eor x1, x4 ;[9] [-2] + out USBOUT, x1 ;[10] [-1] <-- out + ror shift ;[0] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + nop ;[4] + subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 + brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + ror shift ;[9] +didStuff7: + out USBOUT, x1 ;[10] <-- out + ror x2 ;[0] + cpi x2, 0xfc ;[1] + brcc bitstuff7 ;[2] + ld shift, y+ ;[3] + dec cnt ;[5] + brne byteloop ;[6] +;make SE0: + cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[8] + lsl x2 ;[10] we compare with left shifted address + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 + sbci YH, 0 ;[1] + breq skipAddrAssign ;[2] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 12 cycles per bit +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop to receive the data bytes: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; cnt holds the number of bytes left in the receive buffer +; x3 holds the higher crc byte (see algorithm below) +; x4 is used as temporary register for the crc algorithm +; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further +; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted +; zl lower crc value and crc table index +; zh used for crc table accesses + +;-------------------------------------------------------------------------------------------------------------- +; CRC mods: +; table driven crc checker, Z points to table in prog space +; ZL is the lower crc byte, x3 is the higher crc byte +; x4 is used as temp register to store different results +; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the +; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial +; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. +; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and +; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF +; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version +;-------------------------------------------------------------------------------------------------------------- +; CRC algorithm: +; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form +; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order +; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i +; propose a research on CRC :-) ) +; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc +; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 +; (its destination). +; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as +; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored +; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc +; calculation is done. +; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) +; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized +; to a magic number which results in a crc value of 0xFFFF after the first complete byte. +; +; This algorithm is split into the extra cycles of the different bits: +; bit7: XOR the received byte to ZL +; bit5: load the new high byte to x4 +; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) +; move x4 (the new high byte) to x3, the crc value is ready +; + + +macro POP_STANDARD ; 18 cycles + pop ZH + pop ZL + pop cnt + pop x5 + pop x3 + pop x2 + pop x1 + pop shift + pop x4 + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +macro CRC_CLEANUP_AND_CHECK + ; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor + ; x3 is the higher crc byte, zl the lower one + ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table + lpm x2, Z ;[+2][+3][+4] + ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table + lpm ZL, Z ;[+6][+7][+8] + eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value + cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) + brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host + cpi x2, 0xb0 ;[+10] + brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host + endm + + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-17] + rjmp foundK ;[-16] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-15] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push x4 ;[-14] +; [---] ;[-13] + lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers +; [---] ;[-11] + clr YH ;[-10] + subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] + push shift ;[-7] +; [---] ;[-6] + ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop + clc ;[-4] the carry has to be clear for receipt of pid bit 0 + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop x4 ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 24 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] crc high byte + ldi x2, 1< jump back and store the byte + ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) + eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[3] mask the interesting bits + breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[5] the next bit expects the last state to be in x1 + rjmp didunstuff0 ;[6] + ;[7] jump delay of rjmp didunstuffX + +unstuff1: ;[11] this is the jump delay of breq unstuffX + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) + eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[4] mask the interesting bits + breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[6] the next bit expects the last state to be in x2 + nop2 ;[7] + ;[8] + rjmp didunstuff1 ;[9] + ;[10] jump delay of rjmp didunstuffX + +unstuff2: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) + in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x1, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x1, x2 ;[4] the next bit expects the last state to be in x1 + nop2 ;[5] + ;[6] + rjmp didunstuff2 ;[7] + ;[8] jump delay of rjmp didunstuffX + +unstuff3: ;[9] this is the jump delay of breq unstuffX + ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing + andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) + in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors + eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero + andi x2, USBMASK ;[2] mask the interesting bits + breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong + mov x2, x1 ;[4] the next bit expects the last state to be in x2 + nop2 ;[5] + ;[6] + rjmp didunstuff3 ;[7] + ;[8] jump delay of rjmp didunstuffX + + + +; the include has to be here due to branch distance restirctions +#define __USE_CRC__ +#include "asmcommon.inc" + + + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies +; 7.5 bit times is 90 cycles. ...there is plenty of time + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + rjmp sendX3AndReti ;[-17] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent + +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-6] <- acquire bus + ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-5] exor mask + ldi shift, 0x80 ;[-4] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 +txBitLoop: ; the loop sends the first 7 bits of the byte + sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state + eor x1, x4 ;[-1]=[11] + out USBOUT, x1 ;[0] + ror shift ;[1] + ror x2 ;[2] transfers the last sent bit to the stuffing history +didStuffN: + nop ;[3] + nop ;[4] + cpi x2, 0xfc ;[5] if we sent six consecutive ones + brcc bitstuffN ;[6] + lsr bitcnt ;[7] + brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount + +; transmit bit 7 + sbrs shift, 0 ;[9] + eor x1, x4 ;[10] +didStuff7: + ror shift ;[11] + out USBOUT, x1 ;[0] transfer bit 7 to the pins + ror x2 ;[1] move the bit into the stuffing history + cpi x2, 0xfc ;[2] + brcc bitstuff7 ;[3] + ld shift, y+ ;[4] get next byte to transmit + dec cnt ;[5] decrement byte counter + brne txByteLoop ;[7] if we have more bytes start next one + ;[8] branch delay + +;make SE0: + cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] + lds x2, usbNewDeviceAddr;[9] + lsl x2 ;[11] we compare with left shifted address + out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle + subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[2] +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[3] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< +int main (int argc, char **argv) +{ + int i, j; + for (i=0; i<512; i++){ + unsigned short crc = i & 0xff; + for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); + if((i & 7) == 0) printf("\n.byte "); + printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); + if(i == 255) printf("\n"); + } + return 0; +} + +// Use the following algorithm to compute CRC values: +ushort computeCrc(uchar *msg, uchar msgLen) +{ + uchar i; + ushort crc = 0xffff; + for(i = 0; i < msgLen; i++) + crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); + return crc; +} +*/ + +.balign 256 +usbCrcTableLow: +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 +.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + +; .balign 256 +usbCrcTableHigh: +.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 +.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 +.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E +.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 +.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A +.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC +.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 +.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 +.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 +.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 +.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE +.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 +.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA +.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C +.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 +.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 +.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 +.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 +.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE +.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 +.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA +.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C +.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 +.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 +.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 +.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 +.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E +.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 +.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A +.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C +.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 +.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + diff --git a/third_party/usbdrv/usbdrvasm20.inc b/third_party/usbdrv/usbdrvasm20.inc new file mode 100644 index 0000000..0c74679 --- /dev/null +++ b/third_party/usbdrv/usbdrvasm20.inc @@ -0,0 +1,360 @@ +/* Name: usbdrvasm20.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Jeroen Benschop + * Based on usbdrvasm16.inc from Christian Starkjohann + * Creation Date: 2008-03-05 + * Tabsize: 4 + * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 20 MHz version of the asssembler part of the USB driver. It +requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC +oscillator). + +See usbdrv.h for a description of the entire driver. + +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +#define leap2 x3 +#ifdef __IAR_SYSTEMS_ASM__ +#define nextInst $+2 +#else +#define nextInst .+0 +#endif + +;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes +;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts +;register use in receive loop: +; shift assembles the byte currently being received +; x1 holds the D+ and D- line state +; x2 holds the previous line state +; x4 (leap) is used to add a leap cycle once every three bytes received +; X3 (leap2) is used to add a leap cycle once every three stuff bits received +; bitcnt is used to determine when a stuff bit is due +; cnt holds the number of bytes left in the receive buffer + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-28] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-26] + push YL ;[-25] + push YH ;[-23] +;---------------------------------------------------------------------------- +; Synchronize with sync pattern: +;---------------------------------------------------------------------------- +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;The first part waits at most 1 bit long since we must be in sync pattern. +;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to +;waitForJ, ensure that this prerequisite is met. +waitForJ: + inc YL + sbis USBIN, USBMINUS + brne waitForJ ; just make sure we have ANY timeout +waitForK: +;The following code results in a sampling window of < 1/4 bit which meets the spec. + sbis USBIN, USBMINUS ;[-19] + rjmp foundK ;[-18] + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK + sbis USBIN, USBMINUS + rjmp foundK +#if USB_COUNT_SOF + lds YL, usbSofCount + inc YL + sts usbSofCount, YL +#endif /* USB_COUNT_SOF */ +#ifdef USB_SOF_HOOK + USB_SOF_HOOK +#endif + rjmp sofError +foundK: ;[-16] +;{3, 5} after falling D- edge, average delay: 4 cycles +;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample +;use 1 bit time for setup purposes, then sample again. Numbers in brackets +;are cycles from center of first sync (double K) bit after the instruction + push bitcnt ;[-16] +; [---] ;[-15] + lds YL, usbInputBufOffset;[-14] +; [---] ;[-13] + clr YH ;[-12] + subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] + push shift ;[-9] +; [---] ;[-8] + ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected + nop2 ;[-6] +; [---] ;[-5] + ldi bitcnt, 5 ;[-4] [rx loop init] + sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) + rjmp haveTwoBitsK ;[-2] + pop shift ;[-1] undo the push from before + pop bitcnt ;[1] + rjmp waitForK ;[3] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 27 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0] + push x2 ;[2] + push x3 ;[4] (leap2) + ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit + push x4 ;[7] == leap + ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received + push cnt ;[10] + ldi cnt, USB_BUFSIZE ;[12] [rx loop init] + ldi x2, 1< +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) /* not supported on IAR */ + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +/* The following definitions are not needed by the driver, but may be of some + * help if you port a gcc based project to IAR. + */ +#define cli() __disable_interrupt() +#define sei() __enable_interrupt() +#define wdt_reset() __watchdog_reset() +#define _BV(x) (1 << (x)) + +/* assembler compatibility macros */ +#define nop2 rjmp $+2 /* jump to next instruction */ +#define XL r26 +#define XH r27 +#define YL r28 +#define YH r29 +#define ZL r30 +#define ZH r31 +#define lo8(x) LOW(x) +#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ + +/* Depending on the device you use, you may get problems with the way usbdrv.h + * handles the differences between devices. Since IAR does not use #defines + * for MCU registers, we can't check for the existence of a particular + * register with an #ifdef. If the autodetection mechanism fails, include + * definitions for the required USB_INTR_* macros in your usbconfig.h. See + * usbconfig-prototype.h and usbdrv.h for details. + */ + +/* ------------------------------------------------------------------------- */ +#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ +/* ------------------------------------------------------------------------- */ +/* This port is not working (yet) */ + +/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ + +#include +#include + +#define __attribute__(arg) /* not supported on IAR */ + +#define PROGMEM __flash +#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) + +#ifndef __ASSEMBLER__ +static inline void cli(void) +{ + #asm("cli"); +} +static inline void sei(void) +{ + #asm("sei"); +} +#endif +#define _delay_ms(t) delay_ms(t) +#define _BV(x) (1 << (x)) +#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ + +#define macro .macro +#define endm .endmacro +#define nop2 rjmp .+0 /* jump to next instruction */ + +/* ------------------------------------------------------------------------- */ +#else /* default development environment is avr-gcc/avr-libc */ +/* ------------------------------------------------------------------------- */ + +#include +#ifdef __ASSEMBLER__ +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +#else +# include +#endif + +#if USB_CFG_DRIVER_FLASH_PAGE +# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) +#else +# define USB_READ_FLASH(addr) pgm_read_byte(addr) +#endif + +#define macro .macro +#define endm .endm +#define nop2 rjmp .+0 /* jump to next instruction */ + +#endif /* development environment */ + +/* for conveniecne, ensure that PRG_RDB exists */ +#ifndef PRG_RDB +# define PRG_RDB(addr) USB_READ_FLASH(addr) +#endif +#endif /* __usbportability_h_INCLUDED__ */ diff --git a/usb_keyboard.c b/usb_keyboard.c new file mode 100644 index 0000000..d7f6a63 --- /dev/null +++ b/usb_keyboard.c @@ -0,0 +1,108 @@ +/* + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "usb_keyboard.h" + +#include /* for sei() */ +#include +#include /* for _delay_ms() */ + +/* USB report descriptor, size must match usbconfig.h */ +const PROGMEM char usbHidReportDescriptor[35] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x05, 0x07, // USAGE_PAGE (Keyboard) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x25, 0x65, // LOGICAL_MAXIMUM (101) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +static uchar reportBuffer[2]; +static uchar idleRate; /* repeat rate for keyboards */ + +#define MOD_CONTROL_LEFT (1<<0) +#define MOD_SHIFT_LEFT (1<<1) +#define MOD_ALT_LEFT (1<<2) +#define MOD_GUI_LEFT (1<<3) +#define MOD_CONTROL_RIGHT (1<<4) +#define MOD_SHIFT_RIGHT (1<<5) +#define MOD_ALT_RIGHT (1<<6) +#define MOD_GUI_RIGHT (1<<7) + +uchar keyReport[2]; + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *rq = (void *)data; + + /* The following requests are never used. But since they are required by + * the specification, we implement them in this example. + */ + if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { + /* class request type */ + /* wValue: ReportType (highbyte), ReportID (lowbyte) */ + if(rq->bRequest == USBRQ_HID_GET_REPORT) { + /* we only have one report type, so don't look at wValue */ + usbMsgPtr = (void *)&reportBuffer; + return sizeof(reportBuffer); + } else if(rq->bRequest == USBRQ_HID_GET_IDLE) { + usbMsgPtr = &idleRate; + return 1; + } else if(rq->bRequest == USBRQ_HID_SET_IDLE) { + idleRate = rq->wValue.bytes[1]; + } + } else { + /* no vendor specific requests implemented */ + } + /* default for not implemented requests: return no data back to host */ + return 0; +} + +void initUsbKeyboard() { + usbInit(); + /* enforce re-enumeration, do this while interrupts are disabled! */ + usbDeviceDisconnect(); + uchar i; + i = 0; + while(--i) { /* fake USB disconnect for > 250 ms */ + wdt_reset(); + _delay_ms(1); + } + usbDeviceConnect(); + sei(); +} + +void prepareInterruptReport(uchar keycode) { + reportBuffer[0] = 0; /* modifier keys */ + reportBuffer[1] = keycode; + /* called after every poll of the interrupt endpoint */ + usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer)); +} diff --git a/usb_keyboard.h b/usb_keyboard.h new file mode 100644 index 0000000..5749a27 --- /dev/null +++ b/usb_keyboard.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "usbdrv.h" + +usbMsgLen_t usbFunctionSetup(uchar data[8]); +void initUsbKeyboard(); +void prepareInterruptReport(uchar key); + +#define KEYCODE_NONE 0x00 +#define KEYCODE_SPACE 0x2c +#define KEYCODE_PERIOD 0x37 +#define KEYCODE_HYPHEN 0x2d +#define KEYCODE_SLASH 0x38 +#define KEYCODE_ENTER 0x28 +#define KEYCODE_SEMICOLON 0x33 diff --git a/usbconfig.h b/usbconfig.h new file mode 100644 index 0000000..cda570b --- /dev/null +++ b/usbconfig.h @@ -0,0 +1,382 @@ +/* + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* +This is a configuration file of the V-USB USB driver for USB Morse key device. + +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. + +Also see the original V-USB documents to customize. +*/ + +/* ---------------------------- Hardware Config ---------------------------- */ + +#define USB_CFG_IOPORTNAME D +/* 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 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. + */ +#define USB_CFG_DPLUS_BIT 3 +/* 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! [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 ------------------------ */ + +#define USB_CFG_PULLUP_IOPORTNAME D +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +#define USB_CFG_PULLUP_BIT 5 +/* #define USB_CFG_PULLUP_BIT 4 */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ + +/* --------------------------- Functional Range ---------------------------- */ + +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 +/* Define this to 1 if you want to compile a version with two endpoints: The + * 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 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, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#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 100 +/* 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. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/* Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_MAX_BUS_POWER 20 +/* Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 0 +/* Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 0 +/* Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * 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) endpoints. + * You must implement the function usbFunctionWriteOut() which receives all + * 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 + * 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 /* = 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 may use one of obdev's free + * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! + */ +#define USB_CFG_DEVICE_ID 0xdb, 0x27 /* VOTI's Keyboard PID */ +/* 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 may use one of obdev's free shared VID/PID pairs. See the file + * USB-IDs-for-free.txt for details! + */ +#define USB_CFG_DEVICE_VERSION 0x00, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm' +#define USB_CFG_VENDOR_NAME_LEN 10 +/* 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 USB-IDs-for-free.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME 'M', 'o', 'z', 'c', '-', 'M', 'o', 'r', 's', 'e', \ + 'K', 'e', 'y' +#define USB_CFG_DEVICE_NAME_LEN 13 +/* Same as above for the device name. If you don't want a device name, undefine + * 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 */ +/* Same as above for the serial number. If you don't want a serial number, + * undefine the macros. + * It may be useful to provide the serial number through other means than at + * compile time. See the section about descriptor properties below for how + * 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_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 +#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. 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 35 +/* 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. + * 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 + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * 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(). 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), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * 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 +#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + +/* ----------------------- Optional MCU Description ------------------------ */ + +/* The following configurations have working defaults in usbdrv.h. You + * usually don't need to set them explicitly. Only if you want to run + * the driver on a device which is not yet supported or with a compiler + * which is not fully supported (such as IAR C) or if you use a differnt + * interrupt than INT0, you may have to define some of these. + */ +/* #define USB_INTR_CFG MCUCR */ +/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ +/* #define USB_INTR_CFG_CLR 0 */ +/* #define USB_INTR_ENABLE GIMSK */ +/* #define USB_INTR_ENABLE_BIT INT0 */ +/* #define USB_INTR_PENDING GIFR */ +/* #define USB_INTR_PENDING_BIT INTF0 */ +/* #define USB_INTR_VECTOR INT0_vect */ + +#define USB_INTR_CFG_SET ((1 << ISC10) | (1 << ISC11)) /* INT1(Pos Edge only) */ +#define USB_INTR_ENABLE_BIT INT1 +#define USB_INTR_PENDING_BIT INTF1 +#define USB_INTR_VECTOR INT1_vect + +#endif /* __usbconfig_h_included__ */