commit 6c88641e073c084356754653fc2afd05f926dc86 Author: Raphaël Assénat Date: Sun Mar 25 02:59:30 2007 +0000 Initial revision diff --git a/Changelog.txt b/Changelog.txt new file mode 100644 index 0000000..e58ab5e --- /dev/null +++ b/Changelog.txt @@ -0,0 +1,36 @@ +--- v1.4 (Dec 3, 2006) + - Added support for Atari style joysticks. Two button + variations (SMS) are supported. + - Added padding to nes report descriptor. This makes it + work correcly on windows. Linux sure is tolerant... + +--- v1.3 (Oct 28, 2006) + - Fixed a bug in nes.c, snes.c and snesmouse.c which prevented the pullup + on the data pin (from device) from being enabled. This resulted in + random button toggling when no controller was connected. + - A separate USB PID is used when in NES mode. + - PD1 is now left as input (no pullup) since it is shorted with PD0 on the + PCB rev.C. (And I didnt want to scrap a batch of 50 pcb nor cut a track + and solder a wire 50 times...) + +--- v1.2 (Oct 6, 2006) +Added serial number +Added support for Snes mouse (auto detected in snes mode) + - Default speed: Low + - 3 Speeds selectable by holding some buttons at + startup (USB connect or mouse connect) + Left+right buttons: Low speed + Left buton: Medium speed + Right button: High speed + No button: Default speed when USB connect, + continue using same speed for mouse (re)-connect + +--- v1.1 +Reworked report descriptor for better behaviour +in Windows and Mac OS X + +--- v1.0 (Jul 5, 2006) +Initial release: +Snes gamepad support +Nes gamepad support + diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..400d2e5 --- /dev/null +++ b/License.txt @@ -0,0 +1,458 @@ +PREFACE + +Conceiving and understanding a new license is not an easy task. To make things +easier for both, the author and the licensee, we have decided to base our +license for the USB driver on an existing license with well-understood +properties. + +Our favorite choice for the base license was the GNU General Public License +(GPL). However, we cannot use the GNU GPL directly for the following reasons: + +(1) It was not intended for projects involving hardware -- we must extend the + term "source code" to at least the circuit diagram. +(2) The GNU GPL does not require publication. Only if a binary is published, + it requires that the source is published as well. This is reasonable for + software because unpublished software is of little relevance. For projects + involving hardware, we want to REQUIRE publication. More than that, we + even want to define HOW the publication must be done (files contained, + file formats etc). +(3) As the author of the software, we can distribute it under more than one + license. For people who don't want to meet the obligations of the GNU GPL, + we want to offer commercial licenses. To avoid a split in revisions of + the driver, we need special privileges to distribute contributed + modifications under proprietary licenses. + +We can not simply modify the GNU GPL and incorporate our changes because the +Free Software Foundation (FSF) who holds the copyright for the text of the +GNU GPL does not allow modifications. We therefore set up our own small +license which incorporates the GNU GPL by reference: + + + +LICENSE FOR PROJECTS BUILT WITH "OBJECTIVE DEVELOPMENT'S +FIRMWARE-ONLY USB-DRIVER FOR ATMEL'S AVR MICROCONTROLLERS" +Version 2006-01 + + +I. Definitions + +"OBDEV" shall mean OBJECTIVE DEVELOPMENT Software GmbH or any legal successor +thereof. + +"Software Source Code" shall mean the preferred form of the software for +making modifications to it. + +"USB Driver" shall mean the Software Source Code for OBDEV's firmware-only +USB-driver for Atmel's AVR microcontrollers. + +"Function" shall mean the Software Source Code for all software executed on +the microcontroller except the USB Driver. + +"Host Software" shall mean the Software Source Code for all software required +to control the USB device from the USB host running any operating system. + +"Project" shall mean the USB Driver, the Function, the Host Software, circuit +diagrams of the controller based hardware and accompanying documentation. + +"source code" shall have the same meaning as the term "Project" above. + +"Web Site" shall mean a collection of text and multimedia documents accessible +worldwide over internet through the HyperText Transfer Protocol (HTTP) on +TCP port 80 (standard HTTP port). + + +II. General License Terms +The general terms of this license consist of the GNU General Public License +Version 2 (GNU GPL2) which is hereby incorporated into this section as though +it were fully set forth here. A copy of the GNU GPL2 is included for your +convenience in appendix A of this license. + +The term "source code" in the GNU GPL2 is to be understood as defined in +section I above. If any term or definition in section I, III, IV or V +conflicts with the GNU GPL2, the term or definition in section I, III, IV or +V has precedence of the GNU GPL2. + + +III. Distribution of the Project +The distributed form of a Project must contain at least the following files: +(a) Software Source Code files for the USB Driver, the Function and the Host + Software. +(b) Circuit diagrams for the hardware in PDF, PNG or GIF image file format. +(c) A file with name "Readme.txt" in ASCII format with at least the following + content (in English language): + - An explanation what the Project does. + - What to do with the distributed files (installation procedure etc.). + - A reference to Objective Development's USB driver. + - Your (author's) name and contact information. E-mail and/or URL is + sufficient. +(d) Optionally a text file with a description of the circuit diagram, an + explanation of special (software) techniques used etc. +(e) A copy of this license in a file with the name "License.txt". This copy + can be in the "usbdrv" subdirectory which contains the driver. + + +IV. Requirement for Publication +All modifications and derived work (Projects using the USB Driver) MUST be +distributed (published) as described in section III above on a Web Site. The +main page must reproduce at least a description of the Project (e.g. as +contained in the "Readme.txt" file distributed) and a download link for the +entire Project. The URL of the main page must be submitted to OBDEV. OBDEV +will provide a mechanism for submitting Project URLs and for publishing +Projects on their Web Site. The Project must remain available for at least +twelve (12) months after the initial publication or at least six (6) months +after a subsequent version of that particular Project has been published. + + +V. Author Privileges +OBDEV reserves the right to distribute the USB Driver and all modified +versions under other (proprietary) licenses. If you modify the USB Driver +under the grants of this license, you therefore grant OBDEV (in addition to +the grants of the GNU GPL2) a worldwide, perpetual, irrevocable royalty free +license for your modifications. OBDEV shall not automatically gain rights +other than those of the GNU GPL2 in the other parts of the Project. This +section V overrides possibly contradicting terms in the GNU GPL2 referenced +in section II. + + +APPENDIX A + + 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/Makefile b/Makefile new file mode 100644 index 0000000..006e253 --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +# Name: Makefile +# Project: HIDKeys +# Author: Christian Starkjohann +# Creation Date: 2006-02-02 +# Tabsize: 4 +# Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH +# License: Proprietary, free under certain conditions. See Documentation. +# This Revision: $Id: Makefile,v 1.1 2007-03-25 02:59:30 raph Exp $ + +UISP = uisp -dprog=stk500 -dpart=atmega8 -dserial=/dev/avr +COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=atmega8 #-DDEBUG_LEVEL=1 +COMMON_OBJS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o + +OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o snes.o devdesc.o + + +# symbolic targets: +all: main.hex + +.c.o: + $(COMPILE) -c $< -o $@ + +.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. + +.c.s: + $(COMPILE) -S $< -o $@ + + +clean: + rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s + +# file targets: +main.bin: $(COMMON_OBJS) snes.o devdesc.o + $(COMPILE) -o main.bin $(OBJECTS) -Wl,-Map=main.map + +main.hex: main.bin + rm -f main.hex main.eep.hex + avr-objcopy -j .text -j .data -O ihex main.bin main.hex + ./checksize main.bin + +flash: all + #$(UISP) --erase --upload --verify if=main.hex + $(UISP) --erase --upload if=main.hex + +# Fuse high byte: +# 0xc9 = 1 1 0 0 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000) +# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0 +# | | | | | +-------- BOOTSZ1 +# | | | | + --------- EESAVE (don't preserve EEPROM over chip erase) +# | | | +-------------- CKOPT (full output swing) +# | | +---------------- SPIEN (allow serial programming) +# | +------------------ WDTON (WDT not always on) +# +-------------------- RSTDISBL (reset pin is enabled) +# Fuse low byte: +# 0x9f = 1 0 0 1 1 1 1 1 +# ^ ^ \ / \--+--/ +# | | | +------- CKSEL 3..0 (external >8M crystal) +# | | +--------------- SUT 1..0 (crystal osc, BOD enabled) +# | +------------------ BODEN (BrownOut Detector enabled) +# +-------------------- BODLEVEL (2.7V) +fuse: + $(UISP) --wr_fuse_h=0xc9 --wr_fuse_l=0x9f + + + diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..d835d62 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,75 @@ +This is 4nes3snes Readme.txt file. + +Table of contents: + +1) What is 4nes3snes? +2) USB Implementation +3) Compilation and installation +4) License +5) About the vendor id/product id pair: +6) Where do I get more information and updates? + + +1) What is 4nes3snes? + -------------------- + 4nes3snes if a firmware for Atmel ATmega8 which + allows one to connect NES and SNES controllers to + a PC using a single circuit. Here are the possible + combinations: + - 4 NES controllers + - 3 SNES controllers + - 2 SNES controllers + 2 NES controllers + - 1 SNES controller + 3 NES controllers + + The device connects to an USB port and appears to the + PC as standard HID joystick with 4 axes and 32 buttons. + The two first controllers get their D-PADs mapped to a + pair of axes. The remaining controllers D-PADs + are mapped to ordinary buttons. + + +2) USB Implementation + ------------------ + 4nes3snes uses the software-only usb driver from Objective Development. + See http://www.obdev.at/products/avrusb/index.html + + A good portion of 4nes3snes is based on Objective Development's + HIDKeys example. + + +3) Compilation and installation + ---------------------------- + First, you must compile it. To compile, you need a working avr-gcc and + avr-libc. Under linux or cygwin, simply type make in the project directory. + (assuming avr-gcc is in your path). + + Next, you must upload the generated file (main.hex) to the Atmega8 using + whatever tools you like. Personally, I use uisp. The 'flash' and 'fuse' + targets in the makefile is a good example about how to use it. + + The Atmega fuse bits must also be set properly. The idea behind this is to + enable the external 12mhz crystal instead of the internal clock. Check the + makefile for good fuse bytes values. + + +4) License + ------- + 4nes3snes is released under Objective Development's extended GPL + license. See License.txt + + +5) About the vendor id/product id pair: + ------------------------------------ + Please dont re-use them for other projects. Instead, + obtain your own. I got mine from mecanique: + http://www.mecanique.co.uk/products/usb/pid.html + + +6) Where do I get more information and updates? + -------------------------------------------- + Visit 4nes3snes page: + http://www.raphnet.net/electronique/4nes_3snes/index_en.php + you may also contact me by email: + Raphael Assenat + + diff --git a/checksize b/checksize new file mode 100755 index 0000000..64c4a13 --- /dev/null +++ b/checksize @@ -0,0 +1,35 @@ +#!/bin/sh +# Name: checksize +# Project: AVR-USB +# Author: Christian Starkjohann +# Creation Date: 2004-12-29 +# Tabsize: 4 +# Copyright: (c) 2005 OBJECTIVE DEVELOPMENT Software GmbH. +# Revision: $Id: checksize,v 1.1 2007-03-25 02:59:31 raph Exp $ + +error=0 +codelimit=8192 +datalimit=960 # leave 64 bytes for stack + +if [ $# -gt 1 ]; then + codelimit="$2" +fi +if [ $# -gt 2 ]; then + datalimit="$3" +fi + +set -- `avr-size -d "$1" | awk '/[0-9]/ {print $1 + $2, $2 + $3, $2}'` +if [ $1 -gt $codelimit ]; then + echo "*** code size $1 exceeds limit of $codelimit" + error=1 +else + echo "ROM: $1 bytes (data=$3)" +fi +if [ $2 -gt $datalimit ]; then + echo "*** data size $2 exceeds limit of $datalimit" + error=1 +else + echo "RAM: $2 bytes" +fi + +exit $error diff --git a/circuit/sch-connectors.png b/circuit/sch-connectors.png new file mode 100644 index 0000000..77220a9 Binary files /dev/null and b/circuit/sch-connectors.png differ diff --git a/circuit/sch-core.png b/circuit/sch-core.png new file mode 100644 index 0000000..98f37f3 Binary files /dev/null and b/circuit/sch-core.png differ diff --git a/devdesc.c b/devdesc.c new file mode 100644 index 0000000..e3a455d --- /dev/null +++ b/devdesc.c @@ -0,0 +1,36 @@ +#include "devdesc.h" +#include "usbconfig.h" + +#define USBDESCR_DEVICE 1 + +const char usbDescrDevice[] PROGMEM = { /* USB device descriptor */ + 18, /* sizeof(usbDescrDevice): length of descriptor in bytes */ + USBDESCR_DEVICE, /* descriptor type */ + 0x01, 0x01, /* USB version supported */ + USB_CFG_DEVICE_CLASS, + USB_CFG_DEVICE_SUBCLASS, + 0, /* protocol */ + 8, /* max packet size */ + USB_CFG_VENDOR_ID, /* 2 bytes */ + USB_CFG_DEVICE_ID, /* 2 bytes */ + USB_CFG_DEVICE_VERSION, /* 2 bytes */ +#if USB_CFG_VENDOR_NAME_LEN + 1, /* manufacturer string index */ +#else + 0, /* manufacturer string index */ +#endif +#if USB_CFG_DEVICE_NAME_LEN + 2, /* product string index */ +#else + 0, /* product string index */ +#endif +#if USB_CFG_SERIAL_NUMBER_LENGTH + 3, /* serial number string index */ +#else + 0, /* serial number string index */ +#endif + 1, /* number of configurations */ +}; + +int getUsbDescrDevice_size(void) { return sizeof(usbDescrDevice); } + diff --git a/devdesc.h b/devdesc.h new file mode 100644 index 0000000..34d5003 --- /dev/null +++ b/devdesc.h @@ -0,0 +1,10 @@ +#ifndef _devdesc_h__ +#define _devdesc_h__ + +#include + +extern const char usbDescrDevice[] PROGMEM; +int getUsbDescrDevice_size(void); + +#endif // _devdesc_h__ + diff --git a/gamepad.h b/gamepad.h new file mode 100644 index 0000000..95eb401 --- /dev/null +++ b/gamepad.h @@ -0,0 +1,22 @@ +#ifndef _gamepad_h__ +#define _gamepad_h__ + +typedef struct { + // size of reports built by buildReport + int report_size; + + int reportDescriptorSize; + void *reportDescriptor; // must be in flash + + int deviceDescriptorSize; // if 0, use default + void *deviceDescriptor; // must be in flash + + void (*init)(void); + void (*update)(void); + char (*changed)(void); + void (*buildReport)(unsigned char *buf); +} Gamepad; + +#endif // _gamepad_h__ + + diff --git a/leds.h b/leds.h new file mode 100644 index 0000000..c47fb0b --- /dev/null +++ b/leds.h @@ -0,0 +1,18 @@ +#ifndef _leds_h__ +#define _leds_h__ + +#ifndef PORTD +#include +#endif + + +#define LED_OFF() +#define LED_ON(); +#define LED_TOGGLE(); + +//#define LED_OFF() do { PORTD |= 0x20; } while(0) +//#define LED_ON() do { PORTD &= ~0x20; } while(0) +//#define LED_TOGGLE() do { PORTD ^= 0x20; } while(0) + +#endif + diff --git a/main.c b/main.c new file mode 100644 index 0000000..6204155 --- /dev/null +++ b/main.c @@ -0,0 +1,218 @@ +/* Name: main.c + * Project: Multiple NES/SNES to USB converter + * Author: Raphael Assenat + * Copyright: (C) 2007 Raphael Assenat + * License: Proprietary, free under certain conditions. See Documentation. + * Tabsize: 4 + * Comments: Based on HID-Test by Christian Starkjohann + */ + +#define F_CPU 12000000L + +#include +#include +#include +#include +#include +#include + +#include "usbdrv.h" +#include "oddebug.h" +#include "gamepad.h" + +#include "snes.h" + +#include "leds.h" +#include "devdesc.h" + +int usbCfgSerialNumberStringDescriptor[] PROGMEM = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LENGTH), + '1', '0', '0', '0' + }; + +static Gamepad *curGamepad; + + +/* ----------------------- hardware I/O abstraction ------------------------ */ + +static void hardwareInit(void) +{ + uchar i, j; + + // init port C as input with pullup + DDRC = 0x00; + PORTC = 0xff; + + /* 1101 1000 bin: activate pull-ups except on USB lines + * + * USB signals are on bit 0 and 2. + * + * Bit 1 is connected with bit 0 (rev.C pcb error), so the pullup + * is not enabled. + * */ + PORTD = 0xf8; + + /* Usb pin are init as outputs */ + DDRD = 0x01 | 0x04; + + + j = 0; + while(--j){ /* USB Reset by device only required on Watchdog Reset */ + i = 0; + while(--i); /* delay >10ms for USB reset */ + } + DDRD = 0x00; /* 0000 0000 bin: remove USB reset condition */ + /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */ + TCCR0 = 5; /* timer 0 prescaler: 1024 */ + + TCCR2 = (1<bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */ + if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */ + /* we only have one report type, so don't look at wValue */ + curGamepad->buildReport(reportBuffer); + return curGamepad->report_size; + }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 */ + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int main(void) +{ + char must_report = 0, first_run = 1; + uchar idleCounter = 0; +// int run_mode; + + // led pin as output +// DDRD |= 0x20; + +#if 0 + /* Dip switch common: DB0, outputs: DB1 and DB2 */ + DDRB |= 0x01; + DDRB &= ~0x06; + + PORTB |= 0x06; /* enable pull up on DB1 and DB2 */ + PORTB &= ~0x01; /* Set DB0 to low */ + + _delay_ms(10); /* let pins settle */ + run_mode = (PINB & 0x06)>>1; + + switch(run_mode) + { + default: + case 3: + curGamepad = snesGetGamepad(); + break; + } +#endif + + curGamepad = snesGetGamepad(); + + // configure report descriptor according to + // the current gamepad + rt_usbHidReportDescriptor = curGamepad->reportDescriptor; + rt_usbHidReportDescriptorSize = curGamepad->reportDescriptorSize; + + if (curGamepad->deviceDescriptor != 0) + { + rt_usbDeviceDescriptor = (void*)curGamepad->deviceDescriptor; + rt_usbDeviceDescriptorSize = curGamepad->deviceDescriptorSize; + } + else + { + // use descriptor from devdesc.c + // + rt_usbDeviceDescriptor = (void*)usbDescrDevice; + rt_usbDeviceDescriptorSize = getUsbDescrDevice_size(); + } + + //wdt_enable(WDTO_2S); + hardwareInit(); + curGamepad->init(); + odDebugInit(); + usbInit(); + sei(); + DBG1(0x00, 0, 0); + + + for(;;){ /* main event loop */ + wdt_reset(); + + // this must be called at each 50 ms or less + usbPoll(); + + if (first_run) { + curGamepad->update(); + first_run = 0; + } + + if(TIFR & (1< 4){ + idleCounter -= 5; /* 22 ms in units of 4 ms */ + }else{ + idleCounter = idleRate; + must_report = 1; + } + } + } + + if (TIFR & (1<update(); + if (curGamepad->changed()) { + must_report = 1; + } + } + + } + + + if(must_report) + { + if (usbInterruptIsReady()) + { + + must_report = 0; + + curGamepad->buildReport(reportBuffer); + usbSetInterrupt(reportBuffer, curGamepad->report_size); + } + } + } + return 0; +} + +/* ------------------------------------------------------------------------- */ diff --git a/snes.c b/snes.c new file mode 100644 index 0000000..d226179 --- /dev/null +++ b/snes.c @@ -0,0 +1,339 @@ +/* Name: snes.c + * Project: Multiple NES/SNES to USB converter + * Author: Raphael Assenat + * Copyright: (C) 2007 Raphael Assenat + * License: Proprietary, free under certain conditions. See Documentation. + * Tabsize: 4 + */ +#define F_CPU 12000000L + +#include +#include +#include +#include +#include +#include "gamepad.h" +#include "leds.h" +#include "snes.h" + +#define REPORT_SIZE 8 +#define GAMEPAD_BYTES 8 /* 2 byte per snes controller * 4 controllers */ + +/******** IO port definitions **************/ +#define SNES_LATCH_DDR DDRC +#define SNES_LATCH_PORT PORTC +#define SNES_LATCH_BIT (1<<4) + +#define SNES_CLOCK_DDR DDRC +#define SNES_CLOCK_PORT PORTC +#define SNES_CLOCK_BIT (1<<5) + +#define SNES_DATA_PORT PORTC +#define SNES_DATA_DDR DDRC +#define SNES_DATA_PIN PINC +#define SNES_DATA_BIT1 (1<<3) /* controller 1 */ +#define SNES_DATA_BIT2 (1<<2) /* controller 2 */ +#define SNES_DATA_BIT3 (1<<1) /* controller 3 */ +#define SNES_DATA_BIT4 (1<<0) /* controller 4 */ + +/********* IO port manipulation macros **********/ +#define SNES_LATCH_LOW() do { SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); } while(0) +#define SNES_LATCH_HIGH() do { SNES_LATCH_PORT |= SNES_LATCH_BIT; } while(0) +#define SNES_CLOCK_LOW() do { SNES_CLOCK_PORT &= ~(SNES_CLOCK_BIT); } while(0) +#define SNES_CLOCK_HIGH() do { SNES_CLOCK_PORT |= SNES_CLOCK_BIT; } while(0) + +#define SNES_GET_DATA1() (SNES_DATA_PIN & SNES_DATA_BIT1) +#define SNES_GET_DATA2() (SNES_DATA_PIN & SNES_DATA_BIT2) +#define SNES_GET_DATA3() (SNES_DATA_PIN & SNES_DATA_BIT3) +#define SNES_GET_DATA4() (SNES_DATA_PIN & SNES_DATA_BIT4) + +/*********** prototypes *************/ +static void snesInit(void); +static void snesUpdate(void); +static char snesChanged(void); +static void snesBuildReport(unsigned char *reportBuffer); + + +// the most recent bytes we fetched from the controller +static unsigned char last_read_controller_bytes[GAMEPAD_BYTES]; + +// the most recently reported bytes +static unsigned char last_reported_controller_bytes[GAMEPAD_BYTES]; + +// indicates if a controller is in NES mode +static unsigned char nesMode=0; /* Bit0: controller 1, Bit1: controller 2...*/ + +static void snesInit(void) +{ + unsigned char sreg; + sreg = SREG; + cli(); + + // clock and latch as output + SNES_LATCH_DDR |= SNES_LATCH_BIT; + SNES_CLOCK_DDR |= SNES_CLOCK_BIT; + + // data as input + SNES_DATA_DDR &= ~(SNES_DATA_BIT1 | SNES_DATA_BIT2 | SNES_DATA_BIT3 | SNES_DATA_BIT4 ); + // enable pullup. This should prevent random toggling of pins + // when no controller is connected. + SNES_DATA_PORT |= (SNES_DATA_BIT1 | SNES_DATA_BIT2 | SNES_DATA_BIT3 | SNES_DATA_BIT4 ); + + // clock is normally high + SNES_CLOCK_PORT |= SNES_CLOCK_BIT; + + // LATCH is Active HIGH + SNES_LATCH_PORT &= ~(SNES_LATCH_BIT); + + nesMode = 0; + snesUpdate(); + + /* Snes controller buttons are sent in this order: + * 1st byte: B Y SEL START UP DOWN LEFT RIGHT + * 2nd byte: A X L R 1 1 1 1 + * + * Nes controller buttons are sent in this order: + * One byte: A B SEL START UP DOWN LEFT RIGHT + * + * When an additional byte is read from a NES controller, + * all bits are 0. Because the data signal is active low, + * this corresponds to pressed buttons. When we read + * from the controller for the first time, detect NES + * controllers by checking those 4 bits. + **/ + if (last_read_controller_bytes[1]==0xFF) + nesMode |= 1; + if (last_read_controller_bytes[3]==0xFF) + nesMode |= 2; + + if (last_read_controller_bytes[5]==0xFF) + nesMode |= 4; + + /* The last controllers are always in NES mode. But + * it is accessible only of the third is in NES mode + * too. */ + if (last_read_controller_bytes[7]==0xFF) + nesMode |= 8; + + SREG = sreg; +} + + +/* + * + Clock Cycle Button Reported + =========== =============== + 1 B + 2 Y + 3 Select + 4 Start + 5 Up on joypad + 6 Down on joypad + 7 Left on joypad + 8 Right on joypad + 9 A + 10 X + 11 L + 12 R + 13 none (always high) + 14 none (always high) + 15 none (always high) + 16 none (always high) + * + */ + +static void snesUpdate(void) +{ + int i; + unsigned char tmp1=0; + unsigned char tmp2=0; + unsigned char tmp3=0; + unsigned char tmp4=0; + + SNES_LATCH_HIGH(); + _delay_us(12); + SNES_LATCH_LOW(); + + for (i=0; i<8; i++) + { + _delay_us(6); + SNES_CLOCK_LOW(); + + tmp1 <<= 1; + tmp2 <<= 1; + tmp3 <<= 1; + tmp4 <<= 1; + if (!SNES_GET_DATA1()) { tmp1 |= 1; } + if (!SNES_GET_DATA2()) { tmp2 |= 1; } + if (!SNES_GET_DATA3()) { tmp3 |= 1; } + if (!SNES_GET_DATA4()) { tmp4 |= 1; } + + _delay_us(6); + + SNES_CLOCK_HIGH(); + } + last_read_controller_bytes[0] = tmp1; + last_read_controller_bytes[2] = tmp2; + last_read_controller_bytes[4] = tmp3; + last_read_controller_bytes[6] = tmp4; + + for (i=0; i<8; i++) + { + _delay_us(6); + + SNES_CLOCK_LOW(); + + // notice that this is different from above. We + // want the bits to be in reverse-order + tmp1 >>= 1; + tmp2 >>= 1; + tmp3 >>= 1; + tmp4 >>= 1; + if (!SNES_GET_DATA1()) { tmp1 |= 0x80; } + if (!SNES_GET_DATA2()) { tmp2 |= 0x80; } + if (!SNES_GET_DATA3()) { tmp3 |= 0x80; } + if (!SNES_GET_DATA4()) { tmp4 |= 0x80; } + + _delay_us(6); + SNES_CLOCK_HIGH(); + } + /* Force extra bits to 0 when in NES mode. Otherwise, if + * we read zeros on the wire, we will have permanantly + * pressed buttons */ + last_read_controller_bytes[1] = (nesMode & 1) ? 0x00 : tmp1; + last_read_controller_bytes[3] = (nesMode & 2) ? 0x00 : tmp2; + last_read_controller_bytes[5] = (nesMode & 4) ? 0x00 : tmp3; + last_read_controller_bytes[7] = (nesMode & 8) ? 0x00 : tmp4; + +} + +static char snesChanged(void) +{ + static int first = 1; + if (first) { first = 0; return 1; } + + return memcmp(last_read_controller_bytes, + last_reported_controller_bytes, GAMEPAD_BYTES); +} + +static char getX(unsigned char nesByte1) +{ + char x = 128; + if (nesByte1&0x1) { x = 255; } + if (nesByte1&0x2) { x = 0; } + return x; +} + +static char getY(unsigned char nesByte1) +{ + char y = 128; + if (nesByte1&0x4) { y = 255; } + if (nesByte1&0x8) { y = 0; } + return y; +} + +static unsigned char snesReorderButtons(unsigned char bytes[2]) +{ + unsigned char v; + + /* pack the snes button bits, which are on two bytes, in + * one single byte. */ + v = (bytes[0]&0x80)>>7; + v |= (bytes[0]&0x40)>>5; + v |= (bytes[0]&0x20)>>3; + v |= (bytes[0]&0x10)>>1; + v |= (bytes[1]&0x0f)<<4; + + return v; +} + +static void snesBuildReport(unsigned char *reportBuffer) +{ + /* last_read_controller_bytes[] structure: + * + * [0] : controller 1, 8 first bits (dpad + start + sel + y|a + b) + * [1] : controller 1, 8 snes extra bits (4 lower bits are buttons) + * + * [2] : controller 2, 8 first bits + * [3] : controller 2, 4 extra snes buttons + * + * [4] : controller 3, 8 first bits + * [5] : controller 3, 4 extra snes buttons + * + * [6] : controller 4, 8 first bits + * [7] : controller 4, 4 extra snes buttons + */ + + if (reportBuffer != NULL) + { + reportBuffer[0]=getX(last_read_controller_bytes[0]); + reportBuffer[1]=getY(last_read_controller_bytes[0]); + reportBuffer[2]=getX(last_read_controller_bytes[2]); + reportBuffer[3]=getY(last_read_controller_bytes[2]); + + reportBuffer[4] = snesReorderButtons(&last_read_controller_bytes[0]); + reportBuffer[5] = snesReorderButtons(&last_read_controller_bytes[2]); + + if (nesMode & 0x04) { + // Two last controllers are in NES mode. + reportBuffer[6] = last_read_controller_bytes[4]; + reportBuffer[7] = last_read_controller_bytes[6]; + } + else { + // Third controller is in SNES mode. Use the two + // last bytes for it. Sorry, no fourth controller. + reportBuffer[6] = last_read_controller_bytes[4]; + reportBuffer[7] = last_read_controller_bytes[5]; + } + + } + memcpy(last_reported_controller_bytes, + last_read_controller_bytes, + GAMEPAD_BYTES); +} + +const char snes_usbHidReportDescriptor[] PROGMEM = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x04, // USAGE (Joystick) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x32, // USAGE (Z) + 0x09, 0x36, // USAGE (SLIDER) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x04, // REPORT_COUNT (4) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xc0, // END_COLLECTION + + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 1, // USAGE_MINIMUM (Button 1) + 0x29, 32, // USAGE_MAXIMUM (Button 32) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 1, // REPORT_SIZE (1) + 0x95, 32, // REPORT_COUNT (32) + 0x81, 0x02, // INPUT (Data,Var,Abs) + + 0xc0 // END_COLLECTION +}; + +Gamepad SnesGamepad = { + report_size: REPORT_SIZE, + reportDescriptorSize: sizeof(snes_usbHidReportDescriptor), + init: snesInit, + update: snesUpdate, + changed: snesChanged, + buildReport: snesBuildReport +}; + +Gamepad *snesGetGamepad(void) +{ + SnesGamepad.reportDescriptor = (void*)snes_usbHidReportDescriptor; + + return &SnesGamepad; +} + diff --git a/snes.h b/snes.h new file mode 100644 index 0000000..71b616c --- /dev/null +++ b/snes.h @@ -0,0 +1,4 @@ +#include "gamepad.h" + +Gamepad *snesGetGamepad(void); + diff --git a/usbconfig.h b/usbconfig.h new file mode 100644 index 0000000..c7e0c7e --- /dev/null +++ b/usbconfig.h @@ -0,0 +1,166 @@ +/* Name: usbconfig.h + * Project: AVR USB driver + * Author: Christian Starkjohann, Modified by Raphael Assenat + * Creation Date: 2005-04-01 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: usbconfig.h,v 1.1 2007-03-25 02:59:31 raph Exp $ + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + +/* +General Description: +This file contains parts of the USB driver which can be configured and can or +must be adapted to your hardware. +*/ + +/* ---------------------------- Hardware Config ---------------------------- */ + +#define USB_CFG_IOPORT PORTD +/* This is the port where the USB bus is connected. When you configure it to + * "PORTB", the registers PORTB, PINB (=PORTB+2) and DDRB (=PORTB+1) will be + * used. + */ +#define USB_CFG_DMINUS_BIT 0 +/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This MUST be bit 0. All other values will result in a compile error! + */ +#define USB_CFG_DPLUS_BIT 2 +/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! + */ + +/* --------------------------- 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 1. + */ +#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_INTR_POLL_INTERVAL 10 +/* If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#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 100 +/* 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_SAMPLE_EXACT 1 +/* This variable affects Sampling Jitter for USB receiving. When it is 0, the + * driver guarantees a sampling window of 1/2 bit. The USB spec requires + * that the receiver has at most 1/4 bit sampling window. The 1/2 bit window + * should still work reliably enough because we work at low speed. If you want + * to meet the spec, set this value to 1. This will unroll a loop which + * results in bigger code size. + * If you have problems with long cables, try setting this value to 1. + */ +#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. + */ + +/* -------------------------- Device Description --------------------------- */ + +/* We cannot use Obdev's free shared VID/PID pair because this is a HID. + * We use John Hyde's VID (author of the book "USB Design By Example") for + * this example instead. John has offered this VID for use by students for + * non-commercial devices. Well... This example is for demonstration and + * education only... DO NOT LET DEVICES WITH THIS VID ESCAPE YOUR LAB! + * The Product-ID is a random number. + */ +#define USB_CFG_VENDOR_ID 0x81, 0x17 +/* USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you use obdev's free shared + * VID/PID pair. Be sure to read USBID-License.txt for rules! + */ +#define USB_CFG_DEVICE_ID 0x9c, 0x0a +/* This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you use obdev's free shared VID/PID pair. Be sure to read the rules in + * USBID-License.txt! + */ +#define USB_CFG_DEVICE_VERSION 0x04, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't' +#define USB_CFG_VENDOR_NAME_LEN 11 +/* These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USBID-License.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME '4',' ','A','x','e','s',',',' ', \ + '3','2',' ','B','u','t','t','o','n','s' +#define USB_CFG_DEVICE_NAME_LEN 18 +/* Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USBID-License.txt before you assign a name. + */ +#define USB_CFG_SERIAL_NUMBER_LENGTH 4 +/* Set this define to the number of charcters in the serial number if your + * device should have a serial number to uniquely identify each hardware + * instance. You must supply the serial number in a string descriptor with the + * name "usbCfgSerialNumberStringDescriptor", e.g.: + * #define USB_CFG_SERIAL_NUMBER_LENGTH 5 + * int usbCfgSerialNumberStringDescriptor[] PROGMEM = { + * USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LENGTH), + * '1', '2', '3', '4', '5' + * }; + * See usbdrv.h for more information about the USB_STRING_DESCRIPTOR_HEADER() + * macro or usbdrv.c for example string descriptors. + * You may want to put "usbCfgSerialNumberStringDescriptor" at a constant + * flash memory address (with magic linker commands) so that you don't need + * to recompile if you change it. + */ +#define USB_CFG_DEVICE_CLASS 0 /* specify the class at the interface level */ +#define USB_CFG_DEVICE_SUBCLASS 0 +/* See USB specification if you want to conform to an existing device class. + */ +#define USB_CFG_INTERFACE_CLASS 0x03 /* HID class */ +#define USB_CFG_INTERFACE_SUBCLASS 0 /* no boot interface */ +#define USB_CFG_INTERFACE_PROTOCOL 0 /* no protocol */ +/* See USB specification if you want to conform to an existing device class or + * protocol. + */ +//#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 +/* Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + */ + +/* Define this if you want to be able to set the report descriptor + * dynamically at runtime. + * */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME + +/* Define this if you want to be able to set the device descriptor + * dynamically at runtime. + */ +#define USB_CFG_DEVICE_DESCRIPTOR_RUNTIME + + +#endif /* __usbconfig_h_included__ */ diff --git a/usbdrv/Changelog.txt b/usbdrv/Changelog.txt new file mode 100644 index 0000000..88c6870 --- /dev/null +++ b/usbdrv/Changelog.txt @@ -0,0 +1,83 @@ +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 diff --git a/usbdrv/License.txt b/usbdrv/License.txt new file mode 100644 index 0000000..400d2e5 --- /dev/null +++ b/usbdrv/License.txt @@ -0,0 +1,458 @@ +PREFACE + +Conceiving and understanding a new license is not an easy task. To make things +easier for both, the author and the licensee, we have decided to base our +license for the USB driver on an existing license with well-understood +properties. + +Our favorite choice for the base license was the GNU General Public License +(GPL). However, we cannot use the GNU GPL directly for the following reasons: + +(1) It was not intended for projects involving hardware -- we must extend the + term "source code" to at least the circuit diagram. +(2) The GNU GPL does not require publication. Only if a binary is published, + it requires that the source is published as well. This is reasonable for + software because unpublished software is of little relevance. For projects + involving hardware, we want to REQUIRE publication. More than that, we + even want to define HOW the publication must be done (files contained, + file formats etc). +(3) As the author of the software, we can distribute it under more than one + license. For people who don't want to meet the obligations of the GNU GPL, + we want to offer commercial licenses. To avoid a split in revisions of + the driver, we need special privileges to distribute contributed + modifications under proprietary licenses. + +We can not simply modify the GNU GPL and incorporate our changes because the +Free Software Foundation (FSF) who holds the copyright for the text of the +GNU GPL does not allow modifications. We therefore set up our own small +license which incorporates the GNU GPL by reference: + + + +LICENSE FOR PROJECTS BUILT WITH "OBJECTIVE DEVELOPMENT'S +FIRMWARE-ONLY USB-DRIVER FOR ATMEL'S AVR MICROCONTROLLERS" +Version 2006-01 + + +I. Definitions + +"OBDEV" shall mean OBJECTIVE DEVELOPMENT Software GmbH or any legal successor +thereof. + +"Software Source Code" shall mean the preferred form of the software for +making modifications to it. + +"USB Driver" shall mean the Software Source Code for OBDEV's firmware-only +USB-driver for Atmel's AVR microcontrollers. + +"Function" shall mean the Software Source Code for all software executed on +the microcontroller except the USB Driver. + +"Host Software" shall mean the Software Source Code for all software required +to control the USB device from the USB host running any operating system. + +"Project" shall mean the USB Driver, the Function, the Host Software, circuit +diagrams of the controller based hardware and accompanying documentation. + +"source code" shall have the same meaning as the term "Project" above. + +"Web Site" shall mean a collection of text and multimedia documents accessible +worldwide over internet through the HyperText Transfer Protocol (HTTP) on +TCP port 80 (standard HTTP port). + + +II. General License Terms +The general terms of this license consist of the GNU General Public License +Version 2 (GNU GPL2) which is hereby incorporated into this section as though +it were fully set forth here. A copy of the GNU GPL2 is included for your +convenience in appendix A of this license. + +The term "source code" in the GNU GPL2 is to be understood as defined in +section I above. If any term or definition in section I, III, IV or V +conflicts with the GNU GPL2, the term or definition in section I, III, IV or +V has precedence of the GNU GPL2. + + +III. Distribution of the Project +The distributed form of a Project must contain at least the following files: +(a) Software Source Code files for the USB Driver, the Function and the Host + Software. +(b) Circuit diagrams for the hardware in PDF, PNG or GIF image file format. +(c) A file with name "Readme.txt" in ASCII format with at least the following + content (in English language): + - An explanation what the Project does. + - What to do with the distributed files (installation procedure etc.). + - A reference to Objective Development's USB driver. + - Your (author's) name and contact information. E-mail and/or URL is + sufficient. +(d) Optionally a text file with a description of the circuit diagram, an + explanation of special (software) techniques used etc. +(e) A copy of this license in a file with the name "License.txt". This copy + can be in the "usbdrv" subdirectory which contains the driver. + + +IV. Requirement for Publication +All modifications and derived work (Projects using the USB Driver) MUST be +distributed (published) as described in section III above on a Web Site. The +main page must reproduce at least a description of the Project (e.g. as +contained in the "Readme.txt" file distributed) and a download link for the +entire Project. The URL of the main page must be submitted to OBDEV. OBDEV +will provide a mechanism for submitting Project URLs and for publishing +Projects on their Web Site. The Project must remain available for at least +twelve (12) months after the initial publication or at least six (6) months +after a subsequent version of that particular Project has been published. + + +V. Author Privileges +OBDEV reserves the right to distribute the USB Driver and all modified +versions under other (proprietary) licenses. If you modify the USB Driver +under the grants of this license, you therefore grant OBDEV (in addition to +the grants of the GNU GPL2) a worldwide, perpetual, irrevocable royalty free +license for your modifications. OBDEV shall not automatically gain rights +other than those of the GNU GPL2 in the other parts of the Project. This +section V overrides possibly contradicting terms in the GNU GPL2 referenced +in section II. + + +APPENDIX A + + 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/usbdrv/Readme.txt b/usbdrv/Readme.txt new file mode 100644 index 0000000..7d8031a --- /dev/null +++ b/usbdrv/Readme.txt @@ -0,0 +1,87 @@ +This is the Readme file to Objective Development's firmware-only USB driver +for Atmel AVR microcontrollers. For more information please visit +http://www.obdev.at/avrusb/ + +This directory contains the USB firmware only. Copy it as-is to your own +project and add your own version of "usbconfig.h". + + +TECHNICAL DOCUMENTATION +======================= +The technical documentation for the firmware driver is contained in the file +"usbdrv.h". Please read all of it carefully! + + +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 a single VID/PID pair for free. If you want to use +your own VID and PID instead of our's, define the macros "USB_CFG_VENDOR_ID" +and "USB_CFG_DEVICE_ID" accordingly in "usbconfig.h". + +To use our predefined VID/PID pair, you MUST conform to a couple of +requirements. See the file "USBID-License.txt" for details. + + +HOST DRIVER +=========== +You have received this driver together with an example device implementation +and an example host driver. The host driver is based on libusb and compiles +on various Unix flavors (Linux, BSD, Mac OS X). It also compiles natively on +Windows using MinGW (see www.mingw.org) and libusb-win32 (see +libusb-win32.sourceforge.net). The "Automator" project contains a native +Windows host driver (not based on libusb) for Human Interface Devices. + + +DEVELOPMENT SYSTEM +================== +This driver has been developed and optimized for the GNU compiler version 3 +(gcc 3). It does work well with gcc 4 and future versions will probably be +optimized for gcc 4. We recommend that you use the GNU compiler suite because +it is freely available. AVR-USB has also been ported to the IAR compiler and +assembler. It has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 +with the "small" memory model. The "tiny" memory is not supported. Please +note that gcc is more efficient for usbdrv.c because this module has been +deliberately optimized for gcc. + + +USING AVR-USB FOR FREE +====================== +The AVR firmware driver is published under an Open Source compliant license. +See the file "License.txt" for details. Since it is not obvious for many +people how this license applies to their own projects, here's a short guide: + +(1) The USB driver and all your modifications to the driver itself are owned +by Objective Development. You must give us a worldwide, perpetual, +irrevocable royalty free license for your modifications. + +(2) Since you own the code you have written (except where you modify our +driver), you can (at least in principle) determine the license for it freely. +However, to "pay" for the USB driver code you link against, we demand that +you choose an Open Source compliant license (compatible with our license) for +your source code and the hardware circuit diagrams. Simply attach your +license of choice to your parts of the project and leave our "License.txt" in +the "usbdrv" subdirectory. + +(3) We also demand that you publish your work on the Internet and drop us a +note with the URL. The publication must meet certain formal criteria (files +distributed, file formats etc.). See the file "License.txt" for details. + +Other than that, you are allowed to manufacture any number of units and sell +them for any price. If you like our driver, we also encourage you to make a +donation on our web site. + + +COMMERCIAL LICENSES FOR AVR-USB +=============================== +If you don't want to publish your source code and the circuit diagrams under +an Open Source license, you can simply pay money for AVR-USB. As an +additional benefit you get USB PIDs for free, licensed exclusively to you. +See http://www.obdev.at/products/avrusb/license.html for details. + + + diff --git a/usbdrv/USBID-License.txt b/usbdrv/USBID-License.txt new file mode 100644 index 0000000..509f38d --- /dev/null +++ b/usbdrv/USBID-License.txt @@ -0,0 +1,132 @@ +Royalty-Free Non-Exclusive License USB Product-ID +================================================= + +Version 2006-02-20 + +OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive +right to use two USB.org vendor-ID (VID) / product-ID (PID) pairs with +products based on Objective Development's firmware-only USB driver for +Atmel AVR microcontrollers: + + * VID = 5824 (=0x16c0) / PID = 1500 (=0x5dc) for devices implementing no + USB device class (vendor-class devices with USB class = 0xff). Devices + using this pair will be referred to as "VENDOR CLASS" devices. + + * VID = 5824 (=0x16c0) / PID = 1503 (=0x5df) for HID class devices + (excluding mice and keyboards). Devices using this pair will be referred + to as "HID CLASS" devices. + +Since the granted right is non-exclusive, the same VID/PID pairs may be +used by many companies and individuals for different products. To avoid +conflicts, your device and host driver software MUST adhere to the rules +outlined below. + +OBJECTIVE DEVELOPMENT Software GmbH has licensed these VID/PID pairs from +Wouter van Ooijen (see www.voti.nl), who has licensed the VID from the USB +Implementers Forum, Inc. (see www.usb.org). The VID is registered for the +company name "Van Ooijen Technische Informatica". + + +RULES AND RESTRICTIONS +====================== + +(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/avrusb/". + +(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) Matching of device-specific drivers MUST be based on the textual +manufacturer and product identification in addition to the usual VID/PID +matching. This means that operating system features which are based on +VID/PID matching only (e.g. Windows kernel level drivers, automatic actions +when the device is plugged in etc) MUST NOT be used. The driver matching +MUST be a comparison of the entire strings, NOT a sub-string match. + +(6) The extent to which VID/PID matching is allowed for non device-specific +drivers or features depends on the operating system and particular VID/PID +pair used: + + * Mac OS X, Linux, FreeBSD and other Unixes: No VID/PID matching is + required for both, the VENDOR and the HID CLASS devices and hence no + VID/PID-only matching is allowed at all. + + * Windows: The operating system performs VID/PID matching for the kernel + level driver. You are REQUIRED to use libusb-win32 (see + http://libusb-win32.sourceforge.net/) as the kernel level driver for + VENDOR CLASS devices. HID CLASS devices all use the generic HID class + driver shipped with Windows, except mice and keyboards. You therefore + MUST NOT use any of the shared VID/PID pairs for mice or keyboards. + +(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any +problems which are caused by the shared use of these VID/PID pairs. You +have been warned that the sharing of VID/PID pairs may cause problems. If +you want to avoid them, get your own VID/PID pair for exclusive use. + + +HOW TO IMPLEMENT THESE RULES +============================ + +The host driver MUST iterate over all devices with the given VID/PID +numbers in their device descriptors and query the string representation for +the manufacturer name in USB language 0x0409 (English/US). It MUST compare +the ENTIRE string with your textual manufacturer identification chosen in +(2) above. A substring search for your domain or e-mail address is NOT +acceptable. The driver MUST NOT touch the device (other than querying the +descriptors) unless the strings match. + +For all USB devices with matching VID/PID and textual manufacturer +identification, the host driver must query the textual product +identification and string-compare it with the name of the product it can +control. It may only initialize the device if the product matches exactly. + +Objective Development provides examples for these matching rules with the +"PowerSwitch" project (using libusb) and with the "Automator" project +(using Windows calls on Windows and libusb on Unix). + + +Technical Notes: +================ + +Sharing the same VID/PID pair among devices is possible as long as ALL +drivers which match the VID/PID also perform matching on the textual +identification strings. This is easy on all operating systems except +Windows, since Windows establishes a static connection between the VID/PID +pair and a kernel level driver. All devices with the same VID/PID pair must +therefore use THE SAME kernel level driver. + +We therefore demand that you use libusb-win32 for VENDOR CLASS devices. +This is a generic kernel level driver which allows all types of USB access +for user space applications. This is only a partial solution of the +problem, though, because different device drivers may come with different +versions of libusb-win32 and they may not work with the libusb version of +the respective other driver. You are therefore encouraged to test your +driver against a broad range of libusb-win32 versions. Do not use new +features in new versions, or check for their existence before you use them. +When a new libusb-win32 becomes available, make sure that your driver is +compatible with it. + +For HID CLASS devices it is necessary that all those devices bind to the +same kernel driver: Microsoft's generic USB HID driver. This is true for +all HID devices except those with a specialized driver. Currently, the only +HIDs with specialized drivers are mice and keyboards. You therefore MUST +NOT use a shared VID/PID with mouse and keyboard devices. + +Sharing the same VID/PID among different products is unusual and probably +violates the USB specification. If you do it, you do it at your own risk. + +To avoid possible incompatibilities, we highly recommend that you get your +own VID/PID pair if you intend to sell your product. Objective +Development's commercial licenses for AVR-USB include a PID for +unrestricted exclusive use. diff --git a/usbdrv/iarcompat.h b/usbdrv/iarcompat.h new file mode 100644 index 0000000..9cede20 --- /dev/null +++ b/usbdrv/iarcompat.h @@ -0,0 +1,63 @@ +/* Name: iarcompat.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: iarcompat.h,v 1.1 2007-03-25 02:59:32 raph Exp $ + */ + +/* +General Description: +This header is included when we compile with the IAR C-compiler and assembler. +It defines macros for cross compatibility between gcc and IAR-cc. + +Thanks to Oleg Semyonov for his help with the IAR tools port! +*/ + +#ifndef __iarcompat_h_INCLUDED__ +#define __iarcompat_h_INCLUDED__ + +#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ + +/* Enable bit definitions */ +#ifndef ENABLE_BIT_DEFINITIONS +# define ENABLE_BIT_DEFINITIONS 1 +#endif + +/* Include IAR headers */ +#include +#ifndef __IAR_SYSTEMS_ASM__ +# include +#endif + +#define __attribute__(arg) +#define IAR_SECTION(section) @ section + +#ifndef USB_BUFFER_SECTION +# define USB_BUFFER_SECTION "TINY_Z" /* if user has not selected a named section */ +#endif + +#ifdef __IAR_SYSTEMS_ASM__ +# define __ASSEMBLER__ +#endif + +#ifdef __HAS_ELPM__ +# define PROGMEM __farflash +#else +# define PROGMEM __flash +#endif + +#define PRG_RDB(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() + + +#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ +#endif /* __iarcompat_h_INCLUDED__ */ diff --git a/usbdrv/oddebug.c b/usbdrv/oddebug.c new file mode 100644 index 0000000..5ea1f59 --- /dev/null +++ b/usbdrv/oddebug.c @@ -0,0 +1,54 @@ +/* Name: oddebug.c + * Project: AVR library + * Author: Christian Starkjohann + * Creation Date: 2005-01-16 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: oddebug.c,v 1.1 2007-03-25 02:59:31 raph Exp $ + */ + +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ICC__ +# include +#endif +#include "oddebug.h" + +#if DEBUG_LEVEL > 0 + +#warning "Debugging is turned on! Never compile production devices with debugging!" + +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/usbdrv/oddebug.h b/usbdrv/oddebug.h new file mode 100644 index 0000000..de0c496 --- /dev/null +++ b/usbdrv/oddebug.h @@ -0,0 +1,120 @@ +/* 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: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: oddebug.h,v 1.1 2007-03-25 02:59:32 raph Exp $ + */ + +#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 + +#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< +# include +#endif +#include "usbdrv.h" +#include "oddebug.h" +#include "../leds.h" +/* +General Description: +This module implements the C-part of the USB driver. See usbdrv.h for a +documentation of the entire driver. +*/ + +#ifndef IAR_SECTION +#define IAR_SECTION(arg) +#define __no_init +#endif +/* The macro IAR_SECTION is a hack to allow IAR-cc compatibility. On gcc, it + * is defined to nothing. __no_init is required on IAR. + */ + +/* ------------------------------------------------------------------------- */ + +/* raw USB registers / interface to assembler code: */ +/* usbRxBuf MUST be in 1 byte addressable range (because usbInputBuf is only 1 byte) */ +__no_init uchar usbRxBuf[2][USB_BUFSIZE] __attribute__ ((section (USB_BUFFER_SECTION))) IAR_SECTION(USB_BUFFER_SECTION);/* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ +uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ +uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ +uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ +uchar usbInputBuf; /* ptr to raw buffer used for receiving */ +uchar usbAppBuf; /* ptr to raw buffer passed to app for processing */ +volatile schar usbRxLen; /* = 0; number of bytes in usbAppBuf; 0 means free */ +uchar usbCurrentTok; /* last token received */ +uchar usbRxToken; /* token for data we received */ +uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */ +volatile schar usbTxLen = -1; /* number of bytes to transmit with next IN token */ +uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen == -1 */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +/* uchar usbRxEndp; endpoint which was addressed (1 bit in MSB) [not impl] */ +volatile schar usbTxLen1 = -1; /* TX count for endpoint 1 */ +uchar usbTxBuf1[USB_BUFSIZE];/* TX data for endpoint 1 */ +#endif +uchar usbAckBuf[1] = {USBPID_ACK}; /* transmit buffer for ack tokens */ +uchar usbNakBuf[1] = {USBPID_NAK}; /* transmit buffer for nak tokens */ + +/* USB status registers / not shared with asm code */ +uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ +static uchar usbMsgFlags; /* flag values see below */ +static uchar usbIsReset; /* = 0; USB bus is in reset phase */ + +#define USB_FLG_TX_PACKET (1<<0) +/* Leave free 6 bits after TX_PACKET. This way we can increment usbMsgFlags to toggle TX_PACKET */ +#define USB_FLG_MSGPTR_IS_ROM (1<<6) +#define USB_FLG_USE_DEFAULT_RW (1<<7) + + +/* Parameters that are configurable at runtime */ +#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME +const char *rt_usbHidReportDescriptor; +uchar rt_usbHidReportDescriptorSize; +#endif +#ifdef USB_CFG_DEVICE_DESCRIPTOR_RUNTIME +const char *rt_usbDeviceDescriptor; +uchar rt_usbDeviceDescriptorSize; +#endif + + +/* +optimizing hints: +- do not post/pre inc/dec integer values in operations +- assign value of PRG_RDB() to register variables and don't use side effects in arg +- use narrow scope for variables which should be in X/Y/Z register +- assign char sized expressions to variables to force 8 bit arithmetics +*/ + +/* ------------------------------------------------------------------------- */ + +#ifndef USB_CFG_DEVICE_DESCRIPTOR_RUNTIME +static PROGMEM char usbDescrDevice[] = { /* USB device descriptor */ + 18, /* sizeof(usbDescrDevice): length of descriptor in bytes */ + USBDESCR_DEVICE, /* descriptor type */ + 0x01, 0x01, /* USB version supported */ + USB_CFG_DEVICE_CLASS, + USB_CFG_DEVICE_SUBCLASS, + 0, /* protocol */ + 8, /* max packet size */ + USB_CFG_VENDOR_ID, /* 2 bytes */ + USB_CFG_DEVICE_ID, /* 2 bytes */ + USB_CFG_DEVICE_VERSION, /* 2 bytes */ +#if USB_CFG_VENDOR_NAME_LEN + 1, /* manufacturer string index */ +#else + 0, /* manufacturer string index */ +#endif +#if USB_CFG_DEVICE_NAME_LEN + 2, /* product string index */ +#else + 0, /* product string index */ +#endif +#if USB_CFG_SERIAL_NUMBER_LENGTH + 3, /* serial number string index */ +#else + 0, /* serial number string index */ +#endif + 1, /* number of configurations */ +}; +#endif + +static PROGMEM char usbDescrConfig[] = { /* USB configuration descriptor */ + 9, /* sizeof(usbDescrConfig): length of descriptor in bytes */ + USBDESCR_CONFIG, /* descriptor type */ + (18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH || defined(USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME) + + 9 +#endif + ), 0, /* total length of data returned (including inlined descriptors) */ + 1, /* number of interfaces in this configuration */ + 1, /* index of this configuration */ + 0, /* configuration name string index */ +#if USB_CFG_IS_SELF_POWERED + USBATTR_SELFPOWER, /* attributes */ +#else + USBATTR_BUSPOWER, /* attributes */ +#endif + USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ +/* interface descriptor follows inline: */ + 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ + USBDESCR_INTERFACE, /* descriptor type */ + 0, /* index of this interface */ + 0, /* alternate setting for this interface */ + USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */ + USB_CFG_INTERFACE_CLASS, + USB_CFG_INTERFACE_SUBCLASS, + USB_CFG_INTERFACE_PROTOCOL, + 0, /* string index for interface */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH || defined(USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME) /* HID descriptor */ + 9, /* sizeof(usbDescrHID): length of descriptor in bytes */ + USBDESCR_HID, /* descriptor type: HID */ + 0x01, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + 0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ + 0x22, /* descriptor type: report */ +#ifndef USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH + 0, /* substituted with real value on the fly in usbRead */ +#else + USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, +#endif + 0, /* total length of report descriptor */ +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + 0x81, /* IN endpoint number 1 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ +#endif +}; + +static PROGMEM char usbDescrString0[] = { /* language descriptor */ + 4, /* sizeof(usbDescrString0): length of descriptor in bytes */ + 3, /* descriptor type */ + 0x09, 0x04, /* language index (0x0409 = US-English) */ +}; + +#if USB_CFG_VENDOR_NAME_LEN +static PROGMEM int usbDescrString1[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), + USB_CFG_VENDOR_NAME +}; +#endif +#if USB_CFG_DEVICE_NAME_LEN +static PROGMEM int usbDescrString2[] = { + USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), + USB_CFG_DEVICE_NAME +}; +#endif + +/* We don't use prog_int or prog_int16_t for compatibility with various libc + * versions. Here's an other compatibility hack: + */ +#ifndef PRG_RDB +#define PRG_RDB(addr) pgm_read_byte(addr) +#endif + +typedef union{ + unsigned word; + uchar *ptr; + uchar bytes[2]; +}converter_t; +/* We use this union to do type conversions. This is better optimized than + * type casts in gcc 3.4.3 and much better than using bit shifts to build + * ints from chars. Byte ordering is not a problem on an 8 bit platform. + */ + +/* ------------------------------------------------------------------------- */ + +#if USB_CFG_HAVE_INTRIN_ENDPOINT +static uchar usbTxPacketCnt1; +#if USB_CFG_IMPLEMENT_HALT +static uchar usbHalted1; /* not 0 if endpoint 1 is halted */ +#endif + +void usbSetInterrupt(uchar *data, uchar len) +{ +uchar *p, i; + +#if USB_CFG_IMPLEMENT_HALT + if(usbHalted1) + return; +#endif + if(len > 8) /* interrupt transfers are limited to 8 bytes */ + len = 8; + i = USBPID_DATA1; + if(usbTxPacketCnt1 & 1) + i = USBPID_DATA0; + if(usbTxLen1 < 0){ /* packet buffer was empty */ + usbTxPacketCnt1++; + }else{ + usbTxLen1 = -1; /* avoid sending incomplete interrupt data */ + } + p = usbTxBuf1; + *p++ = i; + for(i=len;i--;) + *p++ = *data++; + usbCrc16Append(&usbTxBuf1[1], len); + usbTxLen1 = len + 4; /* len must be given including sync byte */ +#if DEBUG_LEVEL > 1 + DBG2(0x21, usbTxBuf1, usbTxLen1-1); +#else + DBG1(0x21, usbTxBuf1 + 1, 2); +#endif +} +#endif + + +static uchar usbRead(uchar *data, uchar len) +{ +#if USB_CFG_IMPLEMENT_FN_READ + if(usbMsgFlags & USB_FLG_USE_DEFAULT_RW){ +#endif + uchar i = len, *r = usbMsgPtr; + if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ + while(i--){ + uchar c = PRG_RDB(r); /* assign to char size variable to enforce byte ops */ +#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME + if ((usbDescrConfig + 18 + 7) == (void*)r) { + *data++ = rt_usbHidReportDescriptorSize; + } + else { + *data++ = c; + } +#else + *data++ = c; +#endif + + r++; + } + }else{ /* RAM data */ + while(i--) + *data++ = *r++; + } + usbMsgPtr = r; + return len; +#if USB_CFG_IMPLEMENT_FN_READ + }else{ + if(len != 0) /* don't bother app with 0 sized reads */ + return usbFunctionRead(data, len); + return 0; + } +#endif +} + +/* Don't make this function static to avoid inlining. + * The entire function would become too large and exceed the range of + * relative jumps. + * 2006-02-25: Either gcc 3.4.3 is better than the gcc used when the comment + * above was written, or other parts of the code have changed. We now get + * better results with an inlined function. Test condition: PowerSwitch code. + */ +static void usbProcessRx(uchar *data, uchar len) +{ +usbRequest_t *rq = (void *)data; +uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW; + + +/* We use if() cascades because the compare is done byte-wise while switch() + * is int-based. The if() cascades are therefore more efficient. + */ +#if DEBUG_LEVEL > 1 + DBG2(0x10 + (usbRxToken == (uchar)USBPID_SETUP), data, len); +#else + DBG1(0x10 + (usbRxToken == (uchar)USBPID_SETUP), data, 2); +#endif + if(usbRxToken == (uchar)USBPID_SETUP){ + if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */ + uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; + if(type == USBRQ_TYPE_STANDARD){ + uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */ + replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ + if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */ + uchar __attribute__((__unused__)) recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ +#if USB_CFG_IS_SELF_POWERED + if(recipient == USBRQ_RCPT_DEVICE) + replyData[0] = USB_CFG_IS_SELF_POWERED; +#endif +#if USB_CFG_HAVE_INTRIN_ENDPOINT && USB_CFG_IMPLEMENT_HALT + if(usbHalted1 && recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */ + replyData[0] = 1; +#endif + replyData[1] = 0; + replyLen = 2; + }else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */ + usbNewDeviceAddr = rq->wValue.bytes[0]; + }else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */ + flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW; + if(rq->wValue.bytes[1] == 1){ /* descriptor type requested */ +#ifndef USB_CFG_DEVICE_DESCRIPTOR_RUNTIME + replyLen = sizeof(usbDescrDevice); + replyData = (uchar *)usbDescrDevice; +#else + replyLen = rt_usbDeviceDescriptorSize; + replyData = (uchar *)rt_usbDeviceDescriptor; +#endif + }else if(rq->wValue.bytes[1] == 2){ + replyLen = sizeof(usbDescrConfig); + replyData = (uchar *)usbDescrConfig; + }else if(rq->wValue.bytes[1] == 3){ /* string descriptor */ + if(rq->wValue.bytes[0] == 0){ /* descriptor index */ + replyLen = sizeof(usbDescrString0); + replyData = (uchar *)usbDescrString0; +#if USB_CFG_VENDOR_NAME_LEN + }else if(rq->wValue.bytes[0] == 1){ + replyLen = sizeof(usbDescrString1); + replyData = (uchar *)usbDescrString1; +#endif +#if USB_CFG_DEVICE_NAME_LEN + }else if(rq->wValue.bytes[0] == 2){ + replyLen = sizeof(usbDescrString2); + replyData = (uchar *)usbDescrString2; +#endif +#if USB_CFG_SERIAL_NUMBER_LENGTH + }else if(rq->wValue.bytes[0] == 3){ + replyLen = 2 * USB_CFG_SERIAL_NUMBER_LENGTH + 2; + replyData = (uchar *)usbCfgSerialNumberStringDescriptor; +#endif + } + } +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH || defined(USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME) + else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */ + replyLen = 9; + replyData = (uchar *)usbDescrConfig + 18; + }else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */ +#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH + replyLen = USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH; + replyData = (uchar *)usbHidReportDescriptor; +#endif +#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME + replyLen = rt_usbHidReportDescriptorSize; + replyData = (uchar *)rt_usbHidReportDescriptor; + //replyData = snes_usbHidReportDescriptor; +// if (replyData != (void*)0x86) { LED_ON(); } +// if (replyLen != 42) { LED_ON(); } +#endif + } +#endif + }else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */ + replyLen = 1; + replyData = &usbConfiguration; /* send current configuration value */ + }else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */ + usbConfiguration = rq->wValue.bytes[0]; +#if USB_CFG_IMPLEMENT_HALT + usbHalted1 = 0; +#endif + }else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */ + replyLen = 1; +#if USB_CFG_HAVE_INTRIN_ENDPOINT +#if USB_CFG_IMPLEMENT_HALT + }else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */ + if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */ + usbHalted1 = rq->bRequest - 1; + if(usbHalted1){ + usbTxBuf1[0] = USBPID_STALL; + usbTxLen1 = 2; /* length including sync byte */ + } + usbTxPacketCnt1 = 0; /* reset data toggling for interrupt endpoint */ + } +#endif + }else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */ + usbTxPacketCnt1 = 0; /* reset data toggling for interrupt endpoint */ +#if USB_CFG_IMPLEMENT_HALT + usbHalted1 = 0; +#endif +#endif + }else{ + /* the following requests can be ignored, send default reply */ + /* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ + /* 12: SYNCH_FRAME */ + } + usbMsgPtr = replyData; + if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* max length is in */ + replyLen = rq->wLength.bytes[0]; + }else{ /* not a standard request -- must be vendor or class request */ + replyLen = usbFunctionSetup(data); +#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE + if(replyLen == 0xff){ /* use user-supplied read/write function */ + if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){ + replyLen = rq->wLength.bytes[0]; /* IN transfers only */ + } + flags = 0; /* we have no valid msg, use user supplied read/write functions */ + } +#endif + } + } + /* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */ + }else{ /* DATA packet from out request */ +#if USB_CFG_IMPLEMENT_FN_WRITE + if(!(usbMsgFlags & USB_FLG_USE_DEFAULT_RW)){ + uchar rval = usbFunctionWrite(data, len); + replyLen = 0xff; + if(rval == 0xff){ /* an error occurred */ + /* usbMsgLen = 0xff; cancel potentially pending ACK [has been done by ASM module when OUT token arrived] */ + usbTxBuf[0] = USBPID_STALL; + usbTxLen = 2; /* length including sync byte */ + }else if(rval != 0){ /* This was the final package */ + replyLen = 0; /* answer with a zero-sized data packet */ + } + flags = 0; /* start with a DATA1 package, stay with user supplied write() function */ + } +#else + replyLen = 0; /* send zero-sized block as ACK */ +#endif + } + usbMsgFlags = flags; + usbMsgLen = replyLen; +} + +/* ------------------------------------------------------------------------- */ + +static void usbBuildTxBlock(void) +{ +uchar wantLen, len, txLen, token; + + wantLen = usbMsgLen; + if(wantLen > 8) + wantLen = 8; + usbMsgLen -= wantLen; + token = USBPID_DATA1; + if(usbMsgFlags & USB_FLG_TX_PACKET) + token = USBPID_DATA0; + usbMsgFlags++; + len = usbRead(usbTxBuf + 1, wantLen); + if(len <= 8){ /* valid data packet */ + usbCrc16Append(usbTxBuf + 1, len); + txLen = len + 4; /* length including sync byte */ + if(len < 8) /* a partial package identifies end of message */ + usbMsgLen = 0xff; + }else{ + token = USBPID_STALL; + txLen = 2; /* length including sync byte */ + usbMsgLen = 0xff; + } + usbTxBuf[0] = token; + usbTxLen = txLen; +#if DEBUG_LEVEL > 1 + DBG2(0x20, usbTxBuf, txLen-1); +#else + DBG1(0x20, usbTxBuf + 1, 2); +#endif +} + +static inline uchar isNotSE0(void) +{ +uchar rval; +/* We want to do + * return (USBIN & USBMASK); + * here, but the compiler does int-expansion acrobatics. + * We can avoid this by assigning to a char-sized variable. + */ + rval = USBIN & USBMASK; + return rval; +} + +/* ------------------------------------------------------------------------- */ + +void usbPoll(void) +{ +uchar len; + + if((len = usbRxLen) > 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((uchar *)(unsigned)(usbAppBuf + 1), usbRxLen - 3); + */ + len -= 3; /* remove PID and CRC */ + if(len < 128){ /* no overflow */ + converter_t appBuf; + appBuf.ptr = (uchar *)usbRxBuf; + appBuf.bytes[0] = usbAppBuf; + appBuf.bytes[0]++; + usbProcessRx(appBuf.ptr, len); + } + usbRxLen = 0; /* mark rx buffer as available */ + } + if(usbMsgLen != 0xff){ /* transmit data pending? */ + if(usbTxLen < 0) /* transmit system idle */ + usbBuildTxBlock(); + } + if(isNotSE0()){ /* SE0 state */ + usbIsReset = 0; + }else{ + /* check whether SE0 lasts for more than 2.5us (3.75 bit times) */ + if(!usbIsReset){ + uchar i; + for(i=100;i;i--){ + if(isNotSE0()) + goto notUsbReset; + } + usbIsReset = 1; + usbNewDeviceAddr = 0; + usbDeviceAddr = 0; +#if USB_CFG_IMPLEMENT_HALT + usbHalted1 = 0; +#endif + DBG1(0xff, 0, 0); +notUsbReset:; + } + } +} + +/* ------------------------------------------------------------------------- */ + +void usbInit(void) +{ + usbInputBuf = (uchar)usbRxBuf[0]; + usbAppBuf = (uchar)usbRxBuf[1]; +#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); +} + +/* ------------------------------------------------------------------------- */ diff --git a/usbdrv/usbdrv.h b/usbdrv/usbdrv.h new file mode 100644 index 0000000..c6be8a2 --- /dev/null +++ b/usbdrv/usbdrv.h @@ -0,0 +1,472 @@ +/* Name: usbdrv.h + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: usbdrv.h,v 1.1 2007-03-25 02:59:32 raph Exp $ + */ + +#ifndef __usbdrv_h_included__ +#define __usbdrv_h_included__ +#include "usbconfig.h" +#include "iarcompat.h" + +/* +Hardware Prerequisites: +======================= +USB lines D+ and D- MUST be wired to the same I/O port. Line D- MUST be wired +to bit number 0. D+ must also be connected to INT0. D- requires a pullup of +1.5k to +3.5V (and the device must be powered at 3.5V) to identify as +low-speed USB device. A pullup of 1M SHOULD be connected from D+ to +3.5V to +prevent interference when no USB master is connected. We use D+ as interrupt +source and not D- because it does not trigger on keep-alive and RESET states. + +As a compile time option, the 1.5k pullup resistor on D- can be made +switchable to allow the device to disconnect at will. See the definition of +usbDeviceConnect() and usbDeviceDisconnect() further down in this file. + +Please adapt the values in usbconfig.h according to your hardware! + +The device MUST be clocked at 12 MHz. This is more than the 10 MHz allowed by +an AT90S2313 powered at 4.5V. However, if the supply voltage to maximum clock +relation is interpolated linearly, an ATtiny2313 meets the requirement by +specification. In practice, the AT90S2313 can be overclocked and works well. + + +Limitations: +============ +Compiling: +You should link the usbdrv.o module first because it has special alignment +requirements for the receive buffer (the buffer must not cross a 256 byte +page boundary, it must not even touch it at the end). If you can't link it +first, you must use other measures to ensure alignment. +Note: gcc does not always assign variable addresses in the order as the modules +are linked or the variables are declared. You can choose a memory section for +the receive buffer with the configuration option "USB_BUFFER_SECTION". This +option defaults to ".bss". If you use your own section, you can place it at +an arbitrary location with a linker option similar to +"-Wl,--section-start=.mybuffer=0x800060". Use "avr-nm -ng" on the binary and +search for "usbRxBuf" to find tbe base address of the 22 bytes rx buffer. + +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. + +Sampling jitter: +The driver guarantees a sampling window of 1/2 bit. The USB spec requires +that the receiver has at most 1/4 bit sampling window. The 1/2 bit window +should still work reliably enough because we work at low speed. If you want +to meet the spec, define the macro "USB_CFG_SAMPLE_EXACT" to 1 in usbconfig.h. +This will unroll a loop which results in bigger code size. + +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 up to two endpoints: One control endpoint (endpoint 0) and +one interrupt-in endpoint (endpoint 1) where the device can send interrupt +data to the host. Endpoint 1 is only compiled in if +USB_CFG_HAVE_INTRIN_ENDPOINT is defined to 1 in usbconfig.h. + +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 wiring the INT1 pin to D+. It is not necessary +to enable the interrupt, checking the interrupt pending flag should suffice. +Before entering sleep mode, the application should enable INT1 for a wakeup +on the next bus activity. + +Operation without an USB master: +The driver behaves neutral without connection to an USB master if D- reads +as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) +pullup resistor on D+. If D- becomes statically 0, the driver may block in +the interrupt routine. + +Interrupt latency: +The application must ensure that the USB interrupt is not disabled for more +than 20 cycles. This implies that all interrupt routines must either be +declared as "INTERRUPT" instead of "SIGNAL" (see "avr/signal.h") or that they +are written in assembler with "sei" as the first instruction. + +Maximum interrupt duration / CPU cycle consumption: +The driver handles all USB communication during the interrupt service +routine. The routine will not return before an entire USB message is received +and the reply is sent. This may be up to ca. 1200 cycles = 100us if the host +conforms to the standard. The driver will consume CPU cycles for all USB +messages, even if they address another (low-speed) device on the same bus. + +*/ + +/* ------------------------------------------------------------------------- */ +/* --------------------------- Module Interface ---------------------------- */ +/* ------------------------------------------------------------------------- */ + +#define USBDRV_VERSION 20060314 +/* 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 __ASSEMBLER__ + +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef schar +#define schar signed char +#endif +/* shortcuts for well defined 8 bit integer types */ + +extern void usbInit(void); +/* This function must be called before interrupts are enabled and the main + * loop is entered. + */ +extern 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. + */ +extern uchar 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 0xff in 'usbFunctionSetup()'. The driver + * will then call 'usbFunctionRead()' when data is needed. See the + * documentation for usbFunctionRead() for details. + * + * If the SETUP indicates a control-out transfer, the only way to receive the + * data from the host is through the 'usbFunctionWrite()' call. If you + * implement this function, you must return 0xff in 'usbFunctionSetup()' to + * indicate that 'usbFunctionWrite()' should be used. See the documentation of + * this function for more information. If you just want to ignore the data sent + * by the host, return 0 in 'usbFunctionSetup()'. + * + * Note that calls to the functions usbFunctionRead() and usbFunctionWrite() + * are only done if enabled by the configuration in usbconfig.h. + */ +#if USB_CFG_HAVE_INTRIN_ENDPOINT +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. + */ +extern volatile schar usbTxLen1; +#define usbInterruptIsReady() (usbTxLen1 == -1) +/* 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. + */ +#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ +#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH +extern PROGMEM const 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 */ +#ifdef USB_CFG_DEVICE_DESCRIPTOR_RUNTIME +extern const char *rt_usbDeviceDescriptor; +extern uchar rt_usbDeviceDescriptorSize; +#endif +#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_RUNTIME +/* If you have many different HID report descriptors and + * you want to select one of them at runtime (dip switchs?), + * set those globals variables before initializing the driver. + */ +extern const char *rt_usbHidReportDescriptor; +extern uchar rt_usbHidReportDescriptorSize; +#endif + +#if USB_CFG_IMPLEMENT_FN_WRITE +extern 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 +extern 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 */ +#ifdef USB_CFG_PULLUP_IOPORT +#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 +#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/usbdrv/usbdrvasm.S b/usbdrv/usbdrvasm.S new file mode 100644 index 0000000..e6533f7 --- /dev/null +++ b/usbdrv/usbdrvasm.S @@ -0,0 +1,758 @@ +/* Name: usbdrvasm.S + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: usbdrvasm.S,v 1.1 2007-03-25 02:59:31 raph Exp $ + */ + +/* +General Description: +This module implements the assembler part of the USB driver. See usbdrv.h +for a description of the entire driver. +Since almost all of this code is timing critical, don't change unless you +really know what you are doing! Many parts require not only a maximum number +of CPU cycles, but even an exact number of cycles! +*/ + +#include "iarcompat.h" +#ifndef __IAR_SYSTEMS_ASM__ + /* configs for io.h */ +# define __SFR_OFFSET 0 +# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ +# include /* for CPU I/O register definitions and vectors */ +#endif /* __IAR_SYSTEMS_ASM__ */ +#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 + +/* Some assembler dependent definitions and declarations: */ + +#ifdef __IAR_SYSTEMS_ASM__ + +# define nop2 rjmp $+2 /* jump to next instruction */ +# define XL r26 +# define XH r27 +# define YL r28 +# define YH r29 +# define ZL r30 +# define ZH r31 +# define lo8(x) LOW(x) +# define hi8(x) ((x)>>8) /* not HIGH to allow XLINK to make a proper range check */ + + extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBuf + extern usbCurrentTok, usbRxLen, usbRxToken, usbAppBuf, usbTxLen + extern usbTxBuf, usbMsgLen, usbNakBuf, usbAckBuf, usbTxLen1, usbTxBuf1 + public usbCrc16 + public usbCrc16Append + +# ifndef IVT_BASE_ADDRESS +# define IVT_BASE_ADDRESS 0 +# endif + + ASEG + ORG INT0_vect + IVT_BASE_ADDRESS + rjmp SIG_INTERRUPT0 + RSEG CODE + +#else /* __IAR_SYSTEMS_ASM__ */ + +# define nop2 rjmp .+0 /* jump to next instruction */ + + .text + .global SIG_INTERRUPT0 + .type SIG_INTERRUPT0, @function + .global usbCrc16 + .global usbCrc16Append + +#endif /* __IAR_SYSTEMS_ASM__ */ + + +SIG_INTERRUPT0: +;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: 32 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), x1, SREG, x2, cnt, shift, YH, YL, x3, x4] = 11 bytes +usbInterrupt: +;order of registers pushed: +;x1, SREG, x2, cnt, shift, [YH, YL, x3] + push x1 ;2 push only what is necessary to sync with edge ASAP + in x1, SREG ;1 + push x1 ;2 +;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] +;sync up with J to K edge during sync pattern -- use fastest possible loops +;first part has no timeout because it waits for IDLE or SE1 (== disconnected) +#if !USB_CFG_SAMPLE_EXACT + ldi x1, 5 ;1 setup a timeout for waitForK +#endif +waitForJ: + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp waitForJ ;2 +#if USB_CFG_SAMPLE_EXACT +;The following code represents the unrolled loop in the else branch. It +;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 + nop + nop2 +foundK: +#else +waitForK: + dec x1 ;1 + sbic USBIN, USBMINUS ;1 wait for D- == 0 + brne waitForK ;2 +#endif +;{2, 6} 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: + push x2 ;2 + push cnt ;2 + push shift ;2 +shortcutEntry: + ldi cnt, 1 ;1 pre-init bit counter (-1 because no dec follows, -1 because 1 bit already sampled) + ldi x2, 1< 8 edge sync ended with D- == 0 +;now wait until SYNC byte is over. Wait for either 2 bits low (success) or 2 bits high (failure) +waitNoChange: + in x1, USBIN ;1 <-- sample, timing: edge + {2, 6} cycles + eor x2, x1 ;1 + sbrc x2, 0 ;1 | 2 + ldi cnt, 2 ;1 | 0 cnt = numBits - 1 (because dec follows) + mov x2, x1 ;1 + dec cnt ;1 + brne waitNoChange ;2 | 1 + sbrc x1, USBMINUS ;2 + rjmp sofError ;0 two consecutive "1" bits -> framing error +;start reading data, but don't check for bitstuffing because these are the +;first bits. Use the cycles for initialization instead. Note that we read and +;store the binary complement of the data stream because eor results in 1 for +;a change and 0 for no change. + in x1, USBIN ;1 <-- sample bit 0, timing: edge + {3, 7} cycles + eor x2, x1 ;1 + ror x2 ;1 + ldi shift, 0x7f ;1 The last bit of the sync pattern was a "no change" + ror shift ;1 + push YH ;2 -> 7 + in x2, USBIN ;1 <-- sample bit 1, timing: edge + {2, 6} cycles + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + push YL ;2 + lds YL, usbInputBuf ;2 -> 8 + in x1, USBIN ;1 <-- sample bit 2, timing: edge + {2, 6} cycles + eor x2, x1 ;1 + ror x2 ;1 + ror shift ;1 + ldi cnt, USB_BUFSIZE;1 + ldi YH, hi8(usbRxBuf);1 assume that usbRxBuf does not cross a page + push x3 ;2 -> 8 + in x2, USBIN ;1 <-- sample bit 3, timing: edge + {2, 6} cycles + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + ser x3 ;1 + nop ;1 + rjmp rxbit4 ;2 -> 8 + +shortcutToStart: ;{,43} into next frame: max 5.5 sync bits missed +#if !USB_CFG_SAMPLE_EXACT + ldi x1, 5 ;2 setup timeout +#endif +waitForJ1: + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp waitForJ1 ;2 +#if USB_CFG_SAMPLE_EXACT +;The following code represents the unrolled loop in the else branch. It +;results in a sampling window of 1/4 bit which meets the spec. + sbis USBIN, USBMINUS + rjmp foundK1 + sbis USBIN, USBMINUS + rjmp foundK1 + sbis USBIN, USBMINUS + rjmp foundK1 + nop + nop2 +foundK1: +#else +waitForK1: + dec x1 ;1 + sbic USBIN, USBMINUS ;1 wait for D- == 0 + brne waitForK1 ;2 +#endif + pop YH ;2 correct stack alignment + nop2 ;2 delay for the same time as the pushes in the original code + rjmp shortcutEntry ;2 + +; ################# receiver loop ################# +; extra jobs done during bit interval: +; bit 6: se0 check +; bit 7: or, store, clear +; bit 0: recover from delay [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: se0 check +; bit 3: overflow check +; bit 4: se0 check +; bit 5: rjmp + +; stuffed* helpers have the functionality of a subroutine, but we can't afford +; the overhead of a call. We therefore need a separate routine for each caller +; which jumps back appropriately. + +stuffed5: ;1 for branch taken + in x2, USBIN ;1 <-- sample @ +1 + andi x2, USBMASK ;1 + breq se0a ;1 + andi x3, 0xc0 ;1 (0xff03 >> 2) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit6 ;2 + +stuffed6: ;1 for branch taken + in x1, USBIN ;1 <-- sample @ +1 + andi x1, USBMASK ;1 + breq se0a ;1 + andi x3, 0x81 ;1 (0xff03 >> 1) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit7 ;2 + +; This is somewhat special because it has to compensate for the delay in bit 7 +stuffed7: ;1 for branch taken + andi x1, USBMASK ;1 already sampled by caller + breq se0a ;1 + mov x2, x1 ;1 ensure correct NRZI sequence [we can save andi x3 here] + ori shift, 0xfc ;1 + in x1, USBIN ;1 <-- sample bit 0 + rjmp unstuffed7 ;2 + +stuffed0: ;1 for branch taken + in x1, USBIN ;1 <-- sample @ +1 + andi x1, USBMASK ;1 + breq se0a ;1 + andi x3, 0xfe ;1 (0xff03 >> 7) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit1 ;2 + +;----------------------------- +rxLoop: + brlo stuffed5 ;1 +rxbit6: + in x1, USBIN ;1 <-- sample bit 6 + andi x1, USBMASK ;1 + breq se0a ;1 + eor x2, x1 ;1 + ror x2 ;1 + ror shift ;1 + cpi shift, 4 ;1 + brlo stuffed6 ;1 +rxbit7: + in x2, USBIN ;1 <-- sample bit 7 + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + eor x3, shift ;1 x3 is 0 at bit locations we changed, 1 at others + st y+, x3 ;2 the eor above reconstructed modified bits and inverted rx data + ser x3 ;1 +rxbit0: + in x1, USBIN ;1 <-- sample bit 0 + cpi shift, 4 ;1 + brlo stuffed7 ;1 +unstuffed7: + eor x2, x1 ;1 + ror x2 ;1 + ror shift ;1 + cpi shift, 4 ;1 + brlo stuffed0 ;1 +rxbit1: + in x2, USBIN ;1 <-- sample bit 1 + andi x2, USBMASK ;1 +se0a: ; enlarge jump range to SE0 + breq se0 ;1 check for SE0 more often close to start of byte + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + cpi shift, 4 ;1 + brlo stuffed1 ;1 +rxbit2: + in x1, USBIN ;1 <-- sample bit 2 + andi x1, USBMASK ;1 + breq se0 ;1 + eor x2, x1 ;1 + ror x2 ;1 + ror shift ;1 + cpi shift, 4 ;1 + brlo stuffed2 ;1 +rxbit3: + in x2, USBIN ;1 <-- sample bit 3 + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + dec cnt ;1 check for buffer overflow + breq overflow ;1 + cpi shift, 4 ;1 + brlo stuffed3 ;1 +rxbit4: + in x1, USBIN ;1 <-- sample bit 4 + andi x1, USBMASK ;1 + breq se0 ;1 + eor x2, x1 ;1 + ror x2 ;1 + ror shift ;1 + cpi shift, 4 ;1 + brlo stuffed4 ;1 +rxbit5: + in x2, USBIN ;1 <-- sample bit 5 + eor x1, x2 ;1 + ror x1 ;1 + ror shift ;1 + cpi shift, 4 ;1 + rjmp rxLoop ;2 +;----------------------------- + +stuffed1: ;1 for branch taken + in x2, USBIN ;1 <-- sample @ +1 + andi x2, USBMASK ;1 + breq se0 ;1 + andi x3, 0xfc ;1 (0xff03 >> 6) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit2 ;2 + +stuffed2: ;1 for branch taken + in x1, USBIN ;1 <-- sample @ +1 + andi x1, USBMASK ;1 + breq se0 ;1 + andi x3, 0xf8 ;1 (0xff03 >> 5) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit3 ;2 + +stuffed3: ;1 for branch taken + in x2, USBIN ;1 <-- sample @ +1 + andi x2, USBMASK ;1 + breq se0 ;1 + andi x3, 0xf0 ;1 (0xff03 >> 4) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit4 ;2 + +stuffed4: ;1 for branch taken + in x1, USBIN ;1 <-- sample @ +1 + andi x1, USBMASK ;1 + breq se0 ;1 + andi x3, 0xe0 ;1 (0xff03 >> 3) & 0xff + ori shift, 0xfc ;1 + rjmp rxbit5 ;2 + +;################ end receiver loop ############### + +overflow: ; ignore package if buffer overflow + rjmp rxDoReturn ; enlarge jump range + +;This is the only non-error exit point for the software receiver loop +;{4, 20} cycles after start of SE0, typically {10, 18} after SE0 start = {-6, 2} from end of SE0 +;next sync starts {16,} cycles after SE0 -> worst case start: +4 from next sync start +;we don't check any CRCs here because there is no time left. +se0: ;{-6, 2} from end of SE0 / {,4} into next frame + mov cnt, YL ;1 assume buffer in lower 256 bytes of memory + lds YL, usbInputBuf ;2 reposition to buffer start + sub cnt, YL ;1 length of message + ldi x1, 1< 19 = {13, 21} from SE0 end + cpi x1, USBPID_OUT ;1 + breq isSetupOrOut ;2 -> 22 = {16, 24} from SE0 end / {,24} into next frame + cpi x1, USBPID_IN ;1 + breq handleIn ;1 +#define USB_DATA_MASK ~(USBPID_DATA0 ^ USBPID_DATA1) + andi x1, USB_DATA_MASK ;1 + cpi x1, USBPID_DATA0 & USB_DATA_MASK ;1 + brne rxDoReturn ;1 not a data PID -- ignore +isData: + lds x2, usbCurrentTok ;2 + tst x2 ;1 + breq rxDoReturn ;1 for other device or spontaneous data -- ignore + lds x1, usbRxLen ;2 + cpi x1, 0 ;1 + brne sendNakAndReti ;1 no buffer space available / {30, 38} from SE0 end +; 2006-03-11: The following two lines fix a problem where the device was not +; recognized if usbPoll() was called less frequently than once every 4 ms. + cpi cnt, 4 ;1 zero sized data packets are status phase only -- ignore and ack + brmi sendAckAndReti ;1 keep rx buffer clean -- we must not NAK next SETUP + sts usbRxLen, cnt ;2 store received data, swap buffers + sts usbRxToken, x2 ;2 + lds x1, usbAppBuf ;2 + sts usbAppBuf, YL ;2 + sts usbInputBuf, x1 ;2 buffers now swapped + rjmp sendAckAndReti ;2 -> {43, 51} from SE0 end + +handleIn: ; {18, 26} from SE0 end + cp x2, shift ;1 shift contains our device addr + brne rxDoReturn ;1 other device +#if USB_CFG_HAVE_INTRIN_ENDPOINT + sbrc x3, 7 ;2 x3 contains addr + endpoint + rjmp handleIn1 ;0 +#endif + lds cnt, usbTxLen ;2 + cpi cnt, -1 ;1 + breq sendNakAndReti ;1 -> {27, 35} from SE0 end + ldi x1, -1 ;1 + sts usbTxLen, x1 ;2 buffer is now free + ldi YL, lo8(usbTxBuf) ;1 + ldi YH, hi8(usbTxBuf) ;1 + rjmp usbSendAndReti ;2 -> {34, 43} from SE0 end + +; Comment about when to set usbTxLen to -1: +; We should set it back to -1 when we receive the ACK from the host. This would +; be simple to implement: One static variable which stores whether the last +; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the +; ACK. However, we set it back to -1 immediately when we send the package, +; assuming that no error occurs and the host sends an ACK. We save one byte +; RAM this way and avoid potential problems with endless retries. The rest of +; the driver assumes error-free transfers anyway. + +otherOutOrSetup: + clr x1 + sts usbCurrentTok, x1 +rxDoReturn: + pop x3 ;2 + pop YL ;2 + pop YH ;2 + rjmp sofError ;2 + +isSetupOrOut: ; we must be fast here -- a data package may follow / {,24} into next frame + cp x2, shift ;1 shift contains our device addr + brne otherOutOrSetup ;1 other device -- ignore + sts usbCurrentTok, x1 ;2 +#if 0 /* we implement only one rx endpoint */ + sts usbRxEndp, x3 ;2 only stored if we may have to distinguish endpoints +#endif +;A transmission can still have data in the output buffer while we receive a +;SETUP package with an IN phase. To avoid that the old data is sent as a reply, +;we abort transmission. ### This mechanism assumes that NO OUT OR SETUP package +;is ever sent to endpoint 1. We would abort transmission for endpoint 0 +;in this case. + ldi x1, -1 ;1 + sts usbMsgLen, x1 ;2 + sts usbTxLen, x1 ;2 abort transmission + pop x3 ;2 + pop YL ;2 + in x1, USB_INTR_PENDING;1 + sbrc x1, USB_INTR_PENDING_BIT;1 check whether data is already arriving {,41} into next frame + rjmp shortcutToStart ;2 save the pops and pushes -- a new interrupt is aready pending +;If the jump above was not taken, we can be at {,2} into the next frame here + pop YH ;2 +txDoReturn: +sofError: ; error in start of frame -- ignore frame + ldi x1, 1< {,21} into next frame -> up to 3 sync bits missed + + +sendNakAndReti: ; 21 cycles until SOP + ldi YL, lo8(usbNakBuf) ;1 + ldi YH, hi8(usbNakBuf) ;1 + rjmp usbSendToken ;2 + +sendAckAndReti: ; 19 cycles until SOP + ldi YL, lo8(usbAckBuf) ;1 + ldi YH, hi8(usbAckBuf) ;1 +usbSendToken: + ldi cnt, 2 ;1 +;;;;rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x4, shift, cnt, Y +usbSendAndReti: ; SOP starts 16 cycles after call + push x4 ;2 + in x1, USBOUT ;1 + cbr x1, USBMASK ;1 mask out data bits + ori x1, USBIDLE ;1 idle + out USBOUT, x1 ;1 prepare idle state + ldi x4, USBMASK ;1 exor mask + in x2, USBDDR ;1 + ori x2, USBMASK ;1 set both pins to output + out USBDDR, x2 ;1 <-- acquire bus now +; need not init x2 (bitstuff history) because sync starts with 0 + ldi shift, 0x80 ;1 sync byte is first byte sent + rjmp txLoop ;2 -> 13 + 3 = 16 cycles until SOP + +#if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */ +handleIn1: + lds cnt, usbTxLen1 + cpi cnt, -1 + breq sendNakAndReti + ldi x1, -1 + sts usbTxLen1, x1 + ldi YL, lo8(usbTxBuf1) + ldi YH, hi8(usbTxBuf1) + rjmp usbSendAndReti +#endif + +bitstuff0: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + out USBOUT, x1 ;1 <-- out + rjmp didStuff0 ;2 branch back 2 cycles earlier +bitstuff1: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + sec ;1 set carry so that brsh will not jump + out USBOUT, x1 ;1 <-- out + rjmp didStuff1 ;2 jump back 1 cycle earler +bitstuff2: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + rjmp didStuff2 ;2 jump back 3 cycles earlier and do out +bitstuff3: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + rjmp didStuff3 ;2 jump back earlier + +txLoop: + sbrs shift, 0 ;1 + eor x1, x4 ;1 + out USBOUT, x1 ;1 <-- out + ror shift ;1 + ror x2 ;1 +didStuff0: + cpi x2, 0xfc ;1 + brsh bitstuff0 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + out USBOUT, x1 ;1 <-- out + ror x2 ;1 + cpi x2, 0xfc ;1 +didStuff1: + brsh bitstuff1 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + ror x2 ;1 +didStuff2: + out USBOUT, x1 ;1 <-- out + cpi x2, 0xfc ;1 + brsh bitstuff2 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + ror x2 ;1 +didStuff3: + cpi x2, 0xfc ;1 + out USBOUT, x1 ;1 <-- out + brsh bitstuff3 ;1 + nop2 ;2 + ld x3, y+ ;2 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + out USBOUT, x1 ;1 <-- out + ror shift ;1 + ror x2 ;1 +didStuff4: + cpi x2, 0xfc ;1 + brsh bitstuff4 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + out USBOUT, x1 ;1 <-- out + ror x2 ;1 + cpi x2, 0xfc ;1 +didStuff5: + brsh bitstuff5 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + ror x2 ;1 +didStuff6: + out USBOUT, x1 ;1 <-- out + cpi x2, 0xfc ;1 + brsh bitstuff6 ;1 + sbrs shift, 0 ;1 + eor x1, x4 ;1 + ror shift ;1 + ror x2 ;1 +didStuff7: + cpi x2, 0xfc ;1 + out USBOUT, x1 ;1 <-- out + brsh bitstuff7 ;1 + mov shift, x3 ;1 + dec cnt ;1 + brne txLoop ;2 | 1 + cbr x1, USBMASK ;1 prepare SE0 [spec says EOP may be 15 to 18 cycles] + pop x4 ;2 + out USBOUT, x1 ;1 <-- out SE0 -- from now 2 bits = 16 cycles until bus idle + ldi cnt, 2 ;| takes cnt * 3 cycles +se0Delay: ;| + dec cnt ;| + brne se0Delay ;| -> 2 * 3 = 6 cycles +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: + lds x2, usbNewDeviceAddr ;2 + subi YL, lo8(usbNakBuf + 2) ;1 + sbci YH, hi8(usbNakBuf + 2) ;1 + breq skipAddrAssign ;2 + sts usbDeviceAddr, x2 ;0 if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ori x1, USBIDLE ;1 + in x2, USBDDR ;1 + cbr x2, USBMASK ;1 set both pins to input + out USBOUT, x1 ;1 <-- out J (idle) -- end of SE0 (EOP signal) + cbr x1, USBMASK ;1 configure no pullup on both pins + pop x3 ;2 + pop YL ;2 + out USBDDR, x2 ;1 <-- release bus now + out USBOUT, x1 ;1 set pullup state + pop YH ;2 + rjmp txDoReturn ;2 [we want to jump to rxDoReturn, but this saves cycles] + + +bitstuff4: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + out USBOUT, x1 ;1 <-- out + rjmp didStuff4 ;2 jump back 2 cycles earlier +bitstuff5: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + sec ;1 set carry so that brsh is not taken + out USBOUT, x1 ;1 <-- out + rjmp didStuff5 ;2 jump back 1 cycle earlier +bitstuff6: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + rjmp didStuff6 ;2 jump back 3 cycles earlier and do out there +bitstuff7: ;1 (for branch taken) + eor x1, x4 ;1 + ldi x2, 0 ;1 + rjmp didStuff7 ;2 jump back 4 cycles earlier + +; ######################## 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 + */ +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 + +; extern unsigned usbCrc16(unsigned char *data, unsigned char len); +; data: r24/25 +; len: r22 +; temp variables: +; r18: data byte +; r19: bit counter +; r20/21: polynomial +; r23: scratch +; r24/25: crc-sum +; r26/27=X: ptr +usbCrc16: + mov ptrL, argPtrL + mov ptrH, argPtrH + ldi resCrcL, 0xff + ldi resCrcH, 0xff + ldi polyL, lo8(0xa001) + ldi polyH, hi8(0xa001) +crcByteLoop: + subi argLen, 1 + brcs crcReady + ld byte, ptr+ + ldi bitCnt, 8 +crcBitLoop: + mov scratch, byte + eor scratch, resCrcL + lsr resCrcH + ror resCrcL + lsr byte + sbrs scratch, 0 + rjmp crcNoXor + eor resCrcL, polyL + eor resCrcH, polyH +crcNoXor: + dec bitCnt + brne crcBitLoop + rjmp crcByteLoop +crcReady: + com resCrcL + com resCrcH + ret + +; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); +usbCrc16Append: + rcall usbCrc16 + st ptr+, resCrcL + st ptr+, resCrcH + ret diff --git a/usbdrv/usbdrvasm.asm b/usbdrv/usbdrvasm.asm new file mode 100644 index 0000000..713336d --- /dev/null +++ b/usbdrv/usbdrvasm.asm @@ -0,0 +1,21 @@ +/* Name: usbdrvasm.asm + * Project: AVR USB driver + * Author: Christian Starkjohann + * Creation Date: 2006-03-01 + * Tabsize: 4 + * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH + * License: Proprietary, free under certain conditions. See Documentation. + * This Revision: $Id: usbdrvasm.asm,v 1.1 2007-03-25 02:59:31 raph Exp $ + */ + +/* +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