aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Starkjohann <cs+github@obdev.at>2008-04-17 19:00:20 +0000
committerChristian Starkjohann <cs+github@obdev.at>2008-04-17 19:00:20 +0000
commit95ca3f5bd696b5450820929e1b1a444d02f0bd1e (patch)
tree70ac0161a0e4548acaf2f89d2ecb77132579ff7a
parent2f465daef9e6c5a6b7bda3074c050cd7e8515770 (diff)
downloadv-usb-95ca3f5bd696b5450820929e1b1a444d02f0bd1e.tar.gz
v-usb-95ca3f5bd696b5450820929e1b1a444d02f0bd1e.tar.bz2
v-usb-95ca3f5bd696b5450820929e1b1a444d02f0bd1e.zip
- imported new files into project
-rw-r--r--Readme.txt72
-rw-r--r--circuits/Readme.txt75
-rw-r--r--circuits/tiny45-rc.schbin0 -> 193502 bytes
-rw-r--r--circuits/with-series-diodes.schbin0 -> 213956 bytes
-rw-r--r--circuits/with-vreg.schbin0 -> 215922 bytes
-rw-r--r--circuits/with-zener.schbin0 -> 225561 bytes
-rw-r--r--examples/Readme.txt101
-rw-r--r--examples/custom-class/Readme.txt64
-rw-r--r--examples/custom-class/commandline/set-led.c84
-rw-r--r--examples/custom-class/firmware/Makefile164
-rw-r--r--examples/custom-class/firmware/main.c91
-rw-r--r--examples/custom-class/firmware/requests.h32
-rwxr-xr-xexamples/custom-class/make-files.sh44
-rw-r--r--examples/hid-custom-rq/Readme.txt28
-rw-r--r--examples/hid-custom-rq/firmware/main.c119
-rw-r--r--examples/hid-custom-rq/firmware/requests.h32
-rwxr-xr-xexamples/hid-custom-rq/make-files.sh64
-rw-r--r--examples/hid-data/Readme.txt75
-rw-r--r--examples/hid-data/commandline/Makefile42
-rw-r--r--examples/hid-data/commandline/Makefile.windows18
-rw-r--r--examples/hid-data/commandline/hidtool.c127
-rw-r--r--examples/hid-data/firmware/main.c141
-rwxr-xr-xexamples/hid-data/make-files.sh45
-rw-r--r--examples/hid-mouse/Readme.txt48
-rw-r--r--examples/hid-mouse/firmware/main.c165
-rwxr-xr-xexamples/hid-mouse/make-files.sh39
-rw-r--r--examples/usbtool/Makefile48
-rw-r--r--examples/usbtool/Makefile.windows18
-rw-r--r--examples/usbtool/Readme.txt209
-rwxr-xr-xexamples/usbtool/make-files.sh16
-rw-r--r--examples/usbtool/usbtool.c356
-rw-r--r--libs-device/Readme.txt16
-rw-r--r--libs-device/osccal.c64
-rw-r--r--libs-device/osccal.h53
-rw-r--r--libs-host/Readme.txt26
-rw-r--r--libs-host/hiddata.c317
-rw-r--r--libs-host/hiddata.h71
-rw-r--r--libs-host/hidsdi.h49
-rw-r--r--libs-host/opendevice.c203
-rw-r--r--libs-host/opendevice.h77
40 files changed, 3193 insertions, 0 deletions
diff --git a/Readme.txt b/Readme.txt
new file mode 100644
index 0000000..6a35b51
--- /dev/null
+++ b/Readme.txt
@@ -0,0 +1,72 @@
+This is the Readme file to Objective Development's firmware-only USB driver
+for Atmel AVR microcontrollers and related code. For more information please
+visit http://www.obdev.at/avrusb/.
+
+
+WHAT IS INCLUDED IN THIS PACKAGE?
+=================================
+This package consists of the device side USB driver firmware, library code
+for device and host and fully working examples for device and host:
+
+ Readme.txt .............. The file you are currently reading.
+ License.txt ............. Free Open Source license for this package (GPL).
+ CommercialLicense.txt ... Alternative commercial license for this package.
+ usbdrv .................. AVR-USB firmware, to be included in your project.
+ circuits ................ Example circuits using this driver.
+ libs-device ............. Useful code snippets for the device firmware.
+ libs-host ............... Useful code snippets for device drivers.
+ examples ................ Example code for device and host side.
+
+Each subdirectory contains a separate Readme file which explains its
+contents.
+
+
+PREREQUISITES
+=============
+The AVR code of AVR-USB is written in C and assembler. You need either
+avr-gcc or IAR CC to compile the project. We recommend avr-gcc because it
+is free and easily available. Gcc version 3 generates slightly more
+efficient code than version 4 for AVR-USB. Not every release is tested with
+the IAR compiler. Previous versions have been tested with IAR 4.10B/W32 and
+4.12A/W32 on an ATmega8 with the "small" and "tiny" memory model.
+
+Ready made avr-gcc tool chains are available for most operating systems:
+ * Windows: WinAVR http://winavr.sourceforge.net/
+ * Mac: AVRMacPack http://www.obdev.at/avrmacpack/
+ * Linux and other Unixes: Most free Unixes have optional packages for AVR
+ development. If not, follow the instructions at
+ http://www.nongnu.org/avr-libc/user-manual/install_tools.html
+
+Our host side examples are compiled with gcc on all platforms. Gcc is the
+default C compiler on Mac, Linux and many other Unixes. On windows, we
+recommend MinGW (http://www.mingw.org/). Use the automated MinGW installer
+for least troubles. You also need MSYS from the same site to work with
+standard Makefiles.
+
+Most examples also depend on libusb. Libusb is available from
+http://libusb.sourceforge.net/ for Unix and
+http://libusb-win32.sourceforge.net/ for Windows.
+
+
+TECHNICAL DOCUMENTATION
+=======================
+The API reference of the driver firmware can be found in usbdrv/usbdrv.h.
+Documentation for host and device library files are in the respective header
+files. For more information, follow the "Documentation Wiki" link in the
+navigation box at http://www.obdev.at/avrusb/.
+
+
+LICENSE
+=======
+AVR-USB and related code is distributed under the terms of the GNU General
+Public License (GPL) version 2 (see License.txt for details). In addition to
+the terms of the GPL, we strongly encourage you to publish your entire
+project and mail OBJECTIVE DEVELOPMENT a link to your publication.
+
+Alternatively, we offer a commercial license without the restrictions of the
+GPL. See CommercialLicense.txt for details.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/circuits/Readme.txt b/circuits/Readme.txt
new file mode 100644
index 0000000..7b1a95d
--- /dev/null
+++ b/circuits/Readme.txt
@@ -0,0 +1,75 @@
+This is the Readme file for the AVR-USB example circuits directory.
+
+
+CIRCUITS IN THIS DIRECTORY
+==========================
+Since USB requires 3.3 V levels on D+ and D- but delivers a power supply of
+ca. 5 V, some kind of level conversion must be performed. There are several
+ways to implement this level conversion, see the example circuits below.
+
+with-vreg.png and with-vreg.sch (EAGLE schematics):
+ This circuit uses a low drop voltage regulator to reduce the USB supply to
+ 3.3 V. You MUST use a low drop regulator because standard regulators such
+ as the LM317 require at least ca. 2 V drop. The advantage of this approach
+ is that it comes closest to the voltage levels required by the USB
+ specification and that the circuit is powered from a regulated supply. If
+ no USB cable is used (connector directly soldered on PCB), you can even
+ omit the 68 Ohm series resistors. The disadvantage is that you may want to
+ use other chips in your design which require 5 V. Please check that the AVR
+ used in your design allows the chosen clock rate at 3.3 V.
+
+with-zener.png and with-zener.sch (EAGLE schematics):
+ This circuit enforces lower voltage levels on D+ and D- with zener diodes.
+ The zener diodes MUST be low power / low current types to ensure that the
+ 1k5 pull-up resistor on D- generates a voltage of well above 2.5 V (but
+ below 3.6 V). The advantage of this circuit is its simplicity and that the
+ circuit can be powered at 5 V (usually precise enough if the cable drop is
+ neglected). The disadvantage is that some zener diodes have a lower voltage
+ than 3 V when powered through 1k5 and the choice of components becomes
+ relevant. In addition to that, the power consumption during USB data
+ transfer is increased because the current is only limited by the 68 Ohm
+ series resistor. The zeners may even distort the signal waveforms due to
+ their capacity.
+
+with-series-diodes.png and with-series-diodes.sch (EAGLE schematics):
+ This is a simplified low-cost version of the voltage regulator approach.
+ Instead of using a voltage regulator, we reduce the voltage by the forward
+ voltage of two silicon diodes (roughly 1.4 V). This gives ca. 3.6 V which
+ is practically inside the allowed range. The big disadvantage is that the
+ supply is not regulated -- it even depends strongly on the power
+ consumption. This cannot be tolerated for analog circuits.
+
+tiny45-rc.png and tiny45-rc.sch (EAGLE schematics):
+ This is mostly an example for connecting an 8 pin device using the internal
+ RC oscillator for system clock. This example uses series diodes to limit
+ the supply, but you may choose any other method. Please note that you must
+ choose a clock rate of 16.5 MHz because only the receiver module for this
+ frequency has a PLL to allow higher clock rate tolerances.
+
+
+GENERAL DESIGN NOTES
+====================
+All examples have D+ on hardware interrupt INT0 because this is the highest
+priority interrupt on AVRs. You may use other hardware interrupts (and
+configure the options at the end of usbconfig.h accordingly) if you make sure
+that no higher priority interrupt is used.
+
+Most examples have a 1M pull-down resistor at D+. This pull-up ensures that
+in self-powered designs no interrupts occur while USB is not connected. You
+may omit this resistor in bus-powered designs. Older examples had a pull-up
+resistor instead. This is not compatible with the zener diode approach to
+level conversion: 1M pull-up in conjunction with a 3.6 V zener diode give an
+invalid logic level.
+
+All examples with ATMega8/88/168 have D+ at port D bit 2 (because this is
+hardware interrupt 0) and D- on port D bit 4 because it is also a clock input
+for timer/counter 0. This way the firmware can easily check for activity on
+D- (USB frame pulses) by checking the counter value in regular intervals. If
+no activity is found, the firmware should (according to the USB
+specification) put the system into a low power suspend mode.
+
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/circuits/tiny45-rc.sch b/circuits/tiny45-rc.sch
new file mode 100644
index 0000000..349f1ad
--- /dev/null
+++ b/circuits/tiny45-rc.sch
Binary files differ
diff --git a/circuits/with-series-diodes.sch b/circuits/with-series-diodes.sch
new file mode 100644
index 0000000..d84d99e
--- /dev/null
+++ b/circuits/with-series-diodes.sch
Binary files differ
diff --git a/circuits/with-vreg.sch b/circuits/with-vreg.sch
new file mode 100644
index 0000000..1f1797b
--- /dev/null
+++ b/circuits/with-vreg.sch
Binary files differ
diff --git a/circuits/with-zener.sch b/circuits/with-zener.sch
new file mode 100644
index 0000000..e7da8e5
--- /dev/null
+++ b/circuits/with-zener.sch
Binary files differ
diff --git a/examples/Readme.txt b/examples/Readme.txt
new file mode 100644
index 0000000..25f8c55
--- /dev/null
+++ b/examples/Readme.txt
@@ -0,0 +1,101 @@
+This is the Readme file for the directory "examples" of AVR-USB, a firmware-
+only USB driver for AVR microcontrollers.
+
+WHAT IS IN THIS DIRECTORY?
+==========================
+This directory contains examples which are mostly for educational purposes.
+Examples can be device firmware only, host software only or both. Here is
+a summary:
+
+custom-class
+ A custom class device with host software based on libusb. It demonstrates
+ the straight forward way of sending small amounts of data to a device and
+ receiving data from the device. It does NOT demonstrate how to send large
+ amounts of data to the device or how to receive data generated on the fly
+ by the device (how to use usbFunctionWrite() and usbFunctionRead()). See
+ the hid-data example for how usbFunctionWrite() and usbFunctionRead() are
+ used.
+
+hid-custom-rq
+ This example implements the same functionality as the custom-class example
+ above, but declares the device as HID. This prevents the "give me a driver
+ CD" dialog on Windows. The device can still be controlled with libusb as in
+ the previous example (on Windows, the filter version of libusb-win32 must
+ be installed). In addition to the features presented in custom-class, this
+ example demonstrates how a HID class device is defined.
+
+hid-mouse
+ This example implements a mouse device. No host driver is required since
+ today's operating systems have drivers for USB mice built-in. It
+ demonstrates how a real-world HID class device is implemented and how
+ interrupt-in endpoints are used.
+
+hid-data
+ This example demonstrates how the HID class can be misused to transfer
+ arbitrary data over HID feature reports. This technique is of great value
+ on Windows because no driver DLLs are needed (the hid-custom-rq example
+ still requires the libusb-win32 DLL, although it may be in the program's
+ directory). The host side application requires no installation, it can
+ even be started directly from a CD. This example also demonstrates how
+ to transfer data using usbFunctionWrite() and usbFunctionRead().
+
+usbtool
+ This is a general purpose development and debugging tool for USB devices.
+ You can use it during development of your device to test various requests
+ without special test programs. But it is also an example how all the
+ libusb API functions are used.
+
+More information about each example can be found in the Readme file in the
+respective directory.
+
+Hardware dependencies of AVR code has been kept at a minimum. All examples
+should work on any AVR chip which has enough resources to run the driver.
+Makefile and usbconfig.h have been configured for the metaboard hardware (see
+http://metalab.at/wiki/Metaboard for details). Edit the target device, fuse
+values, clock rate and programmer in Makefile and the I/O pins dedicated to
+USB in usbconfig.h.
+
+
+WHAT IS NOT DEMONSTRATED IN THESE EXAMPLES?
+===========================================
+These examples show only the most basic functionality. More elaborate
+examples and real world applications showing more features of the driver are
+available at http://www.obdev.at/avrusb/projects.html. Most of these
+features are described in the Documentation Wiki linked from this page.
+
+To mention just a few:
+
+Using RC oscillator for system clock
+ The 16.5 MHz module of AVR-USB has been designed to cope with clock rate
+ deviations up to 1%. This allows an RC oscillator to be used. Since the
+ AVR's RC oscillator has a factory precision of only 10%, it must be
+ calibrated to an external reference. The EasyLogger example shows how this
+ is done.
+
+Dynamically generated descriptors
+ Sometimes you want to implement different typtes of USB device depending
+ on a jumper or other condition. AVR-USB has a very flexible interface for
+ providing USB descriptors. See AVR-Doper for how to provide descriptors
+ at runtime.
+
+Virtual COM port
+ Some people prefer a virtual serial interface to communicate with their
+ device. We strongly discourage this method because it does things
+ forbidden by the USB specification. If you still want to go this route,
+ see AVR-CDC.
+
+Implementing suspend mode
+ AVR-USB does not implement suspend mode. This means that the device does
+ not reduce power consumption when the host goes into sleep mode. Device
+ firmware is free to implement suspend mode, though. See USB2LPT for an
+ example.
+
+The projects mentioned above can best be found on
+
+ http://www.obdev.at/avrusb/prjall.html
+
+where all projects are listed.
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/custom-class/Readme.txt b/examples/custom-class/Readme.txt
new file mode 100644
index 0000000..82b1b3e
--- /dev/null
+++ b/examples/custom-class/Readme.txt
@@ -0,0 +1,64 @@
+This is the Readme file for the custom-class example. In this example, we
+show how an LED can be controlled via USB.
+
+
+WHAT IS DEMONSTRATED?
+=====================
+This example shows how small amounts of data (several bytes) can be
+transferred between the device and the host. In addition to a very basic
+USB device, it demonstrates how to build a host side driver application
+using libusb or libusb-win32. It does NOT show how usbFunctionWrite() and
+usbFunctionRead() are used. See the hid-data example if you want to learn
+about these functions.
+
+
+PREREQUISITES
+=============
+Target hardware: You need an AVR based circuit based on one of the examples
+(see the "circuits" directory at the top level of this package), e.g. the
+metaboard (http://metalab.at/wiki/Metaboard).
+
+AVR development environment: You need the gcc tool chain for the AVR, see
+the Prerequisites section in the top level Readme file for how to obtain it.
+
+Host development environment: A C compiler and libusb. See the top level
+Readme file, section Prerequisites for more information.
+
+
+BUILDING THE FIRMWARE
+=====================
+Change to the "firmware" directory and modify Makefile according to your
+architecture (CPU clock, target device, fuse values) and ISP programmer. Then
+edit usbconfig.h according to your pin assignments for D+ and D-. The default
+settings are for the metaboard hardware. You should have wired an LED with a
+current limiting resistor of ca. 270 Ohm to a free I/O pin. Change the
+defines in main.c to match the port and bit number.
+
+Type "make hex" to build main.hex, then "make flash" to upload the firmware
+to the device. Don't forget to run "make fuse" once to program the fuses. If
+you use a prototyping board with boot loader, follow the instructions of the
+boot loader instead.
+
+Please note that the first "make hex" copies the driver from the top level
+into the firmware directory. If you use a different build system than our
+Makefile, you must copy the driver by hand.
+
+
+BUILDING THE HOST SOFTWARE
+==========================
+Since the host software is based on libusb or libusb-win32, make sure that
+this library is installed. On Unix, ensure that libusb-config is in your
+search PATH. On Windows, edit Makefile.windows and set the library path
+appropriately. Then type "make" on Unix or "make -f Makefile.windows" on
+Windows to build the command line tool.
+
+
+USING THE COMMAND LINE TOOL
+===========================
+The command line tool has three valid arguments: "status" to query the
+current LED status, "on" to turn on the LED and "off" to turn it off.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/custom-class/commandline/set-led.c b/examples/custom-class/commandline/set-led.c
new file mode 100644
index 0000000..07ba094
--- /dev/null
+++ b/examples/custom-class/commandline/set-led.c
@@ -0,0 +1,84 @@
+/* Name: set-led.c
+ * Project: custom-class, a basic USB example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-10
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description:
+This is the host-side driver for the custom-class example device. It searches
+the USB for the LEDControl device and sends the requests understood by this
+device.
+This program must be linked with libusb on Unix and libusb-win32 on Windows.
+See http://libusb.sourceforge.net/ or http://libusb-win32.sourceforge.net/
+respectively.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <usb.h> /* this is libusb */
+#include "opendevice.h" /* common code moved to separate module */
+
+#include "../firmware/requests.h" /* custom request numbers */
+#include "../firmware/usbconfig.h" /* device's VID/PID and names */
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s on ....... turn on LED\n", name);
+ fprintf(stderr, " %s off ...... turn off LED\n", name);
+ fprintf(stderr, " %s status ... ask current status of LED\n", name);
+}
+
+int main(int argc, char **argv)
+{
+usb_dev_handle *handle = NULL;
+const unsigned char rawVid[2] = {USB_CFG_VENDOR_ID}, rawPid[2] = {USB_CFG_DEVICE_ID};
+char vendor[] = {USB_CFG_VENDOR_NAME, 0}, product[] = {USB_CFG_DEVICE_NAME, 0};
+char buffer[1];
+int cnt, vid, pid, isOn;
+
+ usb_init();
+ if(argc < 2){ /* we need at least one argument */
+ usage(argv[0]);
+ exit(1);
+ }
+ /* compute VID/PID from usbconfig.h so that there is a central source of information */
+ vid = rawVid[1] * 256 + rawVid[0];
+ pid = rawPid[1] * 256 + rawPid[0];
+ /* The following function is in opendevice.c: */
+ if(usbOpenDevice(&handle, vid, vendor, pid, product, NULL, NULL, NULL) != 0){
+ fprintf(stderr, "Could not find USB device \"%s\" with vid=0x%x pid=0x%x\n", product, vid, pid);
+ exit(1);
+ }
+ /* Since we use only control endpoint 0, we don't need to choose a
+ * configuration and interface.
+ */
+ if(strcasecmp(argv[1], "status") == 0){
+ cnt = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CUSTOM_RQ_GET_STATUS, 0, 0, buffer, sizeof(buffer), 5000);
+ if(cnt < 1){
+ if(cnt < 0){
+ fprintf(stderr, "USB error: %s\n", usb_strerror());
+ }else{
+ fprintf(stderr, "only %d bytes received.\n", cnt);
+ }
+ }else{
+ printf("LED is %s\n", buffer[0] ? "on" : "off");
+ }
+ }else if((isOn = (strcasecmp(argv[1], "on") == 0)) || strcasecmp(argv[1], "off") == 0){
+ cnt = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CUSTOM_RQ_SET_STATUS, isOn, 0, buffer, 0, 5000);
+ if(cnt < 0){
+ fprintf(stderr, "USB error: %s\n", usb_strerror());
+ }
+ }else{
+ usage(argv[0]);
+ exit(1);
+ }
+ usb_close(handle);
+ return 0;
+}
diff --git a/examples/custom-class/firmware/Makefile b/examples/custom-class/firmware/Makefile
new file mode 100644
index 0000000..51474ba
--- /dev/null
+++ b/examples/custom-class/firmware/Makefile
@@ -0,0 +1,164 @@
+# Name: Makefile
+# Project: custom-class example
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-07
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+DEVICE = atmega168
+F_CPU = 16000000 # in Hz
+FUSE_L = # see below for fuse values for particular devices
+FUSE_H =
+AVRDUDE = avrdude -c usbasp -p $(DEVICE) # edit this line for your programmer
+
+CFLAGS = -Iusbdrv -I. -DDEBUG_LEVEL=0
+OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o
+
+COMPILE = avr-gcc -Wall -Os -DF_CPU=$(F_CPU) $(CFLAGS) -mmcu=$(DEVICE)
+
+##############################################################################
+# Fuse values for particular devices
+##############################################################################
+# If your device is not listed here, go to
+# http://palmavr.sourceforge.net/cgi-bin/fc.cgi
+# and choose options for external crystal clock and no clock divider
+#
+################################## ATMega8 ##################################
+# ATMega8 FUSE_L (Fuse low byte):
+# 0x9f = 1 0 0 1 1 1 1 1
+# ^ ^ \ / \--+--/
+# | | | +------- CKSEL 3..0 (external >8M crystal)
+# | | +--------------- SUT 1..0 (crystal osc, BOD enabled)
+# | +------------------ BODEN (BrownOut Detector enabled)
+# +-------------------- BODLEVEL (2.7V)
+# ATMega8 FUSE_H (Fuse high byte):
+# 0xc9 = 1 1 0 0 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
+# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0
+# | | | | | +-------- BOOTSZ1
+# | | | | + --------- EESAVE (don't preserve EEPROM over chip erase)
+# | | | +-------------- CKOPT (full output swing)
+# | | +---------------- SPIEN (allow serial programming)
+# | +------------------ WDTON (WDT not always on)
+# +-------------------- RSTDISBL (reset pin is enabled)
+#
+############################## ATMega48/88/168 ##############################
+# ATMega*8 FUSE_L (Fuse low byte):
+# 0xdf = 1 1 0 1 1 1 1 1
+# ^ ^ \ / \--+--/
+# | | | +------- CKSEL 3..0 (external >8M crystal)
+# | | +--------------- SUT 1..0 (crystal osc, BOD enabled)
+# | +------------------ CKOUT (if 0: Clock output enabled)
+# +-------------------- CKDIV8 (if 0: divide by 8)
+# ATMega*8 FUSE_H (Fuse high byte):
+# 0xde = 1 1 0 1 1 1 1 0
+# ^ ^ ^ ^ ^ \-+-/
+# | | | | | +------ BODLEVEL 0..2 (110 = 1.8 V)
+# | | | | + --------- EESAVE (preserve EEPROM over chip erase)
+# | | | +-------------- WDTON (if 0: watchdog always on)
+# | | +---------------- SPIEN (allow serial programming)
+# | +------------------ DWEN (debug wire enable)
+# +-------------------- RSTDISBL (reset pin is enabled)
+#
+############################## ATTiny25/45/85 ###############################
+# ATMega*5 FUSE_L (Fuse low byte):
+# 0xef = 1 1 1 0 1 1 1 1
+# ^ ^ \+/ \--+--/
+# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz)
+# | | +--------------- SUT 1..0 (BOD enabled, fast rising power)
+# | +------------------ CKOUT (clock output on CKOUT pin -> disabled)
+# +-------------------- CKDIV8 (divide clock by 8 -> don't divide)
+# ATMega*5 FUSE_H (Fuse high byte):
+# 0xdd = 1 1 0 1 1 1 0 1
+# ^ ^ ^ ^ ^ \-+-/
+# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V)
+# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved)
+# | | | +-------------- WDTON (watchdog timer always on -> disable)
+# | | +---------------- SPIEN (enable serial programming -> enabled)
+# | +------------------ DWEN (debug wire enable)
+# +-------------------- RSTDISBL (disable external reset -> enabled)
+#
+################################ ATTiny2313 #################################
+# ATTiny2313 FUSE_L (Fuse low byte):
+# 0xef = 1 1 1 0 1 1 1 1
+# ^ ^ \+/ \--+--/
+# | | | +------- CKSEL 3..0 (clock selection -> crystal @ 12 MHz)
+# | | +--------------- SUT 1..0 (BOD enabled, fast rising power)
+# | +------------------ CKOUT (clock output on CKOUT pin -> disabled)
+# +-------------------- CKDIV8 (divide clock by 8 -> don't divide)
+# ATTiny2313 FUSE_H (Fuse high byte):
+# 0xdb = 1 1 0 1 1 0 1 1
+# ^ ^ ^ ^ \-+-/ ^
+# | | | | | +---- RSTDISBL (disable external reset -> enabled)
+# | | | | +-------- BODLEVEL 2..0 (brownout trigger level -> 2.7V)
+# | | | +-------------- WDTON (watchdog timer always on -> disable)
+# | | +---------------- SPIEN (enable serial programming -> enabled)
+# | +------------------ EESAVE (preserve EEPROM on Chip Erase -> not preserved)
+# +-------------------- DWEN (debug wire enable)
+
+
+# symbolic targets:
+help:
+ @echo "This Makefile has no default rule. Use one of the following:"
+ @echo "make hex ....... to build main.hex"
+ @echo "make program ... to flash fuses and firmware"
+ @echo "make fuse ...... to flash the fuses"
+ @echo "make flash ..... to flash the firmware (use this on metaboard)"
+ @echo "make clean ..... to delete objects and hex file"
+
+hex: main.hex
+
+program: flash fuse
+
+# rule for programming fuse bits:
+fuse:
+ @[ "$(FUSE_H)" != "" -a "$(FUSE_L)" != "" ] || \
+ { echo "*** Edit Makefile and choose values for FUSE_L and FUSE_H!"; exit 1; }
+ $(AVRDUDE) -U hfuse:w:$(FUSE_H):m -U lfuse:w:$(FUSE_L):m
+
+# rule for uploading firmware:
+flash: main.hex
+ $(AVRDUDE) -U flash:w:main.hex:i
+
+# rule for deleting dependent files (those which can be built by Make):
+clean:
+ rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.elf *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s
+
+# Generic rule for compiling C files:
+.c.o:
+ $(COMPILE) -c $< -o $@
+
+# Generic rule for assembling Assembler source files:
+.S.o:
+ $(COMPILE) -x assembler-with-cpp -c $< -o $@
+# "-x assembler-with-cpp" should not be necessary since this is the default
+# file type for the .S (with capital S) extension. However, upper case
+# characters are not always preserved on Windows. To ensure WinAVR
+# compatibility define the file type manually.
+
+# Generic rule for compiling C to assembler, used for debugging only.
+.c.s:
+ $(COMPILE) -S $< -o $@
+
+# file targets:
+
+# Since we don't want to ship the driver multipe times, we copy it into this project:
+usbdrv:
+ cp -r ../../../usbdrv .
+
+main.elf: usbdrv $(OBJECTS) # usbdrv dependency only needed because we copy it
+ $(COMPILE) -o main.elf $(OBJECTS)
+
+main.hex: main.elf
+ rm -f main.hex main.eep.hex
+ avr-objcopy -j .text -j .data -O ihex main.elf main.hex
+ avr-size main.hex
+
+# debugging targets:
+
+disasm: main.elf
+ avr-objdump -d main.elf
+
+cpp:
+ $(COMPILE) -E main.c
diff --git a/examples/custom-class/firmware/main.c b/examples/custom-class/firmware/main.c
new file mode 100644
index 0000000..2d93db9
--- /dev/null
+++ b/examples/custom-class/firmware/main.c
@@ -0,0 +1,91 @@
+/* Name: main.c
+ * Project: custom-class, a basic USB example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-09
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+This example should run on most AVRs with only little changes. No special
+hardware resources except INT0 are used. You may have to change usbconfig.h for
+different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or
+at least be connected to INT0 as well.
+We assume that an LED is connected to port B bit 0. If you connect it to a
+different port or bit, change the macros below:
+*/
+#define LED_PORT_DDR DDRB
+#define LED_PORT_OUTPUT PORTB
+#define LED_BIT 0
+
+#include <avr/io.h>
+#include <avr/wdt.h>
+#include <avr/interrupt.h> /* for sei() */
+#include <util/delay.h> /* for _delay_ms() */
+
+#include <avr/pgmspace.h> /* required by usbdrv.h */
+#include "usbdrv.h"
+#include "oddebug.h" /* This is also an example for using debug macros */
+#include "requests.h" /* The custom request numbers we use */
+
+/* ------------------------------------------------------------------------- */
+/* ----------------------------- USB interface ----------------------------- */
+/* ------------------------------------------------------------------------- */
+
+uchar usbFunctionSetup(uchar data[8])
+{
+usbRequest_t *rq = (void *)data;
+
+ if(rq->bRequest == CUSTOM_RQ_SET_STATUS){
+ if(rq->wValue.bytes[0] & 1){ /* set LED */
+ LED_PORT_OUTPUT |= _BV(LED_BIT);
+ }else{ /* clear LED */
+ LED_PORT_OUTPUT &= ~_BV(LED_BIT);
+ }
+ }else if(rq->bRequest == CUSTOM_RQ_GET_STATUS){
+ static uchar dataBuffer[1]; /* buffer must stay valid when usbFunctionSetup returns */
+ dataBuffer[0] = ((LED_PORT_OUTPUT & _BV(LED_BIT)) != 0);
+ usbMsgPtr = dataBuffer; /* tell the driver which data to return */
+ return 1; /* tell the driver to send 1 byte */
+ }
+ return 0; /* default for not implemented requests: return no data back to host */
+}
+
+/* ------------------------------------------------------------------------- */
+
+int main(void)
+{
+uchar i;
+
+ wdt_enable(WDTO_1S);
+ /* Even if you don't use the watchdog, turn it off here. On newer devices,
+ * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
+ */
+ DBG1(0x00, 0, 0); /* debug output: main starts */
+ /* RESET status: all port bits are inputs without pull-up.
+ * That's the way we need D+ and D-. Therefore we don't need any
+ * additional hardware initialization.
+ */
+ odDebugInit();
+ usbInit();
+ usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
+ i = 0;
+ while(--i){ /* fake USB disconnect for > 250 ms */
+ wdt_reset();
+ _delay_ms(1);
+ }
+ usbDeviceConnect();
+ LED_PORT_DDR |= _BV(LED_BIT); /* make the LED bit an output */
+ sei();
+ DBG1(0x01, 0, 0); /* debug output: main loop starts */
+ for(;;){ /* main event loop */
+ DBG1(0x02, 0, 0); /* debug output: main loop iterates */
+ wdt_reset();
+ usbPoll();
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/examples/custom-class/firmware/requests.h b/examples/custom-class/firmware/requests.h
new file mode 100644
index 0000000..b6a3c2b
--- /dev/null
+++ b/examples/custom-class/firmware/requests.h
@@ -0,0 +1,32 @@
+/* Name: requests.h
+ * Project: custom-class, a basic USB example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-09
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/* This header is shared between the firmware and the host software. It
+ * defines the USB request numbers (and optionally data types) used to
+ * communicate between the host and the device.
+ */
+
+#ifndef __REQUESTS_H_INCLUDED__
+#define __REQUESTS_H_INCLUDED__
+
+#define CUSTOM_RQ_SET_STATUS 1
+/* Set the LED status. Control-OUT.
+ * The requested status is passed in the "wValue" field of the control
+ * transfer. No OUT data is sent. Bit 0 of the low byte of wValue controls
+ * the LED.
+ */
+
+#define CUSTOM_RQ_GET_STATUS 2
+/* Get the current LED status. Control-IN.
+ * This control transfer involves a 1 byte data phase where the device sends
+ * the current status to the host. The status is in bit 0 of the byte.
+ */
+
+#endif /* __REQUESTS_H_INCLUDED__ */
diff --git a/examples/custom-class/make-files.sh b/examples/custom-class/make-files.sh
new file mode 100755
index 0000000..f6670fa
--- /dev/null
+++ b/examples/custom-class/make-files.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-17
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+if [ "$1" = remove ]; then
+ (cd firmware; make clean)
+ rm -f firmware/usbconfig.h
+ rm -rf firmware/usbdrv
+ rm -f commandline/Makefile.windows
+ rm -f commandline/Makefile
+ rm -f commandline/opendevice.[ch]
+ exit
+fi
+
+cat << \EOF | sed -n -f /dev/stdin ../../usbdrv/usbconfig-prototype.h >firmware/usbconfig.h
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^#define USB_CFG_DMINUS_BIT .*$/#define USB_CFG_DMINUS_BIT 4/g
+s|^.*#define USB_CFG_CLOCK_KHZ.*$|#define USB_CFG_CLOCK_KHZ (F_CPU/1000)|g
+s/^#define USB_CFG_DEVICE_NAME .*$/#define USB_CFG_DEVICE_NAME 'L', 'E', 'D', 'C', 'o', 'n', 't', 'r', 'o', 'l'/g
+s/^#define USB_CFG_DEVICE_NAME_LEN .*$/#define USB_CFG_DEVICE_NAME_LEN 10/g
+
+s/^#define USB_CFG_MAX_BUS_POWER .*$/#define USB_CFG_MAX_BUS_POWER 40/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../usbtool/Makefile.windows >commandline/Makefile.windows
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: custom-class example/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../usbtool/Makefile >commandline/Makefile
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: custom-class example/g
+s/^NAME = .*$/NAME = set-led/g
+p
+EOF
+
+cp ../../libs-host/opendevice.[ch] commandline/
diff --git a/examples/hid-custom-rq/Readme.txt b/examples/hid-custom-rq/Readme.txt
new file mode 100644
index 0000000..6a2ab3b
--- /dev/null
+++ b/examples/hid-custom-rq/Readme.txt
@@ -0,0 +1,28 @@
+This is the Readme file for the hid-custom-rq example. This is basically the
+same as the custom-class example, except that the device conforms to the USB
+HID class.
+
+
+WHAT IS DEMONSTRATED?
+=====================
+This example demonstrates how custom requests can be sent to devices which
+are otherwise HID compliant. This mechanism can be used to prevent the
+"driver CD" dialog on Windows and still control the device with libusb-win32.
+It can also be used to extend the functionality of the USB class, e.g. by
+setting parameters.
+
+Please note that you should install the filter version of libusb-win32 to
+take full advantage or this mode. The device driver version only has access
+to devices which have been registered for it with a *.inf file. The filter
+version has access to all devices.
+
+
+MORE INFORMATION
+================
+For information about how to build this example and how to use the command
+line tool see the Readme file in the custom-class example.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/hid-custom-rq/firmware/main.c b/examples/hid-custom-rq/firmware/main.c
new file mode 100644
index 0000000..85cae06
--- /dev/null
+++ b/examples/hid-custom-rq/firmware/main.c
@@ -0,0 +1,119 @@
+/* Name: main.c
+ * Project: hid-custom-rq example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-07
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+This example should run on most AVRs with only little changes. No special
+hardware resources except INT0 are used. You may have to change usbconfig.h for
+different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or
+at least be connected to INT0 as well.
+We assume that an LED is connected to port B bit 0. If you connect it to a
+different port or bit, change the macros below:
+*/
+#define LED_PORT_DDR DDRB
+#define LED_PORT_OUTPUT PORTB
+#define LED_BIT 0
+
+#include <avr/io.h>
+#include <avr/wdt.h>
+#include <avr/interrupt.h> /* for sei() */
+#include <util/delay.h> /* for _delay_ms() */
+
+#include <avr/pgmspace.h> /* required by usbdrv.h */
+#include "usbdrv.h"
+#include "oddebug.h" /* This is also an example for using debug macros */
+#include "requests.h" /* The custom request numbers we use */
+
+/* ------------------------------------------------------------------------- */
+/* ----------------------------- USB interface ----------------------------- */
+/* ------------------------------------------------------------------------- */
+
+PROGMEM char usbHidReportDescriptor[22] = { /* USB report descriptor */
+ 0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x01, // USAGE (Vendor Usage 1)
+ 0xa1, 0x01, // COLLECTION (Application)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x95, 0x01, // REPORT_COUNT (1)
+ 0x09, 0x00, // USAGE (Undefined)
+ 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
+ 0xc0 // END_COLLECTION
+};
+/* The descriptor above is a dummy only, it silences the drivers. The report
+ * it describes consists of one byte of undefined data.
+ * We don't transfer our data through HID reports, we use custom requests
+ * instead.
+ */
+
+/* ------------------------------------------------------------------------- */
+
+uchar usbFunctionSetup(uchar data[8])
+{
+usbRequest_t *rq = (void *)data;
+
+ if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_VENDOR){
+ DBG1(0x50, &rq->bRequest, 1); /* debug output: print our request */
+ if(rq->bRequest == CUSTOM_RQ_SET_STATUS){
+ if(rq->wValue.bytes[0] & 1){ /* set LED */
+ LED_PORT_OUTPUT |= _BV(LED_BIT);
+ }else{ /* clear LED */
+ LED_PORT_OUTPUT &= ~_BV(LED_BIT);
+ }
+ }else if(rq->bRequest == CUSTOM_RQ_GET_STATUS){
+ static uchar dataBuffer[1]; /* buffer must stay valid when usbFunctionSetup returns */
+ dataBuffer[0] = ((LED_PORT_OUTPUT & _BV(LED_BIT)) != 0);
+ usbMsgPtr = dataBuffer; /* tell the driver which data to return */
+ return 1; /* tell the driver to send 1 byte */
+ }
+ }else{
+ /* calss requests USBRQ_HID_GET_REPORT and USBRQ_HID_SET_REPORT are
+ * not implemented since we never call them. The operating system
+ * won't call them either because our descriptor defines no meaning.
+ */
+ }
+ return 0; /* default for not implemented requests: return no data back to host */
+}
+
+/* ------------------------------------------------------------------------- */
+
+int main(void)
+{
+uchar i;
+
+ wdt_enable(WDTO_1S);
+ /* Even if you don't use the watchdog, turn it off here. On newer devices,
+ * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
+ */
+ DBG1(0x00, 0, 0); /* debug output: main starts */
+ /* RESET status: all port bits are inputs without pull-up.
+ * That's the way we need D+ and D-. Therefore we don't need any
+ * additional hardware initialization.
+ */
+ odDebugInit();
+ usbInit();
+ usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
+ i = 0;
+ while(--i){ /* fake USB disconnect for > 250 ms */
+ wdt_reset();
+ _delay_ms(1);
+ }
+ usbDeviceConnect();
+ LED_PORT_DDR |= _BV(LED_BIT); /* make the LED bit an output */
+ sei();
+ DBG1(0x01, 0, 0); /* debug output: main loop starts */
+ for(;;){ /* main event loop */
+ DBG1(0x02, 0, 0); /* debug output: main loop iterates */
+ wdt_reset();
+ usbPoll();
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/examples/hid-custom-rq/firmware/requests.h b/examples/hid-custom-rq/firmware/requests.h
new file mode 100644
index 0000000..b6a3c2b
--- /dev/null
+++ b/examples/hid-custom-rq/firmware/requests.h
@@ -0,0 +1,32 @@
+/* Name: requests.h
+ * Project: custom-class, a basic USB example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-09
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/* This header is shared between the firmware and the host software. It
+ * defines the USB request numbers (and optionally data types) used to
+ * communicate between the host and the device.
+ */
+
+#ifndef __REQUESTS_H_INCLUDED__
+#define __REQUESTS_H_INCLUDED__
+
+#define CUSTOM_RQ_SET_STATUS 1
+/* Set the LED status. Control-OUT.
+ * The requested status is passed in the "wValue" field of the control
+ * transfer. No OUT data is sent. Bit 0 of the low byte of wValue controls
+ * the LED.
+ */
+
+#define CUSTOM_RQ_GET_STATUS 2
+/* Get the current LED status. Control-IN.
+ * This control transfer involves a 1 byte data phase where the device sends
+ * the current status to the host. The status is in bit 0 of the byte.
+ */
+
+#endif /* __REQUESTS_H_INCLUDED__ */
diff --git a/examples/hid-custom-rq/make-files.sh b/examples/hid-custom-rq/make-files.sh
new file mode 100755
index 0000000..b52dbf6
--- /dev/null
+++ b/examples/hid-custom-rq/make-files.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-17
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+if [ "$1" = remove ]; then
+ (cd firmware; make clean)
+ rm -f firmware/usbconfig.h
+ rm -rf firmware/usbdrv
+ rm -f firmware/Makefile
+ rm -f commandline/set-led.c
+ rm -f commandline/Makefile.windows
+ rm -f commandline/Makefile
+ rm -f commandline/opendevice.[ch]
+ exit
+fi
+
+cat << \EOF | sed -n -f /dev/stdin ../../usbdrv/usbconfig-prototype.h >firmware/usbconfig.h
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^#define USB_CFG_DMINUS_BIT .*$/#define USB_CFG_DMINUS_BIT 4/g
+s|^.*#define USB_CFG_CLOCK_KHZ.*$|#define USB_CFG_CLOCK_KHZ (F_CPU/1000)|g
+s/^#define USB_CFG_HAVE_INTRIN_ENDPOINT .*$/#define USB_CFG_HAVE_INTRIN_ENDPOINT 1/g
+s|^#define USB_CFG_DEVICE_ID .*$|#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */|g
+s/^#define USB_CFG_DEVICE_NAME .*$/#define USB_CFG_DEVICE_NAME 'L', 'E', 'D', 'C', 't', 'l', 'H', 'I', 'D'/g
+s/^#define USB_CFG_DEVICE_NAME_LEN .*$/#define USB_CFG_DEVICE_NAME_LEN 9/g
+
+s/^#define USB_CFG_INTR_POLL_INTERVAL .*$/#define USB_CFG_INTR_POLL_INTERVAL 100/g
+s/^#define USB_CFG_MAX_BUS_POWER .*$/#define USB_CFG_MAX_BUS_POWER 40/g
+s/^#define USB_CFG_DEVICE_CLASS .*$/#define USB_CFG_DEVICE_CLASS 0/g
+s/^#define USB_CFG_INTERFACE_CLASS .*$/#define USB_CFG_INTERFACE_CLASS 3/g
+s/^.*#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH.*$/#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../custom-class/firmware/Makefile >firmware/Makefile
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: hid-custom-rq example/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../custom-class/commandline/set-led.c >commandline/set-led.c
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^ [*] Project: .*$/ * Project: hid-custom-rq example/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../usbtool/Makefile.windows >commandline/Makefile.windows
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: hid-custom-rq example/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../usbtool/Makefile >commandline/Makefile
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: hid-custom-rq example/g
+s/^NAME = .*$/NAME = set-led/g
+p
+EOF
+
+cp ../../libs-host/opendevice.[ch] commandline/
diff --git a/examples/hid-data/Readme.txt b/examples/hid-data/Readme.txt
new file mode 100644
index 0000000..4f925a8
--- /dev/null
+++ b/examples/hid-data/Readme.txt
@@ -0,0 +1,75 @@
+This is the Readme file for the hid-data example. In this example, we show
+how blocks of data can be exchanged with the device using only functionality
+compliant to the HID class. Since class drivers for HID are included with
+Windows, you don't need to install drivers on Windows.
+
+
+WHAT IS DEMONSTRATED?
+=====================
+This example demonstrates how the HID class can be misused to transfer fixed
+size blocks of data (up to the driver's transfer size limit) over HID feature
+reports. This technique is of great value on Windows because no driver DLLs
+are needed (the hid-custom-rq example still requires the libusb-win32 DLL,
+although it may be in the program's directory). The host side application
+requires no installation, it can even be started directly from a CD. This
+example also demonstrates how to transfer data using usbFunctionWrite() and
+usbFunctionRead().
+
+
+PREREQUISITES
+=============
+Target hardware: You need an AVR based circuit based on one of the examples
+(see the "circuits" directory at the top level of this package), e.g. the
+metaboard (http://metalab.at/wiki/Metaboard).
+
+AVR development environment: You need the gcc tool chain for the AVR, see
+the Prerequisites section in the top level Readme file for how to obtain it.
+
+Host development environment: A C compiler and libusb on Unix. On Windows
+you need the Driver Development Kit (DDK) Instead of libusb. MinGW ships
+with a free version of the DDK.
+
+
+BUILDING THE FIRMWARE
+=====================
+Change to the "firmware" directory and modify Makefile according to your
+architecture (CPU clock, target device, fuse values) and ISP programmer. Then
+edit usbconfig.h according to your pin assignments for D+ and D-. The default
+settings are for the metaboard hardware.
+
+Type "make hex" to build main.hex, then "make flash" to upload the firmware
+to the device. Don't forget to run "make fuse" once to program the fuses. If
+you use a prototyping board with boot loader, follow the instructions of the
+boot loader instead.
+
+Please note that the first "make hex" copies the driver from the top level
+into the firmware directory. If you use a different build system than our
+Makefile, you must copy the driver by hand.
+
+
+BUILDING THE HOST SOFTWARE
+==========================
+Make sure that you have libusb (on Unix) or the DDK (on Windows) installed.
+We recommend MinGW on Windows since it includes a free version of the DDK.
+Then change to directory "commandline" and run "make" on Unix or
+"make -f Makefile.windows" on Windows.
+
+
+USING THE COMMAND LINE TOOL
+===========================
+The device implements a data store of 128 bytes in EEPROM. You can send a
+block of 128 bytes to the device or read the block using the command line
+tool.
+
+To send a block to the device, use e.g.
+
+ hidtool write 0x01,0x02,0x03,0x04,...
+
+and to receive the block, use
+
+ hidtool read
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/hid-data/commandline/Makefile b/examples/hid-data/commandline/Makefile
new file mode 100644
index 0000000..97c6b06
--- /dev/null
+++ b/examples/hid-data/commandline/Makefile
@@ -0,0 +1,42 @@
+# Name: Makefile
+# Project: hid-data example
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-11
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+# Please read the definitions below and edit them as appropriate for your
+# system:
+
+# Use the following 3 lines on Unix and Mac OS X:
+USBFLAGS= `libusb-config --cflags`
+USBLIBS= `libusb-config --libs`
+EXE_SUFFIX=
+
+# Use the following 3 lines on Windows and comment out the 3 above:
+#USBFLAGS=
+#USBLIBS= -lhid -lusb -lsetupapi
+#EXE_SUFFIX= .exe
+
+CC= gcc
+CFLAGS= -O -Wall $(USBFLAGS)
+LIBS= $(USBLIBS)
+
+OBJ= hidtool.o hiddata.o
+PROGRAM= hidtool$(EXE_SUFFIX)
+
+all: $(PROGRAM)
+
+$(PROGRAM): $(OBJ)
+ $(CC) -o $(PROGRAM) $(OBJ) $(LIBS)
+
+strip: $(PROGRAM)
+ strip $(PROGRAM)
+
+clean:
+ rm -f $(OBJ) $(PROGRAM)
+
+.c.o:
+ $(CC) $(ARCH_COMPILE) $(CFLAGS) -c $*.c -o $*.o
diff --git a/examples/hid-data/commandline/Makefile.windows b/examples/hid-data/commandline/Makefile.windows
new file mode 100644
index 0000000..f0e6cfa
--- /dev/null
+++ b/examples/hid-data/commandline/Makefile.windows
@@ -0,0 +1,18 @@
+# Name: Makefile.windows
+# Project: hid-data example
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-11
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+# You may use this file with
+# make -f Makefile.windows
+# on Windows with MinGW instead of editing the main Makefile.
+
+include Makefile
+
+USBFLAGS=
+USBLIBS= -lhid -lusb -lsetupapi
+EXE_SUFFIX= .exe
diff --git a/examples/hid-data/commandline/hidtool.c b/examples/hid-data/commandline/hidtool.c
new file mode 100644
index 0000000..d03bca8
--- /dev/null
+++ b/examples/hid-data/commandline/hidtool.c
@@ -0,0 +1,127 @@
+/* Name: hidtool.c
+ * Project: hid-data example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "hiddata.h"
+#include "../firmware/usbconfig.h" /* for device VID, PID, vendor name and product name */
+
+/* ------------------------------------------------------------------------- */
+
+static char *usbErrorMessage(int errCode)
+{
+static char buffer[80];
+
+ switch(errCode){
+ case USBOPEN_ERR_ACCESS: return "Access to device denied";
+ case USBOPEN_ERR_NOTFOUND: return "The specified device was not found";
+ case USBOPEN_ERR_IO: return "Communication error with device";
+ default:
+ sprintf(buffer, "Unknown USB error %d", errCode);
+ return buffer;
+ }
+ return NULL; /* not reached */
+}
+
+static usbDevice_t *openDevice(void)
+{
+usbDevice_t *dev = NULL;
+unsigned char rawVid[2] = {USB_CFG_VENDOR_ID}, rawPid[2] = {USB_CFG_DEVICE_ID};
+char vendorName[] = {USB_CFG_VENDOR_NAME, 0}, productName[] = {USB_CFG_DEVICE_NAME, 0};
+int vid = rawVid[0] + 256 * rawVid[1];
+int pid = rawPid[0] + 256 * rawPid[1];
+int err;
+
+ if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) != 0){
+ fprintf(stderr, "error finding %s: %s\n", productName, usbErrorMessage(err));
+ return NULL;
+ }
+ return dev;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void hexdump(char *buffer, int len)
+{
+int i;
+FILE *fp = stdout;
+
+ for(i = 0; i < len; i++){
+ if(i != 0){
+ if(i % 16 == 0){
+ fprintf(fp, "\n");
+ }else{
+ fprintf(fp, " ");
+ }
+ }
+ fprintf(fp, "0x%02x", buffer[i] & 0xff);
+ }
+ if(i != 0)
+ fprintf(fp, "\n");
+}
+
+static int hexread(char *buffer, char *string, int buflen)
+{
+char *s;
+int pos = 0;
+
+ while((s = strtok(string, ", ")) != NULL && pos < buflen){
+ string = NULL;
+ buffer[pos++] = (char)strtol(s, NULL, 0);
+ }
+ return pos;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void usage(char *myName)
+{
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s read\n", myName);
+ fprintf(stderr, " %s write <listofbytes>\n", myName);
+}
+
+int main(int argc, char **argv)
+{
+usbDevice_t *dev;
+char buffer[129]; /* room for dummy report ID */
+int err;
+
+ if(argc < 2){
+ usage(argv[0]);
+ exit(1);
+ }
+ if((dev = openDevice()) == NULL)
+ exit(1);
+ if(strcasecmp(argv[1], "read") == 0){
+ int len = sizeof(buffer);
+ if((err = usbhidGetReport(dev, 0, buffer, &len)) != 0){
+ fprintf(stderr, "error reading data: %s\n", usbErrorMessage(err));
+ }else{
+ hexdump(buffer + 1, sizeof(buffer) - 1);
+ }
+ }else if(strcasecmp(argv[1], "write") == 0){
+ int i, pos;
+ bzero(buffer, sizeof(buffer));
+ for(pos = 1, i = 2; i < argc && pos < sizeof(buffer); i++){
+ pos += hexread(buffer + pos, argv[i], sizeof(buffer) - pos);
+ }
+ if((err = usbhidSetReport(dev, buffer, sizeof(buffer))) != 0) /* add a dummy report ID */
+ fprintf(stderr, "error writing data: %s\n", usbErrorMessage(err));
+ }else{
+ usage(argv[0]);
+ exit(1);
+ }
+ usbhidCloseDevice(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/examples/hid-data/firmware/main.c b/examples/hid-data/firmware/main.c
new file mode 100644
index 0000000..3ef68d5
--- /dev/null
+++ b/examples/hid-data/firmware/main.c
@@ -0,0 +1,141 @@
+/* Name: main.c
+ * Project: hid-data, example how to use HID for data transfer
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+This example should run on most AVRs with only little changes. No special
+hardware resources except INT0 are used. You may have to change usbconfig.h for
+different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or
+at least be connected to INT0 as well.
+*/
+
+#include <avr/io.h>
+#include <avr/wdt.h>
+#include <avr/interrupt.h> /* for sei() */
+#include <util/delay.h> /* for _delay_ms() */
+#include <avr/eeprom.h>
+
+#include <avr/pgmspace.h> /* required by usbdrv.h */
+#include "usbdrv.h"
+#include "oddebug.h" /* This is also an example for using debug macros */
+
+/* ------------------------------------------------------------------------- */
+/* ----------------------------- USB interface ----------------------------- */
+/* ------------------------------------------------------------------------- */
+
+PROGMEM char usbHidReportDescriptor[22] = { /* USB report descriptor */
+ 0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x01, // USAGE (Vendor Usage 1)
+ 0xa1, 0x01, // COLLECTION (Application)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x95, 0x80, // REPORT_COUNT (128)
+ 0x09, 0x00, // USAGE (Undefined)
+ 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf)
+ 0xc0 // END_COLLECTION
+};
+/* Since we define only one feature report, we don't use report-IDs (which
+ * would be the first byte of the report). The entire report consists of 128
+ * opaque data bytes.
+ */
+
+/* The following variables store the status of the current data transfer */
+static uchar currentAddress;
+static uchar bytesRemaining;
+
+/* ------------------------------------------------------------------------- */
+
+/* usbFunctionRead() is called when the host requests a chunk of data from
+ * the device. For more information see the documentation in usbdrv/usbdrv.h.
+ */
+uchar usbFunctionRead(uchar *data, uchar len)
+{
+ if(len > bytesRemaining)
+ len = bytesRemaining;
+ eeprom_read_block(data, (uchar *)0 + currentAddress, len);
+ currentAddress += len;
+ bytesRemaining -= len;
+ return len;
+}
+
+/* usbFunctionWrite() is called when the host sends a chunk of data to the
+ * device. For more information see the documentation in usbdrv/usbdrv.h.
+ */
+uchar usbFunctionWrite(uchar *data, uchar len)
+{
+ if(bytesRemaining == 0)
+ return 1; /* end of transfer */
+ if(len > bytesRemaining)
+ len = bytesRemaining;
+ eeprom_write_block(data, (uchar *)0 + currentAddress, len);
+ currentAddress += len;
+ bytesRemaining -= len;
+ return bytesRemaining == 0; /* return 1 if this was the last chunk */
+}
+
+/* ------------------------------------------------------------------------- */
+
+uchar usbFunctionSetup(uchar data[8])
+{
+usbRequest_t *rq = (void *)data;
+
+ if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* HID class request */
+ if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
+ /* since we have only one report type, we can ignore the report-ID */
+ bytesRemaining = 128;
+ currentAddress = 0;
+ return 0xff; /* use usbFunctionRead() to obtain data */
+ }else if(rq->bRequest == USBRQ_HID_SET_REPORT){
+ /* since we have only one report type, we can ignore the report-ID */
+ bytesRemaining = 128;
+ currentAddress = 0;
+ return 0xff; /* use usbFunctionWrite() to receive data from host */
+ }
+ }else{
+ /* ignore vendor type requests, we don't use any */
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int main(void)
+{
+uchar i;
+
+ wdt_enable(WDTO_1S);
+ /* Even if you don't use the watchdog, turn it off here. On newer devices,
+ * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
+ */
+ DBG1(0x00, 0, 0); /* debug output: main starts */
+ /* RESET status: all port bits are inputs without pull-up.
+ * That's the way we need D+ and D-. Therefore we don't need any
+ * additional hardware initialization.
+ */
+ odDebugInit();
+ usbInit();
+ usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
+ i = 0;
+ while(--i){ /* fake USB disconnect for > 250 ms */
+ wdt_reset();
+ _delay_ms(1);
+ }
+ usbDeviceConnect();
+ sei();
+ DBG1(0x01, 0, 0); /* debug output: main loop starts */
+ for(;;){ /* main event loop */
+ DBG1(0x02, 0, 0); /* debug output: main loop iterates */
+ wdt_reset();
+ usbPoll();
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/examples/hid-data/make-files.sh b/examples/hid-data/make-files.sh
new file mode 100755
index 0000000..a8d524c
--- /dev/null
+++ b/examples/hid-data/make-files.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-17
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+if [ "$1" = remove ]; then
+ (cd firmware; make clean)
+ rm -f firmware/usbconfig.h
+ rm -rf firmware/usbdrv
+ rm -f firmware/Makefile
+ rm -f commandline/hiddata.[ch]
+ rm -f commandline/hidsdi.h
+ exit
+fi
+
+cat << \EOF | sed -n -f /dev/stdin ../../usbdrv/usbconfig-prototype.h >firmware/usbconfig.h
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^#define USB_CFG_DMINUS_BIT .*$/#define USB_CFG_DMINUS_BIT 4/g
+s|^.*#define USB_CFG_CLOCK_KHZ.*$|#define USB_CFG_CLOCK_KHZ (F_CPU/1000)|g
+s/^#define USB_CFG_HAVE_INTRIN_ENDPOINT .*$/#define USB_CFG_HAVE_INTRIN_ENDPOINT 1/g
+s|^#define USB_CFG_DEVICE_ID .*$|#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */|g
+s/^#define USB_CFG_DEVICE_NAME .*$/#define USB_CFG_DEVICE_NAME 'D', 'a', 't', 'a', 'S', 't', 'o', 'r', 'e'/g
+s/^#define USB_CFG_DEVICE_NAME_LEN .*$/#define USB_CFG_DEVICE_NAME_LEN 9/g
+
+s/^#define USB_CFG_INTR_POLL_INTERVAL .*$/#define USB_CFG_INTR_POLL_INTERVAL 100/g
+s/^#define USB_CFG_MAX_BUS_POWER .*$/#define USB_CFG_MAX_BUS_POWER 20/g
+s/^#define USB_CFG_IMPLEMENT_FN_WRITE .*$/#define USB_CFG_IMPLEMENT_FN_WRITE 1/g
+s/^#define USB_CFG_IMPLEMENT_FN_READ .*$/#define USB_CFG_IMPLEMENT_FN_READ 1/g
+s/^#define USB_CFG_DEVICE_CLASS .*$/#define USB_CFG_DEVICE_CLASS 0/g
+s/^#define USB_CFG_INTERFACE_CLASS .*$/#define USB_CFG_INTERFACE_CLASS 3/g
+s/^.*#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH.*$/#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../custom-class/firmware/Makefile >firmware/Makefile
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: hid-data example/g
+p
+EOF
+
+cp ../../libs-host/hiddata.[ch] ../../libs-host/hidsdi.h commandline
diff --git a/examples/hid-mouse/Readme.txt b/examples/hid-mouse/Readme.txt
new file mode 100644
index 0000000..1e269dc
--- /dev/null
+++ b/examples/hid-mouse/Readme.txt
@@ -0,0 +1,48 @@
+This is the Readme file for hid-mouse, an example of a USB mouse device. In
+order to have as little dependencies on hardware and architecture as
+possible, mouse movements are computed internally so that the mouse pointer
+moves in a circle.
+
+
+WHAT IS DEMONSTRATED?
+=====================
+This example demonstrates how HID class devices are implemented. The example
+is kept as simple as possible, except the report descriptor which is taken
+from a real-world mouse.
+
+It does NOT include a host side driver because all modern operating systems
+include one. It does NOT implement USBRQ_HID_SET_REPORT and report-IDs. See
+the "hid-data" example for this topic. It does NOT implement any special
+features such as suspend mode etc.
+
+
+PREREQUISITES
+=============
+Target hardware: You need an AVR based circuit based on one of the examples
+(see the "circuits" directory at the top level of this package), e.g. the
+metaboard (http://metalab.at/wiki/Metaboard).
+
+AVR development environment: You need the gcc tool chain for the AVR, see
+the Prerequisites section in the top level Readme file for how to obtain it.
+
+
+BUILDING THE FIRMWARE
+=====================
+Change to the "firmware" directory and modify Makefile according to your
+architecture (CPU clock, target device, fuse values) and ISP programmer. Then
+edit usbconfig.h according to your pin assignments for D+ and D-. The default
+settings are for the metaboard hardware.
+
+Type "make hex" to build main.hex, then "make flash" to upload the firmware
+to the device. Don't forget to run "make fuse" once to program the fuses. If
+you use a prototyping board with boot loader, follow the instructions of the
+boot loader instead.
+
+Please note that the first "make hex" copies the driver from the top level
+into the firmware directory. If you use a different build system than our
+Makefile, you must copy the driver by hand.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/hid-mouse/firmware/main.c b/examples/hid-mouse/firmware/main.c
new file mode 100644
index 0000000..0f3fd42
--- /dev/null
+++ b/examples/hid-mouse/firmware/main.c
@@ -0,0 +1,165 @@
+/* Name: main.c
+ * Project: hid-mouse, a very simple HID example
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-07
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+This example should run on most AVRs with only little changes. No special
+hardware resources except INT0 are used. You may have to change usbconfig.h for
+different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or
+at least be connected to INT0 as well.
+
+We use VID/PID 0x046D/0xC00E which is taken from a Logitech mouse. Don't
+publish any hardware using these IDs! This is for demonstration only!
+*/
+
+#include <avr/io.h>
+#include <avr/wdt.h>
+#include <avr/interrupt.h> /* for sei() */
+#include <util/delay.h> /* for _delay_ms() */
+
+#include <avr/pgmspace.h> /* required by usbdrv.h */
+#include "usbdrv.h"
+#include "oddebug.h" /* This is also an example for using debug macros */
+
+/* ------------------------------------------------------------------------- */
+/* ----------------------------- USB interface ----------------------------- */
+/* ------------------------------------------------------------------------- */
+
+PROGMEM char usbHidReportDescriptor[52] = { /* USB report descriptor, size must match usbconfig.h */
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x02, // USAGE (Mouse)
+ 0xa1, 0x01, // COLLECTION (Application)
+ 0x09, 0x01, // USAGE (Pointer)
+ 0xA1, 0x00, // COLLECTION (Physical)
+ 0x05, 0x09, // USAGE_PAGE (Button)
+ 0x19, 0x01, // USAGE_MINIMUM
+ 0x29, 0x03, // USAGE_MAXIMUM
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1)
+ 0x95, 0x03, // REPORT_COUNT (3)
+ 0x75, 0x01, // REPORT_SIZE (1)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x95, 0x01, // REPORT_COUNT (1)
+ 0x75, 0x05, // REPORT_SIZE (5)
+ 0x81, 0x03, // INPUT (Const,Var,Abs)
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x30, // USAGE (X)
+ 0x09, 0x31, // USAGE (Y)
+ 0x09, 0x38, // USAGE (Wheel)
+ 0x15, 0x81, // LOGICAL_MINIMUM (-127)
+ 0x25, 0x7F, // LOGICAL_MAXIMUM (127)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x95, 0x03, // REPORT_COUNT (3)
+ 0x81, 0x06, // INPUT (Data,Var,Rel)
+ 0xC0, // END_COLLECTION
+ 0xC0, // END COLLECTION
+};
+/* This is the same report descriptor as seen in a Logitech mouse. The data
+ * described by this descriptor consists of 4 bytes:
+ * . . . . . B2 B1 B0 .... one byte with mouse button states
+ * X7 X6 X5 X4 X3 X2 X1 X0 .... 8 bit signed relative coordinate x
+ * Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 .... 8 bit signed relative coordinate y
+ * W7 W6 W5 W4 W3 W2 W1 W0 .... 8 bit signed relative coordinate wheel
+ */
+typedef struct{
+ uchar buttonMask;
+ char dx;
+ char dy;
+ char dWheel;
+}report_t;
+
+static report_t reportBuffer;
+static int sinus = 7 << 6, cosinus = 0;
+static uchar idleRate; /* repeat rate for keyboards, never used for mice */
+
+
+/* The following function advances sin/cos by a fixed angle
+ * and stores the difference to the previous coordinates in the report
+ * descriptor.
+ * The algorithm is the simulation of a second order differential equation.
+ */
+static void advanceCircleByFixedAngle(void)
+{
+char d;
+
+#define DIVIDE_BY_64(val) (val + (val > 0 ? 32 : -32)) >> 6 /* rounding divide */
+ reportBuffer.dx = d = DIVIDE_BY_64(cosinus);
+ sinus += d;
+ reportBuffer.dy = d = DIVIDE_BY_64(sinus);
+ cosinus -= d;
+}
+
+/* ------------------------------------------------------------------------- */
+
+uchar usbFunctionSetup(uchar data[8])
+{
+usbRequest_t *rq = (void *)data;
+
+ /* The following requests are never used. But since they are required by
+ * the specification, we implement them in this example.
+ */
+ if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
+ DBG1(0x50, &rq->bRequest, 1); /* debug output: print our request */
+ if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
+ /* we only have one report type, so don't look at wValue */
+ usbMsgPtr = (void *)&reportBuffer;
+ return sizeof(reportBuffer);
+ }else if(rq->bRequest == USBRQ_HID_GET_IDLE){
+ usbMsgPtr = &idleRate;
+ return 1;
+ }else if(rq->bRequest == USBRQ_HID_SET_IDLE){
+ idleRate = rq->wValue.bytes[1];
+ }
+ }else{
+ /* no vendor specific requests implemented */
+ }
+ return 0; /* default for not implemented requests: return no data back to host */
+}
+
+/* ------------------------------------------------------------------------- */
+
+int main(void)
+{
+uchar i;
+
+ wdt_enable(WDTO_1S);
+ /* Even if you don't use the watchdog, turn it off here. On newer devices,
+ * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
+ */
+ DBG1(0x00, 0, 0); /* debug output: main starts */
+ /* RESET status: all port bits are inputs without pull-up.
+ * That's the way we need D+ and D-. Therefore we don't need any
+ * additional hardware initialization.
+ */
+ odDebugInit();
+ usbInit();
+ usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
+ i = 0;
+ while(--i){ /* fake USB disconnect for > 250 ms */
+ wdt_reset();
+ _delay_ms(1);
+ }
+ usbDeviceConnect();
+ sei();
+ DBG1(0x01, 0, 0); /* debug output: main loop starts */
+ for(;;){ /* main event loop */
+ DBG1(0x02, 0, 0); /* debug output: main loop iterates */
+ wdt_reset();
+ usbPoll();
+ if(usbInterruptIsReady()){
+ /* called after every poll of the interrupt endpoint */
+ advanceCircleByFixedAngle();
+ DBG1(0x03, 0, 0); /* debug output: interrupt report prepared */
+ usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/examples/hid-mouse/make-files.sh b/examples/hid-mouse/make-files.sh
new file mode 100755
index 0000000..557350b
--- /dev/null
+++ b/examples/hid-mouse/make-files.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-17
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+if [ "$1" = remove ]; then
+ (cd firmware; make clean)
+ rm -f firmware/usbconfig.h
+ rm -rf firmware/usbdrv
+ rm -f firmware/Makefile
+ exit
+fi
+
+cat << \EOF | sed -n -f /dev/stdin ../../usbdrv/usbconfig-prototype.h >firmware/usbconfig.h
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^#define USB_CFG_DMINUS_BIT .*$/#define USB_CFG_DMINUS_BIT 4/g
+s|^.*#define USB_CFG_CLOCK_KHZ.*$|#define USB_CFG_CLOCK_KHZ (F_CPU/1000)|g
+s/^#define USB_CFG_HAVE_INTRIN_ENDPOINT .*$/#define USB_CFG_HAVE_INTRIN_ENDPOINT 1/g
+s|^#define USB_CFG_DEVICE_ID .*$|#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */|g
+s/^#define USB_CFG_DEVICE_NAME .*$/#define USB_CFG_DEVICE_NAME 'D', 'a', 't', 'a', 'S', 't', 'o', 'r', 'e'/g
+s/^#define USB_CFG_DEVICE_NAME_LEN .*$/#define USB_CFG_DEVICE_NAME_LEN 9/g
+
+s/^#define USB_CFG_INTR_POLL_INTERVAL .*$/#define USB_CFG_INTR_POLL_INTERVAL 100/g
+s/^#define USB_CFG_MAX_BUS_POWER .*$/#define USB_CFG_MAX_BUS_POWER 20/g
+s/^#define USB_CFG_DEVICE_CLASS .*$/#define USB_CFG_DEVICE_CLASS 0/g
+s/^#define USB_CFG_INTERFACE_CLASS .*$/#define USB_CFG_INTERFACE_CLASS 3/g
+s/^.*#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH.*$/#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 52/g
+p
+EOF
+
+cat << \EOF | sed -n -f /dev/stdin ../custom-class/firmware/Makefile >firmware/Makefile
+/^\( [*] \)\{0,1\}[+].*$/ d
+s/^# Project: .*$/# Project: hid-mouse example/g
+p
+EOF
diff --git a/examples/usbtool/Makefile b/examples/usbtool/Makefile
new file mode 100644
index 0000000..f79fc03
--- /dev/null
+++ b/examples/usbtool/Makefile
@@ -0,0 +1,48 @@
+# Name: Makefile
+# Project: usbtool
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-06
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+# Concigure the following definitions according to your system.
+# This Makefile has been tested on Mac OS X, Linux and Windows.
+
+# Use the following 3 lines on Unix (uncomment the framework on Mac OS X):
+USBFLAGS = `libusb-config --cflags`
+USBLIBS = `libusb-config --libs`
+EXE_SUFFIX =
+
+# Use the following 3 lines on Windows and comment out the 3 above. You may
+# have to change the include paths to where you installed libusb-win32
+#USBFLAGS = -I/usr/local/include
+#USBLIBS = -L/usr/local/lib -lusb
+#EXE_SUFFIX = .exe
+
+NAME = usbtool
+
+OBJECTS = opendevice.o $(NAME).o
+
+CC = gcc
+CFLAGS = $(USBFLAGS) -O -g -Wall
+LIBS = $(USBLIBS)
+
+PROGRAM = $(NAME)$(EXE_SUFFIX)
+
+
+all: $(PROGRAM)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+$(PROGRAM): $(OBJECTS)
+ $(CC) -o $(PROGRAM) $(OBJECTS) $(LIBS)
+
+strip: $(PROGRAM)
+ strip $(PROGRAM)
+
+clean:
+ rm -f *.o $(PROGRAM)
diff --git a/examples/usbtool/Makefile.windows b/examples/usbtool/Makefile.windows
new file mode 100644
index 0000000..1c4aaa1
--- /dev/null
+++ b/examples/usbtool/Makefile.windows
@@ -0,0 +1,18 @@
+# Name: Makefile.windows
+# Project: usbtool
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-06
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+# You may use this file with
+# make -f Makefile.windows
+# on Windows with MinGW instead of editing the main Makefile.
+
+include Makefile
+
+USBFLAGS = -I/usr/local/mingw/include
+USBLIBS = -L/usr/local/mingw/lib -lusb
+EXE_SUFFIX = .exe
diff --git a/examples/usbtool/Readme.txt b/examples/usbtool/Readme.txt
new file mode 100644
index 0000000..33f527c
--- /dev/null
+++ b/examples/usbtool/Readme.txt
@@ -0,0 +1,209 @@
+This is the Readme file for usbtool, a general purpose command line utility
+which can send USB requests to arbitrary devices. Usbtool is based on libusb.
+
+
+WHAT IS USBTOOL GOOD FOR?
+=========================
+When you implement a communication protocol like USB, you must usually write
+two programs: one on each end of the communication. For USB, this means that
+you must write a firmware for the device and driver software for the host.
+
+Usbtool can save you the work of writing the host software, at least during
+firmware development and testing. Usbtool can send control-in and -out
+requests to arbitrary devices and send and receive data on interrupt- and
+bulk-endpoints.
+
+Usbtool is not only a useful developer tool, it's also an example for using
+libusb for communication with the device.
+
+
+SYNOPSIS
+========
+ usbtool [options] <command>
+
+
+COMMANDS
+========
+ list
+ This command prints a list of devices found on all available USB busses.
+ Options -v, -V, -p and -P can be used to filter the list.
+
+ control in|out <type> <recipient> <request> <value> <index>
+ Sends a control-in or control-out request to the device. The request
+ parameters are:
+ type ........ Type of request, can be "standard", "class", "vendor" or
+ "reserved". The type determines which software module in
+ the device is responsible for answering the request:
+ Standard requests are answered by the driver, class
+ requests by the class implementation (e.g. HID, CDC) and
+ vendor requests by custom code.
+ recipient ... Recipient of the request in the device. Can be "device",
+ "interface", "endpoint" or "other". For standard and
+ class requests, the specification defines a recipient for
+ each request. For vendor requests, choose whatever your
+ code expects.
+ request ..... 8 bit numeric value identifying the request.
+ value ....... 16 bit numeric value passed to the device.
+ index ....... another 16 bit numeric value passed to the device.
+ Use options -v, -V, -p and -P to single out a particular device. Use
+ options -d or -D to to send data in an OUT request. Use options -n, -O
+ and -b to determine what to do with data received in an IN request.
+
+ interrupt in|out
+ Sends or receives data on an interrupt-out resp. -in endpoint.
+ Use options -v, -V, -p and -P to single out a particular device. Use
+ options -d or -D to to send data to an OUT endpoint. Use options -n, -O
+ and -b to determine what to do with data received from an IN endpoint.
+ Use option -e to set the endpoint number, -c to choose a configuration
+ -i to claim a particular interface.
+
+ bulk in|out
+ Same as "interrupt in" and "interrupt out", but for bulk endpoints.
+
+
+OPTIONS
+=======
+Most options have already been mentioned at the commands which use them.
+here is a complete list:
+
+ -h or -?
+ Prints a short help.
+
+ -v <vendor-id>
+ Numeric vendor ID, can be "*" to allow any VID. Take only devices with
+ matching vendor ID into account.
+
+ -p <product-id>
+ Numeric product ID, can be "*" to allow any PID. Take only devices with
+ matching product ID into account.
+
+ -V <vendor-name-pattern>
+ Shell style matching pattern for vendor name. Take only devices into
+ account which have a vendor name that matches this pattern.
+
+ -P <product-name-pattern>
+ Shell style matching pattern for product name. Take only devices into
+ account which have a product name that matches this pattern.
+
+ -S <serial-pattern>
+ Shell style matching pattern for serial number. Take only devices into
+ account which have a serial number that matches this pattern.
+
+ -d <databytes>
+ Data bytes to send to the device, comma separated list of numeric values.
+ E.g.: "1,2,3,4,5".
+
+ -D <file>
+ Binary data sent to the device should be taken from this file.
+
+ -O <file>
+ Write received data bytes to the given file. Format is either hex or
+ binary, depending on the -b flag. By default, received data is printed
+ to standard output.
+
+ -b
+ Request binary output format for files and standard output. Default is
+ a hexadecimal listing.
+
+ -n <count>
+ Numeric value: Maximum number of bytes to receive. This value is passed
+ directly to the libusb API functions.
+
+ -e <endpoint>
+ Numeric value: Endpoint number for interrupt and bulk commands.
+
+ -t <timeout>
+ Numeric value: Timeout in milliseconds for the request. This value is
+ passed directly to the libusb API functions.
+
+ -c <configuration>
+ Numeric value: Interrupt and bulk endpoints can usually only be used if
+ a configuration and an interface has been chosen. Use -c and -i to
+ specify configuration and interface values.
+
+ -i <interface>
+ Numeric value: Interrupt and bulk endpoints can usually only be used if
+ a configuration and an interface has been chosen. Use -c and -i to
+ specify configuration and interface values.
+
+ -w
+ Usbtool may be too verbose with warnings for some applications. Use this
+ option to suppress USB warnings.
+
+
+NUMERIC VALUES
+==============
+All numeric values can be given in hexadecimal, decimal or octal. Hex values
+are identified by their 0x or 0X prefix, octal values by a leading "0" (the
+digit zero) and decimal values because they start with a non-zero digit. An
+optional sign character is allowed. The special value "*" is translated to
+zero and stands for "any value" in some contexts.
+
+
+SHELL STYLE MATCHING PATTERNS
+=============================
+Some options take shell style matching patterns as an argument. This refers
+to Unix shells and their file wildcard operations:
+ + "*" (asterisk character) matches any number (0 to infinite) of any
+ characters.
+ + "?" matches exactly one arbitrary character.
+ + A list of characters in square brackets (e.g. "[abc]") matches any of the
+ characters in the list. If a dash ("-") is in the list, it must be the
+ first or the last character. If a caret ("^") is in the list, it must
+ not be the first character. A closing square bracket ("]") must be the
+ first character in the list. A range of characters can be specified in
+ the way "[a-z]". This matches all characters with numeric representation
+ (usually ASCII) starting with "a" and ending with "z". The entire
+ construct matches only one character.
+ + A list of characters in square brackets starting with a caret ("^"), e.g.
+ ("[^abc]") matches any character NOT in the list. The other rules are as
+ above.
+ + "\" (backslash) followed by any character matches that following
+ character. This can be used to escape "*", "?", "[" and "\".
+
+
+BUILDING USBTOOL
+================
+Usbtool uses libusb on Unix and libusb-win32 on Windows. These libraries can
+be obtained from http://libusb.sourceforge.net/ and
+http://libusb-win32.sourceforge.net/ respectively. On Unix, a simple "make"
+should compile the sources (although you may have to edit Makefile to
+include or remove additional libraries). On Windows, we recommend that you
+use MinGW and MSYS. See the top level Readme file for details. Edit
+Makefile.windows according to your library installation paths and build with
+"make -f Makefile.windows".
+
+
+EXAMPLES
+========
+To list all devices connected to your computer, do
+
+ usbtool -w list
+
+To check whether our selection options single out the desired device, use eg.
+
+ usbtool -w -P LEDControl list
+
+This command shows all LEDControl devices connected or prints nothing if
+none is found. LEDControl is the device from the "custom-class" example.
+
+You can also send commands to the LEDControl device using usbtool. From
+the file requests.h in custom-class/firmware, we know that the set-status
+request has numeric value 1 and the get-status request is 2. See this file
+for details of the protocol used. We can therefore query the status with
+
+ usbtool -w -P LEDControl control in vendor device 2 0 0
+
+This command prints 0x00 if the LED is off or 0x01 if it is on. To turn the
+LED on, use
+
+ usbtool -w -P LEDControl control out vendor device 1 1 0
+
+and to turn it off, use
+
+ usbtool -w -P LEDControl control out vendor device 1 0 0
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/examples/usbtool/make-files.sh b/examples/usbtool/make-files.sh
new file mode 100755
index 0000000..0ba183e
--- /dev/null
+++ b/examples/usbtool/make-files.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-17
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+# This Revision: $Id$
+
+
+if [ "$1" = remove ]; then
+ make clean
+ rm -f opendevice.[ch]
+ exit
+fi
+
+cp ../../libs-host/opendevice.[ch] .
diff --git a/examples/usbtool/usbtool.c b/examples/usbtool/usbtool.c
new file mode 100644
index 0000000..fe29a06
--- /dev/null
+++ b/examples/usbtool/usbtool.c
@@ -0,0 +1,356 @@
+/* Name: usbtool.c
+ * Project: AVR-USB examples, host side
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-06
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description:
+This command line tool can perform various USB requests at arbitrary
+USB devices. It is intended as universal host side tool for experimentation
+and debugging purposes. It must be linked with libusb, a library for accessing
+the USB bus from Linux, FreeBSD, Mac OS X and other Unix operating systems.
+Libusb can be obtained from http://libusb.sourceforge.net/.
+On Windows use libusb-win32 from http://libusb-win32.sourceforge.net/.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */
+#include "opendevice.h" /* common code moved to separate module */
+
+#define DEFAULT_USB_VID 0 /* any */
+#define DEFAULT_USB_PID 0 /* any */
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s [options] <command>\n", name);
+ fprintf(stderr,
+ "Options are:\n"
+ " -h or -? (print this help and exit)\n"
+ " -v <vendor-id> (defaults to 0x%x, can be '*' for any VID)\n"
+ " -p <product-id> (defaults to 0x%x, can be '*' for any PID)\n"
+ " -V <vendor-name-pattern> (shell style matching, defaults to '*')\n"
+ " -P <product-name-pattern> (shell style matching, defaults to '*')\n"
+ " -S <serial-pattern> (shell style matching, defaults to '*')\n"
+ " -d <databytes> (data byte for request, comma separated list)\n"
+ " -D <file> (binary data for request taken from file)\n"
+ " -O <file> (write received data bytes to file)\n"
+ " -b (binary output format, default is hex)\n"
+ " -n <count> (maximum number of bytes to receive)\n"
+ " -e <endpoint> (specify endpoint for some commands)\n"
+ " -t <timeout> (specify USB timeout in milliseconds)\n"
+ " -c <configuration> (device configuration to choose)\n"
+ " -i <interface> (configuration interface to claim)\n"
+ " -w (suppress USB warnings, default is verbose)\n"
+ "\n"
+ "Commands are:\n"
+ " list (list all matching devices by name)\n"
+ " control in|out <type> <recipient> <request> <value> <index> (send control request)\n"
+ " interrupt in|out (send or receive interrupt data)\n"
+ " bulk in|out (send or receive bulk data)\n"
+ "For valid enum values for <type> and <recipient> pass \"x\" for the value.\n"
+ "Objective Development's free VID/PID pairs are:\n"
+ " 5824/1500 for vendor class devices\n"
+ " 5824/1503 for HID class devices excluding mice and keyboards\n"
+ " 5824/1505 for CDC-ACM class devices\n"
+ " 5824/1508 for MIDI class devices\n"
+ , DEFAULT_USB_VID, DEFAULT_USB_PID
+ );
+
+
+}
+
+static int vendorID = DEFAULT_USB_VID;
+static int productID = DEFAULT_USB_PID;
+static char *vendorNamePattern = "*";
+static char *productNamePattern = "*";
+static char *serialPattern = "*";
+static char *sendBytes = NULL;
+static int sendByteCount;
+static char *outputFile = NULL;
+static int endpoint = 0;
+static int outputFormatIsBinary = 0;
+static int showWarnings = 1;
+static int usbTimeout = 5000;
+static int usbCount = 128;
+static int usbConfiguration = 1;
+static int usbInterface = 0;
+
+static int usbDirection, usbType, usbRecipient, usbRequest, usbValue, usbIndex; /* arguments of control transfer */
+
+/* ------------------------------------------------------------------------- */
+
+/* ASCII to integer (number parsing) which allows hex (0x prefix),
+ * octal (0 prefix) and decimal (1-9 prefix) input.
+ */
+static int myAtoi(char *text)
+{
+long l;
+char *endPtr;
+
+ if(strcmp(text, "*") == 0)
+ return 0;
+ l = strtol(text, &endPtr, 0);
+ if(endPtr == text){
+ fprintf(stderr, "warning: can't parse numeric parameter ->%s<-, defaults to 0.\n", text);
+ l = 0;
+ }else if(*endPtr != 0){
+ fprintf(stderr, "warning: numeric parameter ->%s<- only partially parsed.\n", text);
+ }
+ return l;
+}
+
+static int parseEnum(char *text, ...)
+{
+va_list vlist;
+char *entries[64];
+int i, numEntries;
+
+ va_start(vlist, text);
+ for(i = 0; i < 64; i++){
+ entries[i] = va_arg(vlist, char *);
+ if(entries[i] == NULL)
+ break;
+ }
+ numEntries = i;
+ va_end(vlist);
+ for(i = 0; i < numEntries; i++){
+ if(strcasecmp(text, entries[i]) == 0)
+ return i;
+ }
+ if(isdigit(*text)){
+ return myAtoi(text);
+ }
+ fprintf(stderr, "Enum value \"%s\" not allowed. Allowed values are:\n", text);
+ for(i = 0; i < numEntries; i++){
+ fprintf(stderr, " %s\n", entries[i]);
+ }
+ exit(1);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#define ACTION_LIST 0
+#define ACTION_CONTROL 1
+#define ACTION_INTERRUPT 2
+#define ACTION_BULK 3
+
+int main(int argc, char **argv)
+{
+usb_dev_handle *handle = NULL;
+int opt, len, action, argcnt;
+char *myName = argv[0], *s, *rxBuffer = NULL;
+FILE *fp;
+
+ while((opt = getopt(argc, argv, "?hv:p:V:P:S:d:D:O:e:n:tbw")) != -1){
+ switch(opt){
+ case 'h':
+ case '?': /* -h or -? (print this help and exit) */
+ usage(myName);
+ exit(1);
+ case 'v': /* -v <vendor-id> (defaults to 0x%x, can be '*' for any VID) */
+ vendorID = myAtoi(optarg);
+ break;
+ case 'p': /* -p <product-id> (defaults to 0x%x, can be '*' for any PID) */
+ productID = myAtoi(optarg);
+ break;
+ case 'V': /* -V <vendor-name-pattern> (shell style matching, defaults to '*') */
+ vendorNamePattern = optarg;
+ break;
+ case 'P': /* -P <product-name-pattern> (shell style matching, defaults to '*') */
+ productNamePattern = optarg;
+ break;
+ case 'S': /* -S <serial-pattern> (shell style matching, defaults to '*') */
+ serialPattern = optarg;
+ break;
+ case 'd': /* -d <databytes> (data bytes for requests given on command line) */
+ while((s = strtok(optarg, ", ")) != NULL){
+ optarg = NULL;
+ if(sendBytes != NULL){
+ sendBytes = realloc(sendBytes, sendByteCount + 1);
+ }else{
+ sendBytes = malloc(sendByteCount + 1);
+ }
+ sendBytes[sendByteCount++] = myAtoi(s);
+ }
+ break;
+ case 'D': /* -D <file> (data bytes for request taken from file) */
+ if((fp = fopen(optarg, "rb")) == NULL){
+ fprintf(stderr, "error opening %s: %s\n", optarg, strerror(errno));
+ exit(1);
+ }
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if(sendBytes != NULL){
+ sendBytes = realloc(sendBytes, sendByteCount + len);
+ }else{
+ sendBytes = malloc(sendByteCount + len);
+ }
+ fread(sendBytes + sendByteCount, 1, len, fp); /* would need error checking */
+ sendByteCount += len;
+ fclose(fp);
+ break;
+ case 'O': /* -O <file> (write received data bytes to file) */
+ outputFile = optarg;
+ break;
+ case 'e': /* -e <endpoint> (specify endpoint for some commands) */
+ endpoint = myAtoi(optarg);
+ break;
+ case 't': /* -t <timeout> (specify USB timeout in milliseconds) */
+ usbTimeout = myAtoi(optarg);
+ break;
+ case 'b': /* -b (binary output format, default is hex) */
+ outputFormatIsBinary = 1;
+ break;
+ case 'n': /* -n <count> (maximum number of bytes to receive) */
+ usbCount = myAtoi(optarg);
+ break;
+ case 'c': /* -c <configuration> (device configuration to choose) */
+ usbConfiguration = myAtoi(optarg);
+ break;
+ case 'i': /* -i <interface> (configuration interface to claim) */
+ usbInterface = myAtoi(optarg);
+ break;
+ case 'w': /* -w (suppress USB warnings, default is verbose) */
+ showWarnings = 0;
+ break;
+ default:
+ fprintf(stderr, "Option -%c unknown\n", opt);
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if(argc < 1){
+ usage(myName);
+ exit(1);
+ }
+ argcnt = 2;
+ if(strcasecmp(argv[0], "list") == 0){
+ action = ACTION_LIST;
+ argcnt = 1;
+ }else if(strcasecmp(argv[0], "control") == 0){
+ action = ACTION_CONTROL;
+ argcnt = 7;
+ }else if(strcasecmp(argv[0], "interrupt") == 0){
+ action = ACTION_INTERRUPT;
+ }else if(strcasecmp(argv[0], "bulk") == 0){
+ action = ACTION_BULK;
+ }else{
+ fprintf(stderr, "command %s not known\n", argv[0]);
+ usage(myName);
+ exit(1);
+ }
+ if(argc < argcnt){
+ fprintf(stderr, "Not enough arguments.\n");
+ usage(myName);
+ exit(1);
+ }
+ if(argc > argcnt){
+ fprintf(stderr, "Warning: only %d arguments expected, rest ignored.\n", argcnt);
+ }
+ usb_init();
+ if(usbOpenDevice(&handle, vendorID, vendorNamePattern, productID, productNamePattern, serialPattern, action == ACTION_LIST ? stdout : NULL, showWarnings ? stderr : NULL) != 0){
+ fprintf(stderr, "Could not find USB device with VID=0x%x PID=0x%x Vname=%s Pname=%s Serial=%s\n", vendorID, productID, vendorNamePattern, productNamePattern, serialPattern);
+ exit(1);
+ }
+ if(action == ACTION_LIST)
+ exit(0); /* we've done what we were asked to do already */
+ usbDirection = parseEnum(argv[1], "out", "in", NULL);
+ if(usbDirection){ /* IN transfer */
+ rxBuffer = malloc(usbCount);
+ }
+ if(action == ACTION_CONTROL){
+ int requestType;
+ usbType = parseEnum(argv[2], "standard", "class", "vendor", "reserved", NULL);
+ usbRecipient = parseEnum(argv[3], "device", "interface", "endpoint", "other", NULL);
+ usbRequest = myAtoi(argv[4]);
+ usbValue = myAtoi(argv[5]);
+ usbIndex = myAtoi(argv[6]);
+ requestType = ((usbDirection & 1) << 7) | ((usbType & 3) << 5) | (usbRecipient & 0x1f);
+ if(usbDirection){ /* IN transfer */
+ len = usb_control_msg(handle, requestType, usbRequest, usbValue, usbIndex, rxBuffer, usbCount, usbTimeout);
+ }else{ /* OUT transfer */
+ len = usb_control_msg(handle, requestType, usbRequest, usbValue, usbIndex, sendBytes, sendByteCount, usbTimeout);
+ }
+ }else{ /* must be ACTION_INTERRUPT or ACTION_BULK */
+ int retries = 1;
+ if(usb_set_configuration(handle, usbConfiguration) && showWarnings){
+ fprintf(stderr, "Warning: could not set configuration: %s\n", usb_strerror());
+ }
+ /* now try to claim the interface and detach the kernel HID driver on
+ * linux and other operating systems which support the call.
+ */
+ while((len = usb_claim_interface(handle, usbInterface)) != 0 && retries-- > 0){
+#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+ if(usb_detach_kernel_driver_np(handle, 0) < 0 && showWarnings){
+ fprintf(stderr, "Warning: could not detach kernel driver: %s\n", usb_strerror());
+ }
+#endif
+ }
+ if(len != 0 && showWarnings)
+ fprintf(stderr, "Warning: could not claim interface: %s\n", usb_strerror());
+ if(action == ACTION_INTERRUPT){
+ if(usbDirection){ /* IN transfer */
+ len = usb_interrupt_read(handle, endpoint, rxBuffer, usbCount, usbTimeout);
+ }else{
+ len = usb_interrupt_write(handle, endpoint, sendBytes, sendByteCount, usbTimeout);
+ }
+ }else{
+ if(usbDirection){ /* IN transfer */
+ len = usb_bulk_read(handle, endpoint, rxBuffer, usbCount, usbTimeout);
+ }else{
+ len = usb_bulk_write(handle, endpoint, sendBytes, sendByteCount, usbTimeout);
+ }
+ }
+ }
+ if(len < 0){
+ fprintf(stderr, "USB error: %s\n", usb_strerror());
+ exit(1);
+ }
+ if(usbDirection == 0) /* OUT */
+ printf("%d bytes sent.\n", len);
+ if(rxBuffer != NULL){
+ FILE *fp = stdout;
+ if(outputFile != NULL){
+ fp = fopen(outputFile, outputFormatIsBinary ? "wb" : "w");
+ if(fp == NULL){
+ fprintf(stderr, "Error writing \"%s\": %s\n", outputFile, strerror(errno));
+ exit(1);
+ }
+ }
+ if(outputFormatIsBinary){
+ fwrite(rxBuffer, 1, len, fp);
+ }else{
+ int i;
+ for(i = 0; i < len; i++){
+ if(i != 0){
+ if(i % 16 == 0){
+ fprintf(fp, "\n");
+ }else{
+ fprintf(fp, " ");
+ }
+ }
+ fprintf(fp, "0x%02x", rxBuffer[i] & 0xff);
+ }
+ if(i != 0)
+ fprintf(fp, "\n");
+ }
+ }
+ usb_close(handle);
+ if(rxBuffer != NULL)
+ free(rxBuffer);
+ return 0;
+}
diff --git a/libs-device/Readme.txt b/libs-device/Readme.txt
new file mode 100644
index 0000000..7c0e311
--- /dev/null
+++ b/libs-device/Readme.txt
@@ -0,0 +1,16 @@
+This is the Readme file for the libs-device directory. This directory contains
+code snippets which may be useful for USB device firmware.
+
+
+WHAT IS INCLUDED IN THIS DIRECTORY?
+===================================
+
+osccal.c and osccal.h
+ This module contains a function which calibrates the AVR's built-in RC
+ oscillator based on the USB frame clock. See osccal.h for a documentation
+ of the API.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/libs-device/osccal.c b/libs-device/osccal.c
new file mode 100644
index 0000000..5734a84
--- /dev/null
+++ b/libs-device/osccal.c
@@ -0,0 +1,64 @@
+/* Name: osccal.c
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-10
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+#include <avr/io.h>
+
+/* ------------------------------------------------------------------------- */
+/* ------------------------ Oscillator Calibration ------------------------- */
+/* ------------------------------------------------------------------------- */
+
+/* Calibrate the RC oscillator. Our timing reference is the Start Of Frame
+ * signal (a single SE0 bit) repeating every millisecond immediately after
+ * a USB RESET. We first do a binary search for the OSCCAL value and then
+ * optimize this value with a neighboorhod search.
+ * This algorithm may also be used to calibrate the RC oscillator directly to
+ * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
+ * is wide outside the spec for the OSCCAL value and the required precision for
+ * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
+ * experimental purposes only!
+ */
+void calibrateOscillator(void)
+{
+uchar step = 128;
+uchar trialValue = 0, optimumValue;
+int x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
+
+ /* do a binary search: */
+ do{
+ OSCCAL = trialValue + step;
+ x = usbMeasureFrameLength(); /* proportional to current real frequency */
+ if(x < targetValue) /* frequency still too low */
+ trialValue += step;
+ step >>= 1;
+ }while(step > 0);
+ /* We have a precision of +/- 1 for optimum OSCCAL here */
+ /* now do a neighborhood search for optimum value */
+ optimumValue = trialValue;
+ optimumDev = x; /* this is certainly far away from optimum */
+ for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
+ x = usbMeasureFrameLength() - targetValue;
+ if(x < 0)
+ x = -x;
+ if(x < optimumDev){
+ optimumDev = x;
+ optimumValue = OSCCAL;
+ }
+ }
+ OSCCAL = optimumValue;
+}
+/*
+Note: This calibration algorithm may try OSCCAL values of up to 192 even if
+the optimum value is far below 192. It may therefore exceed the allowed clock
+frequency of the CPU in low voltage designs!
+You may replace this search algorithm with any other algorithm you like if
+you have additional constraints such as a maximum CPU clock.
+For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
+ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
+both regions.
+*/
diff --git a/libs-device/osccal.h b/libs-device/osccal.h
new file mode 100644
index 0000000..8bd0209
--- /dev/null
+++ b/libs-device/osccal.h
@@ -0,0 +1,53 @@
+/* Name: osccal.h
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-10
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description:
+This module contains a function which calibrates the AVR's internal RC
+oscillator so that the CPU runs at F_CPU (F_CPU is a macro which must be
+defined when the module is compiled, best passed in the compiler command
+line). The time reference is the USB frame clock of 1 kHz available
+immediately after a USB RESET condition. Timing is done by counting CPU
+cycles, so all interrupts must be disabled while the calibration runs. For
+low level timing measurements, usbMeasureFrameLength() is called. This
+function must be enabled in usbconfig.h by defining
+USB_CFG_HAVE_MEASURE_FRAME_LENGTH to 1.
+
+Algorithm used:
+calibrateOscillator() first does a binary search in the OSCCAL register for
+the best matching oscillator frequency. Then it does a next neighbor search
+to find the value with the lowest clock rate deviation. It is guaranteed to
+find the best match among neighboring values, but for version 5 oscillators
+(which have a discontinuous relationship between OSCCAL and frequency) a
+better match might be available in another OSCCAL region.
+
+Limitations:
+This calibration algorithm may try OSCCAL values of up to 192 even if the
+optimum value is far below 192. It may therefore exceed the allowed clock
+frequency of the CPU in low voltage designs!
+Precision depends on the OSCCAL vs. frequency dependency of the oscillator.
+Typical precision for an ATMega168 (derived from the OSCCAL vs. F_RC diagram
+in the data sheet) should be in the range of 0.4%. Only the 16.5 MHz version
+of AVR-USB (with built-in receiver PLL) can tolerate this deviation! All other
+frequency modules require at least 0.3% precision.
+*/
+
+#ifndef __OSCCAL_H_INCLUDED__
+#define __OSCCAL_H_INCLUDED__
+
+void calibrateOscillator(void);
+/* This function calibrates the RC oscillator so that the CPU runs at F_CPU.
+ * It MUST be called immediately after the end of a USB RESET condition!
+ * Disable all interrupts during the call!
+ * It is recommended that you store the resulting value in EEPROM so that a
+ * good guess value is available after the next reset.
+ */
+
+
+#endif /* __OSCCAL_H_INCLUDED__ */
diff --git a/libs-host/Readme.txt b/libs-host/Readme.txt
new file mode 100644
index 0000000..5117d18
--- /dev/null
+++ b/libs-host/Readme.txt
@@ -0,0 +1,26 @@
+This is the Readme file for the libs-host directory. This directory contains
+code snippets which may be useful for host side USB software.
+
+
+WHAT IS INCLUDED IN THIS DIRECTORY?
+===================================
+
+opendevice.c and opendevice.h
+ This module contains a function to find and open a device given its
+ numeric IDs (VID, PID), names (vendor name and product name) and serial
+ number. It is based on libusb/libusb-win32 and returns a libusb device
+ handle. See opendevice.h for an API documentation.
+
+hiddata.c and hiddata.h
+ This module contains functions for data transfer over HID feature reports.
+ It is based on libusb on Unix and native Windows functions on Windows. No
+ driver DLL is needed on Windows. See hiddata.h for an API documentation.
+
+hidsdi.h
+ This DDK header file is missing in the free MinGW version of the Windows
+ DDK. Use this version if you get an "include file not found" error.
+
+
+----------------------------------------------------------------------------
+(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH.
+http://www.obdev.at/
diff --git a/libs-host/hiddata.c b/libs-host/hiddata.c
new file mode 100644
index 0000000..3e884e8
--- /dev/null
+++ b/libs-host/hiddata.c
@@ -0,0 +1,317 @@
+/* Name: hiddata.c
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+#include <stdio.h>
+#include "hiddata.h"
+
+/* ######################################################################## */
+#if defined(WIN32) /* ##################################################### */
+/* ######################################################################## */
+
+#include <windows.h>
+#include <setupapi.h>
+#include "hidsdi.h"
+#include <ddk/hidpi.h>
+
+#ifdef DEBUG
+#define DEBUG_PRINT(arg) printf arg
+#else
+#define DEBUG_PRINT(arg)
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static void convertUniToAscii(char *buffer)
+{
+unsigned short *uni = (void *)buffer;
+char *ascii = buffer;
+
+ while(*uni != 0){
+ if(*uni >= 256){
+ *ascii++ = '?';
+ }else{
+ *ascii++ = *uni++;
+ }
+ }
+ *ascii++ = 0;
+}
+
+int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int usesReportIDs)
+{
+GUID hidGuid; /* GUID for HID driver */
+HDEVINFO deviceInfoList;
+SP_DEVICE_INTERFACE_DATA deviceInfo;
+SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = NULL;
+DWORD size;
+int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */
+int errorCode = USBOPEN_ERR_NOTFOUND;
+HANDLE handle = INVALID_HANDLE_VALUE;
+HIDD_ATTRIBUTES deviceAttributes;
+
+ HidD_GetHidGuid(&hidGuid);
+ deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
+ deviceInfo.cbSize = sizeof(deviceInfo);
+ for(i=0;;i++){
+ if(handle != INVALID_HANDLE_VALUE){
+ CloseHandle(handle);
+ handle = INVALID_HANDLE_VALUE;
+ }
+ if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo))
+ break; /* no more entries */
+ /* first do a dummy call just to determine the actual size required */
+ SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
+ if(deviceDetails != NULL)
+ free(deviceDetails);
+ deviceDetails = malloc(size);
+ deviceDetails->cbSize = sizeof(*deviceDetails);
+ /* this call is for real: */
+ SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
+ DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
+ /* attempt opening for R/W -- we don't care about devices which can't be accessed */
+ handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
+ if(handle == INVALID_HANDLE_VALUE){
+ DEBUG_PRINT(("opening failed: %d\n", (int)GetLastError()));
+ /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
+ continue;
+ }
+ deviceAttributes.Size = sizeof(deviceAttributes);
+ HidD_GetAttributes(handle, &deviceAttributes);
+ DEBUG_PRINT(("device attributes: vid=%d pid=%d\n", deviceAttributes.VendorID, deviceAttributes.ProductID));
+ if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
+ continue; /* ignore this device */
+ errorCode = USBOPEN_ERR_NOTFOUND;
+ if(vendorName != NULL && productName != NULL){
+ char buffer[512];
+ if(!HidD_GetManufacturerString(handle, buffer, sizeof(buffer))){
+ DEBUG_PRINT(("error obtaining vendor name\n"));
+ errorCode = USBOPEN_ERR_IO;
+ continue;
+ }
+ convertUniToAscii(buffer);
+ DEBUG_PRINT(("vendorName = \"%s\"\n", buffer));
+ if(strcmp(vendorName, buffer) != 0)
+ continue;
+ if(!HidD_GetProductString(handle, buffer, sizeof(buffer))){
+ DEBUG_PRINT(("error obtaining product name\n"));
+ errorCode = USBOPEN_ERR_IO;
+ continue;
+ }
+ convertUniToAscii(buffer);
+ DEBUG_PRINT(("productName = \"%s\"\n", buffer));
+ if(strcmp(productName, buffer) != 0)
+ continue;
+ }
+ break; /* we have found the device we are looking for! */
+ }
+ SetupDiDestroyDeviceInfoList(deviceInfoList);
+ if(deviceDetails != NULL)
+ free(deviceDetails);
+ if(handle != INVALID_HANDLE_VALUE){
+ *device = (usbDevice_t *)handle;
+ errorCode = 0;
+ }
+ return errorCode;
+}
+
+/* ------------------------------------------------------------------------ */
+
+void usbhidCloseDevice(usbDevice_t *device)
+{
+ CloseHandle((HANDLE)device);
+}
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
+{
+BOOLEAN rval;
+
+ rval = HidD_SetFeature((HANDLE)device, buffer, len);
+ return rval == 0 ? USBOPEN_ERR_IO : 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
+{
+BOOLEAN rval = 0;
+
+ buffer[0] = reportNumber;
+ rval = HidD_GetFeature((HANDLE)device, buffer, *len);
+ return rval == 0 ? USBOPEN_ERR_IO : 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* ######################################################################## */
+#else /* defined WIN32 #################################################### */
+/* ######################################################################## */
+
+#include <string.h>
+#include <usb.h>
+
+#define usbDevice usb_dev_handle /* use libusb's device structure */
+
+/* ------------------------------------------------------------------------- */
+
+#define USBRQ_HID_GET_REPORT 0x01
+#define USBRQ_HID_SET_REPORT 0x09
+
+#define USB_HID_REPORT_TYPE_FEATURE 3
+
+
+static int usesReportIDs;
+
+/* ------------------------------------------------------------------------- */
+
+static int usbhidGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
+{
+char buffer[256];
+int rval, i;
+
+ if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
+ return rval;
+ if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
+ return rval;
+ if(buffer[1] != USB_DT_STRING){
+ *buf = 0;
+ return 0;
+ }
+ if((unsigned char)buffer[0] < rval)
+ rval = (unsigned char)buffer[0];
+ rval /= 2;
+ /* lossy conversion to ISO Latin1: */
+ for(i=1;i<rval;i++){
+ if(i > buflen) /* destination buffer overflow */
+ break;
+ buf[i-1] = buffer[2 * i];
+ if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
+ buf[i-1] = '?';
+ }
+ buf[i-1] = 0;
+ return i-1;
+}
+
+int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs)
+{
+struct usb_bus *bus;
+struct usb_device *dev;
+usb_dev_handle *handle = NULL;
+int errorCode = USBOPEN_ERR_NOTFOUND;
+static int didUsbInit = 0;
+
+ if(!didUsbInit){
+ usb_init();
+ didUsbInit = 1;
+ }
+ usb_find_busses();
+ usb_find_devices();
+ for(bus=usb_get_busses(); bus; bus=bus->next){
+ for(dev=bus->devices; dev; dev=dev->next){
+ if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){
+ char string[256];
+ int len;
+ handle = usb_open(dev); /* we need to open the device in order to query strings */
+ if(!handle){
+ errorCode = USBOPEN_ERR_ACCESS;
+ fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
+ continue;
+ }
+ if(vendorName == NULL && productName == NULL){ /* name does not matter */
+ break;
+ }
+ /* now check whether the names match: */
+ len = usbhidGetStringAscii(handle, dev->descriptor.iManufacturer, string, sizeof(string));
+ if(len < 0){
+ errorCode = USBOPEN_ERR_IO;
+ fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
+ }else{
+ errorCode = USBOPEN_ERR_NOTFOUND;
+ /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
+ if(strcmp(string, vendorName) == 0){
+ len = usbhidGetStringAscii(handle, dev->descriptor.iProduct, string, sizeof(string));
+ if(len < 0){
+ errorCode = USBOPEN_ERR_IO;
+ fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
+ }else{
+ errorCode = USBOPEN_ERR_NOTFOUND;
+ /* fprintf(stderr, "seen product ->%s<-\n", string); */
+ if(strcmp(string, productName) == 0)
+ break;
+ }
+ }
+ }
+ usb_close(handle);
+ handle = NULL;
+ }
+ }
+ if(handle)
+ break;
+ }
+ if(handle != NULL){
+ errorCode = 0;
+ *device = (void *)handle;
+ usesReportIDs = _usesReportIDs;
+ }
+ return errorCode;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void usbhidCloseDevice(usbDevice_t *device)
+{
+ if(device != NULL)
+ usb_close((void *)device);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
+{
+int bytesSent;
+
+ if(!usesReportIDs){
+ buffer++; /* skip dummy report ID */
+ len--;
+ }
+ bytesSent = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (buffer[0] & 0xff), 0, buffer, len, 5000);
+ if(bytesSent != len){
+ if(bytesSent < 0)
+ fprintf(stderr, "Error sending message: %s\n", usb_strerror());
+ return USBOPEN_ERR_IO;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
+{
+int bytesReceived, maxLen = *len;
+
+ if(!usesReportIDs){
+ buffer++; /* make room for dummy report ID */
+ maxLen--;
+ }
+ bytesReceived = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, 0, buffer, maxLen, 5000);
+ if(bytesReceived < 0){
+ fprintf(stderr, "Error sending message: %s\n", usb_strerror());
+ return USBOPEN_ERR_IO;
+ }
+ *len = bytesReceived;
+ if(!usesReportIDs){
+ buffer[-1] = reportNumber; /* add dummy report ID */
+ *len++;
+ }
+ return 0;
+}
+
+/* ######################################################################## */
+#endif /* defined WIN32 ################################################### */
+/* ######################################################################## */
diff --git a/libs-host/hiddata.h b/libs-host/hiddata.h
new file mode 100644
index 0000000..e362730
--- /dev/null
+++ b/libs-host/hiddata.h
@@ -0,0 +1,71 @@
+/* Name: hiddata.h
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+#ifndef __HIDDATA_H_INCLUDED__
+#define __HIDDATA_H_INCLUDED__
+
+/*
+General Description:
+This module implements an abstraction layer for data transfer over HID feature
+requests. The implementation uses native Windows functions on Windows so that
+no driver installation is required and libusb on Unix. You must link the
+appropriate libraries in either case: "-lhid -lusb -lsetupapi" on Windows and
+`libusb-config --libs` on Unix.
+*/
+
+/* ------------------------------------------------------------------------ */
+
+#define USBOPEN_SUCCESS 0 /* no error */
+#define USBOPEN_ERR_ACCESS 1 /* not enough permissions to open device */
+#define USBOPEN_ERR_IO 2 /* I/O error */
+#define USBOPEN_ERR_NOTFOUND 3 /* device not found */
+
+/* ------------------------------------------------------------------------ */
+
+typedef struct usbDevice usbDevice_t;
+/* Opaque data type representing the USB device. This can be a Windows handle
+ * or a libusb handle, depending on the backend implementation.
+ */
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidOpenDevice(usbDevice_t **device, int vendorID, char *vendorName, int productID, char *productName, int usesReportIDs);
+/* This function opens a USB device. 'vendorID' and 'productID' are the numeric
+ * Vendor-ID and Product-ID of the device we want to open. If 'vendorName' and
+ * 'productName' are both not NULL, only devices with matching manufacturer-
+ * and product name strings are accepted. If the device uses report IDs,
+ * 'usesReportIDs' must be set to a non-zero value.
+ * Returns: If a matching device has been found, USBOPEN_SUCCESS is returned
+ * and '*device' is set to an opaque pointer representing the device. The
+ * device must be closed with usbhidCloseDevice(). If the device has not been
+ * found or opening failed, an error code is returned.
+ */
+void usbhidCloseDevice(usbDevice_t *device);
+/* Every device opened with usbhidOpenDevice() must be closed with this function.
+ */
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len);
+/* This function sends a feature report to the device. The report ID must be
+ * in the first byte of buffer and the length 'len' of the report is specified
+ * including this report ID. If no report IDs are used, buffer[0] must be set
+ * to 0 (dummy report ID).
+ * Returns: 0 on success, an error code otherwise.
+ */
+int usbhidGetReport(usbDevice_t *device, int reportID, char *buffer, int *len);
+/* This function obtains a feature report from the device. The requested
+ * report-ID is passed in 'reportID'. The caller must pass a buffer of the size
+ * of the expected report in 'buffer' and initialize the variable pointed to by
+ * 'len' to the total size of this buffer. Upon successful return, the report
+ * (prefixed with the report-ID) is in 'buffer' and the actual length of the
+ * report is returned in '*len'.
+ * Returns: 0 on success, an error code otherwise.
+ */
+
+/* ------------------------------------------------------------------------ */
+
+#endif /* __HIDDATA_H_INCLUDED__ */
diff --git a/libs-host/hidsdi.h b/libs-host/hidsdi.h
new file mode 100644
index 0000000..840afcb
--- /dev/null
+++ b/libs-host/hidsdi.h
@@ -0,0 +1,49 @@
+/* Name: hidsdi.h
+ * Author: Christian Starkjohann
+ * Creation Date: 2006-02-02
+ * Tabsize: 4
+ * Copyright: (c) 2006-2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description
+This file is a replacement for hidsdi.h from the Windows DDK. It defines some
+of the types and function prototypes of this header for our project. If you
+have the Windows DDK version of this file or a version shipped with MinGW, use
+that instead.
+*/
+
+#ifndef _HIDSDI_H
+#define _HIDSDI_H
+
+#include <pshpack4.h>
+
+#include <ddk/hidusage.h>
+#include <ddk/hidpi.h>
+
+typedef struct{
+ ULONG Size;
+ USHORT VendorID;
+ USHORT ProductID;
+ USHORT VersionNumber;
+}HIDD_ATTRIBUTES;
+
+void __stdcall HidD_GetHidGuid(OUT LPGUID hidGuid);
+
+BOOLEAN __stdcall HidD_GetAttributes(IN HANDLE device, OUT HIDD_ATTRIBUTES *attributes);
+
+BOOLEAN __stdcall HidD_GetManufacturerString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen);
+BOOLEAN __stdcall HidD_GetProductString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen);
+BOOLEAN __stdcall HidD_GetSerialNumberString(IN HANDLE device, OUT void *buffer, IN ULONG bufferLen);
+
+BOOLEAN __stdcall HidD_GetFeature(IN HANDLE device, OUT void *reportBuffer, IN ULONG bufferLen);
+BOOLEAN __stdcall HidD_SetFeature(IN HANDLE device, IN void *reportBuffer, IN ULONG bufferLen);
+
+BOOLEAN __stdcall HidD_GetNumInputBuffers(IN HANDLE device, OUT ULONG *numBuffers);
+BOOLEAN __stdcall HidD_SetNumInputBuffers(IN HANDLE device, OUT ULONG numBuffers);
+
+#include <poppack.h>
+
+#endif
diff --git a/libs-host/opendevice.c b/libs-host/opendevice.c
new file mode 100644
index 0000000..cd41c15
--- /dev/null
+++ b/libs-host/opendevice.c
@@ -0,0 +1,203 @@
+/* Name: opendevice.c
+ * Project: AVR-USB host-side library
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-10
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description:
+The functions in this module can be used to find and open a device based on
+libusb or libusb-win32.
+*/
+
+#include <stdio.h>
+#include "opendevice.h"
+
+/* ------------------------------------------------------------------------- */
+
+#define MATCH_SUCCESS 1
+#define MATCH_FAILED 0
+#define MATCH_ABORT -1
+
+/* private interface: match text and p, return MATCH_SUCCESS, MATCH_FAILED, or MATCH_ABORT. */
+static int _shellStyleMatch(char *text, char *p)
+{
+int last, matched, reverse;
+
+ for(; *p; text++, p++){
+ if(*text == 0 && *p != '*')
+ return MATCH_ABORT;
+ switch(*p){
+ case '\\':
+ /* Literal match with following character. */
+ p++;
+ /* FALLTHROUGH */
+ default:
+ if(*text != *p)
+ return MATCH_FAILED;
+ continue;
+ case '?':
+ /* Match anything. */
+ continue;
+ case '*':
+ while(*++p == '*')
+ /* Consecutive stars act just like one. */
+ continue;
+ if(*p == 0)
+ /* Trailing star matches everything. */
+ return MATCH_SUCCESS;
+ while(*text)
+ if((matched = _shellStyleMatch(text++, p)) != MATCH_FAILED)
+ return matched;
+ return MATCH_ABORT;
+ case '[':
+ reverse = p[1] == '^';
+ if(reverse) /* Inverted character class. */
+ p++;
+ matched = MATCH_FAILED;
+ if(p[1] == ']' || p[1] == '-')
+ if(*++p == *text)
+ matched = MATCH_SUCCESS;
+ for(last = *p; *++p && *p != ']'; last = *p)
+ if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
+ matched = MATCH_SUCCESS;
+ if(matched == reverse)
+ return MATCH_FAILED;
+ continue;
+ }
+ }
+ return *text == 0;
+}
+
+/* public interface for shell style matching: returns 0 if fails, 1 if matches */
+static int shellStyleMatch(char *text, char *pattern)
+{
+ if(pattern == NULL) /* NULL pattern is synonymous to "*" */
+ return 1;
+ return _shellStyleMatch(text, pattern) == MATCH_SUCCESS;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
+{
+char buffer[256];
+int rval, i;
+
+ if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
+ return rval;
+ if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
+ return rval;
+ if(buffer[1] != USB_DT_STRING){
+ *buf = 0;
+ return 0;
+ }
+ if((unsigned char)buffer[0] < rval)
+ rval = (unsigned char)buffer[0];
+ rval /= 2;
+ /* lossy conversion to ISO Latin1: */
+ for(i=1;i<rval;i++){
+ if(i > buflen) /* destination buffer overflow */
+ break;
+ buf[i-1] = buffer[2 * i];
+ if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
+ buf[i-1] = '?';
+ }
+ buf[i-1] = 0;
+ return i-1;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbOpenDevice(usb_dev_handle **device, int vendorID, char *vendorNamePattern, int productID, char *productNamePattern, char *serialNamePattern, FILE *printMatchingDevicesFp, FILE *warningsFp)
+{
+struct usb_bus *bus;
+struct usb_device *dev;
+usb_dev_handle *handle = NULL;
+int errorCode = USBOPEN_ERR_NOTFOUND;
+
+ usb_find_busses();
+ usb_find_devices();
+ for(bus = usb_get_busses(); bus; bus = bus->next){
+ for(dev = bus->devices; dev; dev = dev->next){ /* iterate over all devices on all busses */
+ if((vendorID == 0 || dev->descriptor.idVendor == vendorID)
+ && (productID == 0 || dev->descriptor.idProduct == productID)){
+ char vendor[256], product[256], serial[256];
+ int len;
+ handle = usb_open(dev); /* we need to open the device in order to query strings */
+ if(!handle){
+ errorCode = USBOPEN_ERR_ACCESS;
+ if(warningsFp != NULL)
+ fprintf(warningsFp, "Warning: cannot open VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
+ continue;
+ }
+ /* now check whether the names match: */
+ len = vendor[0] = 0;
+ if(dev->descriptor.iManufacturer > 0){
+ len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, vendor, sizeof(vendor));
+ }
+ if(len < 0){
+ errorCode = USBOPEN_ERR_ACCESS;
+ if(warningsFp != NULL)
+ fprintf(warningsFp, "Warning: cannot query manufacturer for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
+ }else{
+ errorCode = USBOPEN_ERR_NOTFOUND;
+ /* printf("seen device from vendor ->%s<-\n", vendor); */
+ if(shellStyleMatch(vendor, vendorNamePattern)){
+ len = product[0] = 0;
+ if(dev->descriptor.iProduct > 0){
+ len = usbGetStringAscii(handle, dev->descriptor.iProduct, product, sizeof(product));
+ }
+ if(len < 0){
+ errorCode = USBOPEN_ERR_ACCESS;
+ if(warningsFp != NULL)
+ fprintf(warningsFp, "Warning: cannot query product for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
+ }else{
+ errorCode = USBOPEN_ERR_NOTFOUND;
+ /* printf("seen product ->%s<-\n", product); */
+ if(shellStyleMatch(product, productNamePattern)){
+ len = serial[0] = 0;
+ if(dev->descriptor.iSerialNumber > 0){
+ len = usbGetStringAscii(handle, dev->descriptor.iSerialNumber, serial, sizeof(serial));
+ }
+ if(len < 0){
+ errorCode = USBOPEN_ERR_ACCESS;
+ if(warningsFp != NULL)
+ fprintf(warningsFp, "Warning: cannot query serial for VID=0x%04x PID=0x%04x: %s\n", dev->descriptor.idVendor, dev->descriptor.idProduct, usb_strerror());
+ }
+ if(shellStyleMatch(serial, serialNamePattern)){
+ if(printMatchingDevicesFp != NULL){
+ if(serial[0] == 0){
+ fprintf(printMatchingDevicesFp, "VID=0x%04x PID=0x%04x vendor=\"%s\" product=\"%s\"\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product);
+ }else{
+ fprintf(printMatchingDevicesFp, "VID=0x%04x PID=0x%04x vendor=\"%s\" product=\"%s\" serial=\"%s\"\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product, serial);
+ }
+ }else{
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ usb_close(handle);
+ handle = NULL;
+ }
+ }
+ if(handle) /* we have found a deice */
+ break;
+ }
+ if(handle != NULL){
+ errorCode = 0;
+ *device = handle;
+ }
+ if(printMatchingDevicesFp != NULL) /* never return an error for listing only */
+ errorCode = 0;
+ return errorCode;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/libs-host/opendevice.h b/libs-host/opendevice.h
new file mode 100644
index 0000000..046392e
--- /dev/null
+++ b/libs-host/opendevice.h
@@ -0,0 +1,77 @@
+/* Name: opendevice.h
+ * Project: AVR-USB host-side library
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-10
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
+ * This Revision: $Id$
+ */
+
+/*
+General Description:
+This module offers additional functionality for host side drivers based on
+libusb or libusb-win32. It includes a function to find and open a device
+based on numeric IDs and textual description. It also includes a function to
+obtain textual descriptions from a device.
+
+To use this functionality, simply copy opendevice.c and opendevice.h into your
+project and add them to your Makefile. You may modify and redistribute these
+files according to the GNU General Public License (GPL) version 2.
+*/
+
+#ifndef __OPENDEVICE_H_INCLUDED__
+#define __OPENDEVICE_H_INCLUDED__
+
+#include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */
+#include <stdio.h>
+
+int usbGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen);
+/* This function gets a string descriptor from the device. 'index' is the
+ * string descriptor index. The string is returned in ISO Latin 1 encoding in
+ * 'buf' and it is terminated with a 0-character. The buffer size must be
+ * passed in 'buflen' to prevent buffer overflows. A libusb device handle
+ * must be given in 'dev'.
+ * Returns: The length of the string (excluding the terminating 0) or
+ * a negative number in case of an error. If there was an error, use
+ * usb_strerror() to obtain the error message.
+ */
+
+int usbOpenDevice(usb_dev_handle **device, int vendorID, char *vendorNamePattern, int productID, char *productNamePattern, char *serialNamePattern, FILE *printMatchingDevicesFp, FILE *warningsFp);
+/* This function iterates over all devices on all USB busses and searches for
+ * a device. Matching is done first by means of Vendor- and Product-ID (passed
+ * in 'vendorID' and 'productID'. An ID of 0 matches any numeric ID (wildcard).
+ * When a device matches by its IDs, matching by names is performed. Name
+ * matching can be done on textual vendor name ('vendorNamePattern'), product
+ * name ('productNamePattern') and serial number ('serialNamePattern'). A
+ * device matches only if all non-null pattern match. If you don't care about
+ * a string, pass NULL for the pattern. Patterns are Unix shell style pattern:
+ * '*' stands for 0 or more characters, '?' for one single character, a list
+ * of characters in square brackets for a single character from the list
+ * (dashes are allowed to specify a range) and if the lis of characters begins
+ * with a caret ('^'), it matches one character which is NOT in the list.
+ * Other parameters to the function: If 'warningsFp' is not NULL, warning
+ * messages are printed to this file descriptor with fprintf(). If
+ * 'printMatchingDevicesFp' is not NULL, no device is opened but matching
+ * devices are printed to the given file descriptor with fprintf().
+ * If a device is opened, the resulting USB handle is stored in '*device'. A
+ * pointer to a "usb_dev_handle *" type variable must be passed here.
+ * Returns: 0 on success, an error code (see defines below) on failure.
+ */
+
+/* usbOpenDevice() error codes: */
+#define USBOPEN_SUCCESS 0 /* no error */
+#define USBOPEN_ERR_ACCESS 1 /* not enough permissions to open device */
+#define USBOPEN_ERR_IO 2 /* I/O error */
+#define USBOPEN_ERR_NOTFOUND 3 /* device not found */
+
+
+/* Obdev's free USB IDs, see USBID-License.txt for details */
+
+#define USB_VID_OBDEV_SHARED 5824 /* obdev's shared vendor ID */
+#define USB_PID_OBDEV_SHARED_CUSTOM 1500 /* shared PID for custom class devices */
+#define USB_PID_OBDEV_SHARED_HID 1503 /* shared PID for HIDs except mice & keyboards */
+#define USB_PID_OBDEV_SHARED_CDCACM 1505 /* shared PID for CDC Modem devices */
+#define USB_PID_OBDEV_SHARED_MIDI 1508 /* shared PID for MIDI class devices */
+
+#endif /* __OPENDEVICE_H_INCLUDED__ */