1
0
mirror of https://github.com/raphnet/4nes4snes synced 2024-12-21 22:48:51 -05:00

update driver

This commit is contained in:
Raphaël Assénat 2009-05-02 12:41:40 +00:00
parent 4c4ad386e5
commit 958c62b658
11 changed files with 848 additions and 1130 deletions

View File

@ -81,3 +81,88 @@ Scroll down to the bottom to see the most recent changes.
with a zero length.
* Release 2006-03-14
- Improved IAR C support: tiny memory model, more devices
- Added template usbconfig.h file under the name usbconfig-prototype.h
* Release 2006-03-26
- Added provision for one more interrupt-in endpoint (endpoint 3).
- Added provision for one interrupt-out endpoint (endpoint 1).
- Added flowcontrol macros for USB.
- Added provision for custom configuration descriptor.
- Allow ANY two port bits for D+ and D-.
- Merged (optional) receive endpoint number into global usbRxToken variable.
- Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the
variable name from the single port letter instead of computing the address
of related ports from the output-port address.
* Release 2006-06-26
- Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the
new features.
- Removed "#warning" directives because IAR does not understand them. Use
unused static variables instead to generate a warning.
- Do not include <avr/io.h> when compiling with IAR.
- Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each
USB descriptor should be handled. It is now possible to provide descriptor
data in Flash, RAM or dynamically at runtime.
- STALL is now a status in usbTxLen* instead of a message. We can now conform
to the spec and leave the stall status pending until it is cleared.
- Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the
application code to reset data toggling on interrupt pipes.
* Release 2006-07-18
- Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes
an assembler error.
- usbDeviceDisconnect() takes pull-up resistor to high impedance now.
* Release 2007-02-01
- Merged in some code size improvements from usbtiny (thanks to Dick
Streefland for these optimizations!)
- Special alignment requirement for usbRxBuf not required any more. Thanks
again to Dick Streefland for this hint!
- Reverted to "#warning" instead of unused static variables -- new versions
of IAR CC should handle this directive.
- Changed Open Source license to GNU GPL v2 in order to make linking against
other free libraries easier. We no longer require publication of the
circuit diagrams, but we STRONGLY encourage it. If you improve the driver
itself, PLEASE grant us a royalty free license to your changes for our
commercial license.
* Release 2007-03-29
- New configuration option "USB_PUBLIC" in usbconfig.h.
- Set USB version number to 1.10 instead of 1.01.
- Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and
USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences
to USB_CFG_DESCR_PROPS_STRING_PRODUCT.
- New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver
code.
- New assembler module for 16 MHz crystal.
- usbdrvasm.S contains common code only, clock-specific parts have been moved
to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively.
* Release 2007-06-25
- 16 MHz module: Do SE0 check in stuffed bits as well.
* Release 2007-07-07
- Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary
for negative values.
- Added 15 MHz module contributed by V. Bosch.
- Interrupt vector name can now be configured. This is useful if somebody
wants to use a different hardware interrupt than INT0.
* Release 2007-08-07
- Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is
not exceeded.
- More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN,
USB_COUNT_SOF
- USB_INTR_PENDING can now be a memory address
* Release 2007-09-19

View File

@ -1,121 +1,22 @@
PREFACE
OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the
terms and conditions of the GNU GPL version 2, see the text below. In addition
to the requirements in the GPL, we STRONGLY ENCOURAGE you to do the following:
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.
(1) Publish your entire project on a web site and drop us a note with the URL.
Use the form at http://www.obdev.at/avrusb/feedback.html for your submission.
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:
(2) Adhere to minimum publication standards. Please include AT LEAST:
- a circuit diagram in PDF, PNG or GIF format
- full source code for the host software
- a Readme.txt file in ASCII format which describes the purpose of the
project and what can be found in which directories and which files
- a reference to http://www.obdev.at/avrusb/
(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:
(3) If you improve the driver firmware itself, please give us a free license
to your modifications for our commercial license offerings.
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

View File

@ -3,13 +3,82 @@ 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".
project and add your own version of "usbconfig.h". A template for your own
"usbconfig.h" can be found in "usbconfig-prototype.h" in this directory.
TECHNICAL DOCUMENTATION
=======================
The technical documentation for the firmware driver is contained in the file
"usbdrv.h". Please read all of it carefully!
The technical documentation (API) for the firmware driver is contained in the
file "usbdrv.h". Please read all of it carefully! Configuration options are
documented in "usbconfig-prototype.h".
The driver consists of the following files:
Readme.txt ............. The file you are currently reading.
Changelog.txt .......... Release notes for all versions of the driver.
usbdrv.h ............... Driver interface definitions and technical docs.
* usbdrv.c ............... High level language part of the driver. Link this
module to your code!
* usbdrvasm.S ............ Assembler part of the driver. This module is mostly
a stub and includes one of the usbdrvasm*.S files
depending on processor clock. Link this module to
your code!
usbdrvasm12.S .......... 12 MHz version of the assembler routines. Included
by usbdrvasm.S, don't link it directly!
usbdrvasm16.S .......... 16 MHz version of the assembler routines. Included
by usbdrvasm.S, don't link it directly!
usbdrvasm165.S ......... 16.5 MHz version of the assembler routines including
a PLL so that an 1% accurate RC oscillator can be
used. Included by usbdrvasm.S, don't link directly!
usbconfig-prototype.h .. Prototype for your own usbdrv.h file.
* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is
defined to a value greater than 0. Link this module
to your code!
oddebug.h .............. Interface definitions of the debug module.
iarcompat.h ............ Compatibility definitions for IAR C-compiler.
usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this
module instead of usbdrvasm.S when you assembler
with IAR's tools.
License.txt ............ Open Source license for this driver.
CommercialLicense.txt .. Optional commercial license for this driver.
USBID-License.txt ...... Terms and conditions for using particular USB ID
values for particular purposes.
(*) ... These files should be linked to your project.
CPU CORE CLOCK FREQUENCY
========================
We supply assembler modules for clock frequencies of 12 MHz, 16 MHz and
16.5 MHz. Other clock rates are not supported. The actual clock rate must be
configured in usbdrv.h unless you use the default 12 MHz.
12 MHz Clock
This is the traditional clock rate of AVR-USB because it's the lowest clock
rate where the timing constraints of the USB spec can be met.
16 MHz Clock
This clock rate has been added for users of the Arduino board and other
ready-made boards which come with a fixed 16 MHz crystal. It's also an option
if you need the slightly higher clock rate for performance reasons. Since
16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code
is somewhat tricky and has to insert a leap cycle every third byte.
16.5 MHz Clock
The assembler module for this clock rate differs from the other modules because
it has been built for an RC oscillator with only 1% precision. The receiver
code inserts leap cycles to compensate for clock deviations. 1% is also the
precision which can be achieved by calibrating the internal RC oscillator of
the AVR. Please note that only AVRs with internal 64 MHz PLL oscillator can be
used since the 8 MHz RC oscillator cannot be trimmed up to 16.5 MHz. This
includes the very popular ATTiny25, ATTiny45, ATTiny85 series as well as the
ATTiny26.
We recommend that you obtain appropriate calibration values for 16.5 MHz core
clock at programming time and store it in flash or EEPROM or compute the value
from a reference clock at run time. However, since Atmel's 8 MHz calibration
is much more precise than the guaranteed 10%, it's usually possible to add a
fixed offset to this value.
USB IDENTIFIERS
@ -26,6 +95,9 @@ 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.
Objective Development also has some offerings which include product IDs. See
http://www.obdev.at/avrusb/ for details.
HOST DRIVER
===========
@ -44,44 +116,37 @@ This driver has been developed and optimized for the GNU compiler version 3
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.
with the "small" and "tiny" memory model. Please note that gcc is more
efficient for usbdrv.c because this module has been deliberately optimized
for gcc.
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:
The AVR firmware driver is published under the GNU General Public License
Version 2 (GPL2). See the file "License.txt" for details.
(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.
If you decide for the free GPL2, we STRONGLY ENCOURAGE you to do the following
things IN ADDITION to the obligations from the GPL2:
(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.
(1) Publish your entire project on a web site and drop us a note with the URL.
Use the form at http://www.obdev.at/avrusb/feedback.html for your submission.
(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.
(2) Adhere to minimum publication standards. Please include AT LEAST:
- a circuit diagram in PDF, PNG or GIF format
- full source code for the host software
- a Readme.txt file in ASCII format which describes the purpose of the
project and what can be found in which directories and which files
- a reference to http://www.obdev.at/avrusb/
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.
(3) If you improve the driver firmware itself, please give us a free license
to your modifications for our commercial license offerings.
COMMERCIAL LICENSES FOR AVR-USB
===============================
If you don't want to publish your source code 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.
If you don't want to publish your source code under the terms of the GPL2,
you can simply pay money for AVR-USB. As an additional benefit you get
USB PIDs for free, licensed exclusively to you. See the file
"CommercialLicense.txt" for details.

View File

@ -1,10 +1,10 @@
Royalty-Free Non-Exclusive License USB Product-ID
=================================================
Version 2006-02-20
Version 2006-06-19
OBJECTIVE DEVELOPMENT Software GmbH hereby grants you the non-exclusive
right to use two USB.org vendor-ID (VID) / product-ID (PID) pairs with
right to use three USB.org vendor-ID (VID) / product-ID (PID) pairs with
products based on Objective Development's firmware-only USB driver for
Atmel AVR microcontrollers:
@ -16,6 +16,9 @@ Atmel AVR microcontrollers:
(excluding mice and keyboards). Devices using this pair will be referred
to as "HID CLASS" devices.
* VID = 5824 (=0x16c0) / PID = 1505 (=0x5e1) for CDC class modem devices
Devices using this pair will be referred to as "CDC-ACM 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
@ -52,15 +55,16 @@ 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.
MUST be a comparison of the entire strings, NOT a sub-string match. For
CDC-ACM CLASS devices, a generic class driver should be used and the
matching is based on the USB device class.
(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.
required 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
@ -68,6 +72,9 @@ pair used:
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.
CDC-ACM CLASS devices require a ".inf" file which matches on the VID/PID
pair. This ".inf" file MUST load the "usbser" driver to configure the
device as modem (COM-port).
(7) OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any
problems which are caused by the shared use of these VID/PID pairs. You
@ -78,6 +85,10 @@ you want to avoid them, get your own VID/PID pair for exclusive use.
HOW TO IMPLEMENT THESE RULES
============================
The following rules are for VENDOR CLASS and HID CLASS devices. CDC-ACM
CLASS devices use the operating system's class driver and don't need a
custom driver.
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

View File

@ -4,8 +4,8 @@
* 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 $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: iarcompat.h,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
/*
@ -58,6 +58,13 @@ Thanks to Oleg Semyonov for his help with the IAR tools port!
#define sei() __enable_interrupt()
#define wdt_reset() __watchdog_reset()
/* Depending on the device you use, you may get problems with the way usbdrv.h
* handles the differences between devices. Since IAR does not use #defines
* for MCU registers, we can't check for the existence of a particular
* register with an #ifdef. If the autodetection mechanism fails, include
* definitions for the required USB_INTR_* macros in your usbconfig.h. See
* usbconfig-prototype.h and usbdrv.h for details.
*/
#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */
#endif /* __iarcompat_h_INCLUDED__ */

View File

@ -4,19 +4,15 @@
* 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 $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.c,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
#include "iarcompat.h"
#ifndef __IAR_SYSTEMS_ICC__
# include <avr/io.h>
#endif
#include "oddebug.h"
#if DEBUG_LEVEL > 0
#warning "Debugging is turned on! Never compile production devices with debugging!"
#warning "Never compile production devices with debugging enabled"
static void uartPutc(char c)
{

View File

@ -4,8 +4,8 @@
* 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 $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.h,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
#ifndef __oddebug_h_included__
@ -28,6 +28,12 @@ the output and a memory block to dump in hex ('data' and 'len').
# define F_CPU 12000000 /* 12 MHz */
#endif
/* make sure we have the UART defines: */
#include "iarcompat.h"
#ifndef __IAR_SYSTEMS_ICC__
# include <avr/io.h>
#endif
#ifndef uchar
# define uchar unsigned char
#endif

View File

@ -4,8 +4,8 @@
* 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.c,v 1.1 2007-03-25 02:59:32 raph Exp $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbdrv.c,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
#include "iarcompat.h"
@ -15,7 +15,7 @@
#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
@ -33,49 +33,38 @@ documentation of the entire driver.
/* ------------------------------------------------------------------------- */
/* 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 usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */
uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */
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 */
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */
uchar usbCurrentTok; /* last token received, if more than 1 rx endpoint: MSb=endpoint */
uchar usbRxToken; /* token for data we received; if more than 1 rx endpoint: MSb=endpoint */
uchar usbMsgLen = 0xff; /* remaining number of bytes, no msg to send if -1 (see usbMsgPtr) */
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 */
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */
# if USB_COUNT_SOF
volatile uchar usbSofCount; /* incremented by assembler module every SOF */
# endif
#if USB_CFG_HAVE_INTRIN_ENDPOINT
volatile uchar usbTxLen1 = USBPID_NAK; /* TX count for endpoint 1 */
uchar usbTxBuf1[USB_BUFSIZE]; /* TX data for endpoint 1 */
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
volatile uchar usbTxLen3 = USBPID_NAK; /* TX count for endpoint 3 */
uchar usbTxBuf3[USB_BUFSIZE]; /* TX data for endpoint 3 */
#endif
#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
@ -86,11 +75,54 @@ optimizing hints:
/* ------------------------------------------------------------------------- */
#ifndef USB_CFG_DEVICE_DESCRIPTOR_RUNTIME
static PROGMEM char usbDescrDevice[] = { /* USB device descriptor */
18, /* sizeof(usbDescrDevice): length of descriptor in bytes */
#if USB_CFG_DESCR_PROPS_STRINGS == 0
#if USB_CFG_DESCR_PROPS_STRING_0 == 0
#undef USB_CFG_DESCR_PROPS_STRING_0
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0)
PROGMEM char usbDescriptorString0[] = { /* language descriptor */
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */
3, /* descriptor type */
0x09, 0x04, /* language index (0x0409 = US-English) */
};
#endif
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor)
PROGMEM int usbDescriptorStringVendor[] = {
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN),
USB_CFG_VENDOR_NAME
};
#endif
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice)
PROGMEM int usbDescriptorStringDevice[] = {
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN),
USB_CFG_DEVICE_NAME
};
#endif
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber)
PROGMEM int usbDescriptorStringSerialNumber[] = {
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN),
USB_CFG_SERIAL_NUMBER
};
#endif
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */
#if USB_CFG_DESCR_PROPS_DEVICE == 0
#undef USB_CFG_DESCR_PROPS_DEVICE
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice)
PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */
USBDESCR_DEVICE, /* descriptor type */
0x01, 0x01, /* USB version supported */
0x10, 0x01, /* USB version supported */
USB_CFG_DEVICE_CLASS,
USB_CFG_DEVICE_SUBCLASS,
0, /* protocol */
@ -98,33 +130,26 @@ static PROGMEM char usbDescrDevice[] = { /* USB device descriptor */
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
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */
1, /* number of configurations */
};
#endif
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
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0
#undef USB_CFG_DESCR_PROPS_HID
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */
#endif
), 0, /* total length of data returned (including inlined descriptors) */
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0
#undef USB_CFG_DESCR_PROPS_CONFIGURATION
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration)
PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */
USBDESCR_CONFIG, /* descriptor type */
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + (USB_CFG_DESCR_PROPS_HID & 0xff), 0,
/* 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 */
@ -144,19 +169,14 @@ static PROGMEM char usbDescrConfig[] = { /* USB configuration descriptor */
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 */
#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* 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 */
USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */
#endif
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */
7, /* sizeof(usbDescrEndpoint) */
@ -167,24 +187,6 @@ static PROGMEM char usbDescrConfig[] = { /* USB configuration descriptor */
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
@ -207,40 +209,48 @@ typedef union{
/* ------------------------------------------------------------------------- */
#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)
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len)
{
uchar *p, i;
#if USB_CFG_IMPLEMENT_HALT
if(usbHalted1)
if(usbTxLen1 == USBPID_STALL)
return;
#endif
#if 0 /* No runtime checks! Caller is responsible for valid data! */
if(len > 8) /* interrupt transfers are limited to 8 bytes */
len = 8;
i = USBPID_DATA1;
if(usbTxPacketCnt1 & 1)
i = USBPID_DATA0;
if(usbTxLen1 < 0){ /* packet buffer was empty */
usbTxPacketCnt1++;
#endif
if(usbTxLen1 & 0x10){ /* packet buffer was empty */
usbTxBuf1[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */
}else{
usbTxLen1 = -1; /* avoid sending incomplete interrupt data */
usbTxLen1 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */
}
p = usbTxBuf1;
*p++ = i;
p = usbTxBuf1 + 1;
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);
DBG2(0x21, usbTxBuf1, len + 3);
}
#endif
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len)
{
uchar *p, i;
if(usbTxLen3 & 0x10){ /* packet buffer was empty */
usbTxBuf3[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */
}else{
usbTxLen3 = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */
}
p = usbTxBuf3 + 1;
for(i=len;i--;)
*p++ = *data++;
usbCrc16Append(&usbTxBuf3[1], len);
usbTxLen3 = len + 4; /* len must be given including sync byte */
DBG2(0x23, usbTxBuf3, len + 3);
}
#endif
@ -254,17 +264,7 @@ static uchar usbRead(uchar *data, uchar len)
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 */
@ -282,6 +282,23 @@ static uchar usbRead(uchar *data, uchar len)
#endif
}
#define GET_DESCRIPTOR(cfgProp, staticName) \
if(cfgProp){ \
if((cfgProp) & USB_PROP_IS_RAM) \
flags &= ~USB_FLG_MSGPTR_IS_ROM; \
if((cfgProp) & USB_PROP_IS_DYNAMIC){ \
replyLen = usbFunctionDescriptor(rq); \
}else{ \
replyData = (uchar *)(staticName); \
SET_REPLY_LEN((cfgProp) & 0xff); \
} \
}
/* We use if() instead of #if in the macro above because #if can't be used
* in macros and the compiler optimizes constant conditions anyway.
*/
/* Don't make this function static to avoid inlining.
* The entire function would become too large and exceed the range of
* relative jumps.
@ -293,20 +310,32 @@ 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);
/* usbRxToken can be:
* 0x2d 00101101 (USBPID_SETUP for endpoint 0)
* 0xe1 11100001 (USBPID_OUT for endpoint 0)
* 0xff 11111111 (USBPID_OUT for endpoint 1)
*/
DBG2(0x10 + ((usbRxToken >> 1) & 3), data, len); /* SETUP0=12; OUT0=10; OUT1=13 */
#ifdef USB_RX_USER_HOOK
USB_RX_USER_HOOK(data, len)
#endif
#if USB_CFG_IMPLEMENT_FN_WRITEOUT
if(usbRxToken == 0xff){
usbFunctionWriteOut(data, len);
return; /* no reply expected, hence no usbMsgPtr, usbMsgFlags, usbMsgLen set */
}
#endif
if(usbRxToken == (uchar)USBPID_SETUP){
usbTxLen = USBPID_NAK; /* abort pending transmit */
if(len == 8){ /* Setup size must be always 8 bytes. Ignore otherwise. */
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK;
if(type == USBRQ_TYPE_STANDARD){
#define SET_REPLY_LEN(len) replyLen = (len); usbMsgPtr = replyData
/* This macro ensures that replyLen and usbMsgPtr are always set in the same way.
* That allows optimization of common code in if() branches */
uchar *replyData = usbTxBuf + 9; /* there is 3 bytes free space at the end of the buffer */
replyData[0] = 0; /* common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */
if(rq->bRequest == USBRQ_GET_STATUS){ /* 0 */
@ -316,91 +345,72 @@ uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW;
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;
if(recipient == USBRQ_RCPT_ENDPOINT && rq->wIndex.bytes[0] == 0x81) /* request status for endpoint 1 */
replyData[0] = usbTxLen1 == USBPID_STALL;
#endif
replyData[1] = 0;
replyLen = 2;
SET_REPLY_LEN(2);
}else if(rq->bRequest == USBRQ_SET_ADDRESS){ /* 5 */
usbNewDeviceAddr = rq->wValue.bytes[0];
}else if(rq->bRequest == USBRQ_GET_DESCRIPTOR){ /* 6 */
flags = USB_FLG_MSGPTR_IS_ROM | USB_FLG_USE_DEFAULT_RW;
if(rq->wValue.bytes[1] == 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[1] == USBDESCR_DEVICE){ /* 1 */
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice)
}else if(rq->wValue.bytes[1] == USBDESCR_CONFIG){ /* 2 */
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration)
}else if(rq->wValue.bytes[1] == USBDESCR_STRING){ /* 3 */
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM)
flags &= ~USB_FLG_MSGPTR_IS_ROM;
replyLen = usbFunctionDescriptor(rq);
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */
if(rq->wValue.bytes[0] == 0){ /* descriptor index */
replyLen = sizeof(usbDescrString0);
replyData = (uchar *)usbDescrString0;
#if USB_CFG_VENDOR_NAME_LEN
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0)
}else if(rq->wValue.bytes[0] == 1){
replyLen = sizeof(usbDescrString1);
replyData = (uchar *)usbDescrString1;
#endif
#if USB_CFG_DEVICE_NAME_LEN
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor)
}else if(rq->wValue.bytes[0] == 2){
replyLen = sizeof(usbDescrString2);
replyData = (uchar *)usbDescrString2;
#endif
#if USB_CFG_SERIAL_NUMBER_LENGTH
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice)
}else if(rq->wValue.bytes[0] == 3){
replyLen = 2 * USB_CFG_SERIAL_NUMBER_LENGTH + 2;
replyData = (uchar *)usbCfgSerialNumberStringDescriptor;
#endif
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber)
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){
replyLen = usbFunctionDescriptor(rq);
}
}
#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;
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */
#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */
}else if(rq->wValue.bytes[1] == USBDESCR_HID){ /* 0x21 */
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18)
}else if(rq->wValue.bytes[1] == USBDESCR_HID_REPORT){ /* 0x22 */
#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
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport)
#endif /* USB_CFG_DESCR_PROPS_HID_REPORT */
}else if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){
replyLen = usbFunctionDescriptor(rq);
}
#endif
}else if(rq->bRequest == USBRQ_GET_CONFIGURATION){ /* 8 */
replyLen = 1;
replyData = &usbConfiguration; /* send current configuration value */
SET_REPLY_LEN(1);
}else if(rq->bRequest == USBRQ_SET_CONFIGURATION){ /* 9 */
usbConfiguration = rq->wValue.bytes[0];
#if USB_CFG_IMPLEMENT_HALT
usbHalted1 = 0;
usbTxLen1 = USBPID_NAK;
#endif
}else if(rq->bRequest == USBRQ_GET_INTERFACE){ /* 10 */
replyLen = 1;
SET_REPLY_LEN(1);
#if USB_CFG_HAVE_INTRIN_ENDPOINT
}else if(rq->bRequest == USBRQ_SET_INTERFACE){ /* 11 */
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# endif
# if USB_CFG_IMPLEMENT_HALT
usbTxLen1 = USBPID_NAK;
}else if(rq->bRequest == USBRQ_CLEAR_FEATURE || rq->bRequest == USBRQ_SET_FEATURE){ /* 1|3 */
if(rq->wValue.bytes[0] == 0 && rq->wIndex.bytes[0] == 0x81){ /* feature 0 == HALT for endpoint == 1 */
usbHalted1 = rq->bRequest - 1;
if(usbHalted1){
usbTxBuf1[0] = USBPID_STALL;
usbTxLen1 = 2; /* length including sync byte */
}
usbTxPacketCnt1 = 0; /* reset data toggling for interrupt endpoint */
}
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL;
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# endif
}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{
@ -408,20 +418,20 @@ uchar replyLen = 0, flags = USB_FLG_USE_DEFAULT_RW;
/* 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];
#undef SET_REPLY_LEN
}else{ /* not a standard request -- must be vendor or class request */
replyLen = usbFunctionSetup(data);
}
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE
if(replyLen == 0xff){ /* use user-supplied read/write function */
if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_DEVICE_TO_HOST){
replyLen = rq->wLength.bytes[0]; /* IN transfers only */
}
flags = 0; /* we have no valid msg, use user supplied read/write functions */
}
flags &= ~USB_FLG_USE_DEFAULT_RW; /* we have no valid msg, use user supplied read/write functions */
}else /* The 'else' prevents that we limit a replyLen of 0xff to the maximum transfer len. */
#endif
}
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */
replyLen = rq->wLength.bytes[0];
}
/* make sure that data packets which are sent as ACK to an OUT transfer are always zero sized */
}else{ /* DATA packet from out request */
@ -430,16 +440,13 @@ uchar replyLen = 0, flags = 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 */
usbMsgLen = 0xff; /* cancel potentially pending data packet for ACK */
usbTxLen = USBPID_STALL;
}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;
@ -462,22 +469,17 @@ uchar wantLen, len, txLen, token;
usbMsgFlags++;
len = usbRead(usbTxBuf + 1, wantLen);
if(len <= 8){ /* valid data packet */
usbCrc16Append(usbTxBuf + 1, len);
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 */
txLen = USBPID_STALL; /* stall the endpoint */
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)
@ -494,59 +496,52 @@ uchar rval;
/* ------------------------------------------------------------------------- */
void usbPoll(void)
USB_PUBLIC void usbPoll(void)
{
uchar len;
schar len;
uchar i;
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);
* unsigned crc = usbCrc16(buffer + 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);
}
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len - 3);
#if USB_CFG_HAVE_FLOWCONTROL
if(usbRxLen > 0) /* only mark as available if not inactivated */
usbRxLen = 0;
#else
usbRxLen = 0; /* mark rx buffer as available */
#endif
}
if(usbTxLen & 0x10){ /* transmit system idle */
if(usbMsgLen != 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;
for(i = 10; i > 0; i--){
if(isNotSE0())
break;
}
if(i == 0){ /* RESET condition, called multiple times during reset */
usbNewDeviceAddr = 0;
usbDeviceAddr = 0;
#if USB_CFG_IMPLEMENT_HALT
usbHalted1 = 0;
usbTxLen1 = USBPID_NAK;
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
usbTxLen3 = USBPID_NAK;
#endif
#endif
DBG1(0xff, 0, 0);
notUsbReset:;
}
}
}
/* ------------------------------------------------------------------------- */
void usbInit(void)
USB_PUBLIC 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
@ -554,6 +549,12 @@ void usbInit(void)
USB_INTR_CFG &= ~(USB_INTR_CFG_CLR);
#endif
USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT);
#if USB_CFG_HAVE_INTRIN_ENDPOINT
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */
# endif
#endif
}
/* ------------------------------------------------------------------------- */

View File

@ -4,8 +4,8 @@
* 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 $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbdrv.h,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
#ifndef __usbdrv_h_included__
@ -16,12 +16,14 @@
/*
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.
USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+
triggers the interrupt (best achieved by using INT0 for D+), but it is also
possible to trigger the interrupt from D-. If D- is used, interrupts are also
triggered by SOF packets. D- requires a 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
@ -29,27 +31,12 @@ usbDeviceConnect() and usbDeviceDisconnect() further down in this file.
Please adapt the values in usbconfig.h according to your hardware!
The device MUST be clocked at 12 MHz. This is more than the 10 MHz allowed by
an AT90S2313 powered at 4.5V. However, if the supply voltage to maximum clock
relation is interpolated linearly, an ATtiny2313 meets the requirement by
specification. In practice, the AT90S2313 can be overclocked and works well.
The device MUST be clocked at exactly 12 MHz, 15 MHz or 16 MHz
or at 16.5 MHz +/- 1%. See usbconfig-prototype.h for details.
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,
@ -59,13 +46,6 @@ 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
@ -75,10 +55,16 @@ 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.
The driver supports up to four endpoints: One control endpoint (endpoint 0),
two interrupt-in (or bulk-in) endpoints (endpoint 1 and 3) and one
interrupt-out (or bulk-out) endpoint (endpoint 1). Please note that the USB
standard forbids bulk endpoints for low speed devices! Most operating systems
allow them anyway, but the AVR will spend 90% of the CPU time in the USB
interrupt polling for bulk data.
By default, only the control endpoint 0 is enabled. To get the other endpoints,
define USB_CFG_HAVE_INTRIN_ENDPOINT, USB_CFG_HAVE_INTRIN_ENDPOINT3 and/or
USB_CFG_IMPLEMENT_FN_WRITEOUT respectively (see usbconfig-prototype.h for
details).
Maximum data payload:
Data payload of control in and out transfers may be up to 254 bytes. In order
@ -92,7 +78,7 @@ 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
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.
@ -100,21 +86,22 @@ 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.
pullup resistor on D+ (interrupt). If D- becomes statically 0, the driver may
block in the interrupt routine.
Interrupt latency:
The application must ensure that the USB interrupt is not disabled for more
than 20 cycles. This implies that all interrupt routines must either be
declared as "INTERRUPT" instead of "SIGNAL" (see "avr/signal.h") or that they
are written in assembler with "sei" as the first instruction.
than 25 cycles (this is for 12 MHz, faster clocks allow longer latency).
This implies that all interrupt routines must either 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.
and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if
the host conforms to the standard. The driver will consume CPU cycles for all
USB messages, even if they address another (low-speed) device on the same bus.
*/
@ -122,7 +109,7 @@ messages, even if they address another (low-speed) device on the same bus.
/* --------------------------- Module Interface ---------------------------- */
/* ------------------------------------------------------------------------- */
#define USBDRV_VERSION 20060314
#define USBDRV_VERSION 20070919
/* 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
@ -130,8 +117,18 @@ messages, even if they address another (low-speed) device on the same bus.
* older than 2006-01-25.
*/
#ifndef __ASSEMBLER__
#ifndef USB_PUBLIC
#define USB_PUBLIC
#endif
/* USB_PUBLIC is used as declaration attribute for all functions exported by
* the USB driver. The default is no attribute (see above). You may define it
* to static either in usbconfig.h or from the command line if you include
* usbdrv.c instead of linking against it. Including the C module of the driver
* directly in your code saves a couple of bytes in flash memory.
*/
#ifndef __ASSEMBLER__
#ifndef uchar
#define uchar unsigned char
#endif
@ -140,11 +137,13 @@ messages, even if they address another (low-speed) device on the same bus.
#endif
/* shortcuts for well defined 8 bit integer types */
extern void usbInit(void);
struct usbRequest; /* forward declaration */
USB_PUBLIC void usbInit(void);
/* This function must be called before interrupts are enabled and the main
* loop is entered.
*/
extern void usbPoll(void);
USB_PUBLIC void usbPoll(void);
/* This function must be called at regular intervals from the main loop.
* Maximum delay between calls is somewhat less than 50ms (USB timeout for
* accepting a Setup message). Otherwise the device will not be recognized.
@ -156,7 +155,7 @@ extern uchar *usbMsgPtr;
* implementation of usbFunctionWrite(). It is also used internally by the
* driver for standard control requests.
*/
extern uchar usbFunctionSetup(uchar data[8]);
USB_PUBLIC 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
@ -183,23 +182,36 @@ extern uchar usbFunctionSetup(uchar data[8]);
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite()
* are only done if enabled by the configuration in usbconfig.h.
*/
USB_PUBLIC uchar usbFunctionDescriptor(struct usbRequest *rq);
/* You need to implement this function ONLY if you provide USB descriptors at
* runtime (which is an expert feature). It is very similar to
* usbFunctionSetup() above, but it is called only to request USB descriptor
* data. See the documentation of usbFunctionSetup() above for more info.
*/
#if USB_CFG_HAVE_INTRIN_ENDPOINT
void usbSetInterrupt(uchar *data, uchar len);
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len);
/* This function sets the message which will be sent during the next interrupt
* IN transfer. The message is copied to an internal buffer and must not exceed
* a length of 8 bytes. The message may be 0 bytes long just to indicate the
* 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)
extern volatile uchar usbTxLen1;
#define usbInterruptIsReady() (usbTxLen1 & 0x10)
/* This macro indicates whether the last interrupt message has already been
* sent. If you set a new interrupt message before the old was sent, the
* message already buffered will be lost.
*/
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len);
extern volatile uchar usbTxLen3;
#define usbInterruptIsReady3() (usbTxLen3 & 0x10)
/* Same as above for endpoint 3 */
#endif
#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */
#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
extern PROGMEM const char usbHidReportDescriptor[];
#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */
#define usbHidReportDescriptor usbDescriptorHidReport
/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */
/* If you implement an HID device, you need to provide a report descriptor.
* The HID report descriptor syntax is a bit complex. If you understand how
* report descriptors are constructed, we recommend that you use the HID
@ -207,21 +219,8 @@ extern PROGMEM const char usbHidReportDescriptor[];
* 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);
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len);
/* This function is called by the driver to provide a control transfer's
* payload data (control-out). It is called in chunks of up to 8 bytes. The
* total count provided in the current control transfer can be obtained from
@ -239,7 +238,7 @@ extern uchar usbFunctionWrite(uchar *data, uchar len);
*/
#endif /* USB_CFG_IMPLEMENT_FN_WRITE */
#if USB_CFG_IMPLEMENT_FN_READ
extern uchar usbFunctionRead(uchar *data, uchar len);
USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len);
/* This function is called by the driver to ask the application for a control
* transfer's payload data (control-in). It is called in chunks of up to 8
* bytes each. You should copy the data to the location given by 'data' and
@ -250,26 +249,37 @@ extern uchar usbFunctionRead(uchar *data, uchar len);
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup()..
*/
#endif /* USB_CFG_IMPLEMENT_FN_READ */
#ifdef USB_CFG_PULLUP_IOPORT
#if USB_CFG_IMPLEMENT_FN_WRITEOUT
USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len);
/* This function is called by the driver when data on interrupt-out or bulk-
* out endpoint 1 is received. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT
* to 1 in usbconfig.h to get this function called.
*/
#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */
#ifdef USB_CFG_PULLUP_IOPORTNAME
#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<<USB_CFG_PULLUP_BIT)), \
(USB_PULLUP_OUT |= (1<<USB_CFG_PULLUP_BIT)))
/* This macro (intended to look like a function) connects the device to the
* USB bus. It is only available if you have defined the constants
* USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h.
*/
#define usbDeviceDisconnect() (USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT))
#define usbDeviceDisconnect() ((USB_PULLUP_DDR &= ~(1<<USB_CFG_PULLUP_BIT)), \
(USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT)))
/* This macro (intended to look like a function) disconnects the device from
* the USB bus. It is only available if you have defined the constants
* USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT in usbconfig.h.
*/
#endif /* USB_CFG_PULLUP_IOPORT */
extern unsigned usbCrc16(uchar *data, uchar len);
extern unsigned usbCrc16(unsigned data, uchar len);
#define usbCrc16(data, len) usbCrc16((unsigned)(data), len)
/* This function calculates the binary complement of the data CRC used in
* USB data packets. The value is used to build raw transmit packets.
* You may want to use this function for data checksums or to verify received
* data.
* data. We enforce 16 bit calling conventions for compatibility with IAR's
* tiny memory model.
*/
extern unsigned usbCrc16Append(unsigned char *data, unsigned char len);
extern unsigned usbCrc16Append(unsigned data, uchar len);
#define usbCrc16Append(data, len) usbCrc16Append((unsigned)(data), len)
/* This function is equivalent to usbCrc16() above, except that it appends
* the 2 bytes CRC (lowbyte first) in the 'data' buffer after reading 'len'
* bytes.
@ -281,27 +291,173 @@ extern uchar usbConfiguration;
* You may want to reflect the "configured" status with a LED on the device or
* switch on high power parts of the circuit only if the device is configured.
*/
#if USB_COUNT_SOF
extern volatile uchar usbSofCount;
/* This variable is incremented on every SOF packet. It is only available if
* the macro USB_COUNT_SOF is defined to a value != 0.
*/
#endif
#define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8))
/* This macro builds a descriptor header for a string descriptor given the
* string's length. See usbdrv.c for an example how to use it.
*/
#if USB_CFG_SERIAL_NUMBER_LENGTH
extern PROGMEM int usbCfgSerialNumberStringDescriptor[];
/* This array of unicode characters (prefixed by a string descriptor header as
* explained above) represents the serial number of the device.
#if USB_CFG_HAVE_FLOWCONTROL
extern volatile schar usbRxLen;
#define usbDisableAllRequests() usbRxLen = -1
/* Must be called from usbFunctionWrite(). This macro disables all data input
* from the USB interface. Requests from the host are answered with a NAK
* while they are disabled.
*/
#define usbEnableAllRequests() usbRxLen = 0
/* May only be called if requests are disabled. This macro enables input from
* the USB interface after it has been disabled with usbDisableAllRequests().
*/
#define usbAllRequestsAreDisabled() (usbRxLen < 0)
/* Use this macro to find out whether requests are disabled. It may be needed
* to ensure that usbEnableAllRequests() is never called when requests are
* enabled.
*/
#endif
#define USB_SET_DATATOKEN1(token) usbTxBuf1[0] = token
#define USB_SET_DATATOKEN3(token) usbTxBuf3[0] = token
/* These two macros can be used by application software to reset data toggling
* for interrupt-in endpoints 1 and 3.
*/
#endif /* __ASSEMBLER__ */
/* ------------------------------------------------------------------------- */
/* ----------------- Definitions for Descriptor Properties ----------------- */
/* ------------------------------------------------------------------------- */
/* This is advanced stuff. See usbconfig-prototype.h for more information
* about the various methods to define USB descriptors. If you do nothing,
* the default descriptors will be used.
*/
#define USB_PROP_IS_DYNAMIC (1 << 8)
/* If this property is set for a descriptor, usbFunctionDescriptor() will be
* used to obtain the particular descriptor.
*/
#define USB_PROP_IS_RAM (1 << 9)
/* If this property is set for a descriptor, the data is read from RAM
* memory instead of Flash. The property is used for all methods to provide
* external descriptors.
*/
#define USB_PROP_LENGTH(len) ((len) & 0xff)
/* If a static external descriptor is used, this is the total length of the
* descriptor in bytes.
*/
/* all descriptors which may have properties: */
#ifndef USB_CFG_DESCR_PROPS_DEVICE
#define USB_CFG_DESCR_PROPS_DEVICE 0
#endif
#ifndef USB_CFG_DESCR_PROPS_CONFIGURATION
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#endif
#ifndef USB_CFG_DESCR_PROPS_STRINGS
#define USB_CFG_DESCR_PROPS_STRINGS 0
#endif
#ifndef USB_CFG_DESCR_PROPS_STRING_0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#endif
#ifndef USB_CFG_DESCR_PROPS_STRING_VENDOR
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#endif
#ifndef USB_CFG_DESCR_PROPS_STRING_PRODUCT
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#endif
#ifndef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#endif
#ifndef USB_CFG_DESCR_PROPS_HID
#define USB_CFG_DESCR_PROPS_HID 0
#endif
#if !(USB_CFG_DESCR_PROPS_HID_REPORT)
# undef USB_CFG_DESCR_PROPS_HID_REPORT
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* do some backward compatibility tricks */
# define USB_CFG_DESCR_PROPS_HID_REPORT USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
# else
# define USB_CFG_DESCR_PROPS_HID_REPORT 0
# endif
#endif
#ifndef USB_CFG_DESCR_PROPS_UNKNOWN
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
#endif
/* ------------------ forward declaration of descriptors ------------------- */
/* If you use external static descriptors, they must be stored in global
* arrays as declared below:
*/
#ifndef __ASSEMBLER__
extern
#if !(USB_CFG_DESCR_PROPS_DEVICE & USB_PROP_IS_RAM)
PROGMEM
#endif
char usbDescriptorDevice[];
extern
#if !(USB_CFG_DESCR_PROPS_CONFIGURATION & USB_PROP_IS_RAM)
PROGMEM
#endif
char usbDescriptorConfiguration[];
extern
#if !(USB_CFG_DESCR_PROPS_HID_REPORT & USB_PROP_IS_RAM)
PROGMEM
#endif
char usbDescriptorHidReport[];
extern
#if !(USB_CFG_DESCR_PROPS_STRING_0 & USB_PROP_IS_RAM)
PROGMEM
#endif
char usbDescriptorString0[];
extern
#if !(USB_CFG_DESCR_PROPS_STRING_VENDOR & USB_PROP_IS_RAM)
PROGMEM
#endif
int usbDescriptorStringVendor[];
extern
#if !(USB_CFG_DESCR_PROPS_STRING_PRODUCT & USB_PROP_IS_RAM)
PROGMEM
#endif
int usbDescriptorStringDevice[];
extern
#if !(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER & USB_PROP_IS_RAM)
PROGMEM
#endif
int usbDescriptorStringSerialNumber[];
#endif /* __ASSEMBLER__ */
/* ------------------------------------------------------------------------- */
/* ------------------------ General Purpose Macros ------------------------- */
/* ------------------------------------------------------------------------- */
#define USB_CONCAT(a, b) a ## b
#define USB_CONCAT_EXPANDED(a, b) USB_CONCAT(a, b)
#define USB_OUTPORT(name) USB_CONCAT(PORT, name)
#define USB_INPORT(name) USB_CONCAT(PIN, name)
#define USB_DDRPORT(name) USB_CONCAT(DDR, name)
/* The double-define trick above lets us concatenate strings which are
* defined by macros.
*/
/* ------------------------------------------------------------------------- */
/* ------------------------- Constant definitions -------------------------- */
/* ------------------------------------------------------------------------- */
#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID)
#error "You MUST NOT use obdev's shared VID/PID with HID class devices!"
/* The shared VID/PID must be used in conjunction with libusb (see license for
* the IDs). This contradicts HID usage (at least on Windows).
#if !defined __ASSEMBLER__ && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID)
#warning "You should define USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID in usbconfig.h"
/* If the user has not defined IDs, we default to obdev's free IDs.
* See USBID-License.txt for details.
*/
#endif
@ -311,44 +467,57 @@ extern PROGMEM int usbCfgSerialNumberStringDescriptor[];
#endif
#ifndef USB_CFG_DEVICE_ID
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* 1503 in dec, shared PID for HIDs */
# elif USB_CFG_INTERFACE_CLASS == 2
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* 1505 in dec, shared PID for CDC Modems */
# else
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* 1500 in dec, obdev's free PID */
# endif
#ifndef USB_BUFFER_SECTION
# define USB_BUFFER_SECTION ".bss" /* if user has not selected a named section */
#endif
/* I/O definitions for assembler module */
#define USBOUT USB_CFG_IOPORT /* output port for USB bits */
#define USB_PULLUP_OUT USB_CFG_PULLUP_IOPORT
#ifdef __ASSEMBLER__
/* the following two lines must start in column 0 for IAR assembler */
USBIN = (USB_CFG_IOPORT - 2) /* input port for USB bits */
USBDDR = (USB_CFG_IOPORT - 1) /* data direction for USB bits */
#else
#define USBIN (*(&USB_CFG_IOPORT - 2)) /* input port for USB bits */
#define USBDDR (*(&USB_CFG_IOPORT - 1)) /* data direction for USB bits */
#define USB_PULLUP_DDR (*(&USB_CFG_PULLUP_IOPORT - 1))
/* Derive Output, Input and DataDirection ports from port names */
#ifndef USB_CFG_IOPORTNAME
#error "You must define USB_CFG_IOPORTNAME in usbconfig.h, see usbconfig-prototype.h"
#endif
#if USB_CFG_DMINUS_BIT != 0
# error "USB_CFG_DMINUS_BIT MUST be 0!"
#define USBOUT USB_OUTPORT(USB_CFG_IOPORTNAME)
#define USB_PULLUP_OUT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME)
#define USBIN USB_INPORT(USB_CFG_IOPORTNAME)
#define USBDDR USB_DDRPORT(USB_CFG_IOPORTNAME)
#define USB_PULLUP_DDR USB_DDRPORT(USB_CFG_PULLUP_IOPORTNAME)
#define USBMINUS USB_CFG_DMINUS_BIT
#define USBPLUS USB_CFG_DPLUS_BIT
#define USBIDLE (1<<USB_CFG_DMINUS_BIT) /* value representing J state */
#define USBMASK ((1<<USB_CFG_DPLUS_BIT) | (1<<USB_CFG_DMINUS_BIT)) /* mask for USB I/O bits */
/* defines for backward compatibility with older driver versions: */
#define USB_CFG_IOPORT USB_OUTPORT(USB_CFG_IOPORTNAME)
#ifdef USB_CFG_PULLUP_IOPORTNAME
#define USB_CFG_PULLUP_IOPORT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME)
#endif
#define USBMINUS 0 /* D- MUST be on bit 0 */
#define USBIDLE 0x01 /* value representing J state */
#define USBMASK ((1<<USB_CFG_DPLUS_BIT) | 1) /* mask for USB I/O bits */
#define USB_BUFSIZE 11 /* PID, 8 bytes data, 2 bytes CRC */
/* Try to find registers and bits responsible for ext interrupt 0 */
/* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */
#ifndef USB_INTR_CFG /* allow user to override our default */
# if defined EICRA
# define USB_INTR_CFG EICRA
# else
# define USB_INTR_CFG MCUCR
# endif
#endif
#ifndef USB_INTR_CFG_SET /* allow user to override our default */
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */
#endif
#ifndef USB_INTR_CFG_CLR /* allow user to override our default */
# define USB_INTR_CFG_CLR 0 /* no bits to clear */
#endif
#ifndef USB_INTR_ENABLE /* allow user to override our default */
# if defined GIMSK
# define USB_INTR_ENABLE GIMSK
# elif defined EIMSK
@ -356,14 +525,21 @@ USBDDR = (USB_CFG_IOPORT - 1) /* data direction for USB bits */
# else
# define USB_INTR_ENABLE GICR
# endif
#endif
#ifndef USB_INTR_ENABLE_BIT /* allow user to override our default */
# define USB_INTR_ENABLE_BIT INT0
#endif
#ifndef USB_INTR_PENDING /* allow user to override our default */
# if defined EIFR
# define USB_INTR_PENDING EIFR
# else
# define USB_INTR_PENDING GIFR
# endif
#endif
#ifndef USB_INTR_PENDING_BIT /* allow user to override our default */
# define USB_INTR_PENDING_BIT INTF0
#endif
/*
The defines above don't work for the following chips
@ -392,7 +568,14 @@ at90s1200, attiny11, attiny12, attiny15, attiny28: these have no RAM
#define USBPID_NAK 0x5a
#define USBPID_STALL 0x1e
#ifndef USB_INITIAL_DATATOKEN
#define USB_INITIAL_DATATOKEN USBPID_DATA0
#endif
#ifndef __ASSEMBLER__
extern uchar usbTxBuf1[USB_BUFSIZE], usbTxBuf3[USB_BUFSIZE];
typedef union usbWord{
unsigned word;
uchar bytes[2];

View File

@ -1,20 +1,18 @@
/* Name: usbdrvasm.S
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2004-12-29
* Creation Date: 2007-06-13
* 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 $
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* Revision: $Id: usbdrvasm.S,v 1.2 2009-05-02 12:41:41 cvs 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!
This module is the assembler part of the USB driver. This file contains
general code (preprocessor acrobatics and CRC computation) and then includes
the file appropriate for the given clock rate.
*/
#include "iarcompat.h"
@ -26,7 +24,6 @@ of CPU cycles, but even an exact number of cycles!
#endif /* __IAR_SYSTEMS_ASM__ */
#include "usbdrv.h" /* for common defs */
/* register names */
#define x1 r16
#define x2 r17
@ -34,6 +31,9 @@ of CPU cycles, but even an exact number of cycles!
#define cnt r19
#define x3 r20
#define x4 r21
#define bitcnt r22
#define phase x4
#define leap x4
/* Some assembler dependent definitions and declarations: */
@ -47,615 +47,55 @@ of CPU cycles, but even an exact number of cycles!
# 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 */
# define hi8(x) (((x)>>8) & 0xff) /* 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
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen
extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3
# if USB_COUNT_SOF
extern usbSofCount
# endif
public usbCrc16
public usbCrc16Append
# ifndef IVT_BASE_ADDRESS
# define IVT_BASE_ADDRESS 0
# endif
ASEG
ORG INT0_vect + IVT_BASE_ADDRESS
rjmp SIG_INTERRUPT0
COMMON INTVEC
# ifndef USB_INTR_VECTOR
ORG INT0_vect
# else /* USB_INTR_VECTOR */
ORG USB_INTR_VECTOR
# undef USB_INTR_VECTOR
# endif /* USB_INTR_VECTOR */
# define USB_INTR_VECTOR usbInterruptHandler
rjmp USB_INTR_VECTOR
RSEG CODE
#else /* __IAR_SYSTEMS_ASM__ */
# define nop2 rjmp .+0 /* jump to next instruction */
# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */
# define USB_INTR_VECTOR SIG_INTERRUPT0
# endif
.text
.global SIG_INTERRUPT0
.type SIG_INTERRUPT0, @function
.global USB_INTR_VECTOR
.type USB_INTR_VECTOR, @function
.global usbCrc16
.global usbCrc16Append
#endif /* __IAR_SYSTEMS_ASM__ */
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<<USB_CFG_DPLUS_BIT ;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<<USB_INTR_PENDING_BIT ;1
cpi cnt, 3 ;1
out USB_INTR_PENDING, x1;1 clear pending intr and check flag later. SE0 must be over. {,10} into next frame
brlo rxDoReturn ;1 ensure valid packet size, ignore others
ld x1, y ;2 PID
ldd x2, y+1 ;2 ADDR + 1 bit endpoint number
mov x3, x2 ;1 store for endpoint number
andi x2, 0x7f ;1 mask endpoint number bit
lds shift, usbDeviceAddr;2
cpi x1, USBPID_SETUP ;1
breq isSetupOrOut ;2 -> 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<<USB_INTR_PENDING_BIT;1 many int0 events occurred during our processing -- clear pending flag
out USB_INTR_PENDING, x1;1
pop shift ;2
pop cnt ;2
pop x2 ;2
pop x1 ;2
out SREG, x1 ;1
pop x1 ;2
reti ;4 -> {,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
#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */
# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING
# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg
#else /* It's a memory address, use lds and sts */
# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING
# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg
#endif
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 ########################
;----------------------------------------------------------------------------
; Utility functions
;----------------------------------------------------------------------------
#ifdef __IAR_SYSTEMS_ASM__
/* Register assignments for usbCrc16 on IAR cc */
@ -663,6 +103,8 @@ bitstuff7: ;1 (for branch taken)
* First parameter passed in r16/r17, second in r18/r19 and so on.
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer)
* Result is passed in r16/r17
* In case of the "tiny" memory model, pointers are only 8 bit with no
* padding. We therefore pass argument 1 as "16 bit unsigned".
*/
RTMODEL "__rt_version", "3"
/* The line above will generate an error if cc calling conventions change.
@ -756,3 +198,24 @@ usbCrc16Append:
st ptr+, resCrcL
st ptr+, resCrcH
ret
;----------------------------------------------------------------------------
; Now include the clock rate specific code
;----------------------------------------------------------------------------
#ifndef USB_CFG_CLOCK_KHZ
# define USB_CFG_CLOCK_KHZ 12000
#endif
#if USB_CFG_CLOCK_KHZ == 12000
# include "usbdrvasm12.S"
#elif USB_CFG_CLOCK_KHZ == 15000
# include "usbdrvasm15.S"
#elif USB_CFG_CLOCK_KHZ == 16000
# include "usbdrvasm16.S"
#elif USB_CFG_CLOCK_KHZ == 16500
# include "usbdrvasm165.S"
#else
# error "USB_CFG_CLOCK_KHZ is not one of the supported rates!"
#endif

View File

@ -4,8 +4,8 @@
* 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 $
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbdrvasm.asm,v 1.2 2009-05-02 12:41:41 cvs Exp $
*/
/*