o add vusb

This commit is contained in:
optixx
2009-05-02 11:28:11 +02:00
parent 0693dbc933
commit 25c1f38aed
104 changed files with 15514 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
This is the Readme file for the directory "examples" of V-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://www.obdev.at/goto.php?t=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/vusb/projects.html. Most of these
features are described in our documentation wiki at
http://www.obdev.at/goto.php?t=vusb-wiki.
To mention just a few:
Using RC oscillator for system clock
The 12.8 MHz and 16.5 MHz modules of V-USB have 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 can be done.
Dynamically generated descriptors
Sometimes you want to implement different typtes of USB device depending
on a jumper or other condition. V-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
V-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/vusb/prjall.html
where all projects are listed.
----------------------------------------------------------------------------
(c) 2009 by OBJECTIVE DEVELOPMENT Software GmbH.
http://www.obdev.at/

View File

@@ -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://www.obdev.at/goto.php?t=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/

View File

@@ -0,0 +1,48 @@
# Name: Makefile
# Project: custom-class example
# 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
# 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 = set-led
OBJECTS = opendevice.o $(NAME).o
CC = gcc
CFLAGS = $(CPPFLAGS) $(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)

View File

@@ -0,0 +1,18 @@
# Name: Makefile.windows
# Project: custom-class example
# 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), GNU GPL v3 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

View File

@@ -0,0 +1,203 @@
/* Name: opendevice.c
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.c 740 2009-04-13 18:23:31Z cs $
*/
/*
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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,77 @@
/* Name: opendevice.h
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.h 740 2009-04-13 18:23:31Z cs $
*/
/*
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 or 3.
*/
#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__ */

View File

@@ -0,0 +1,135 @@
/* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: set-led.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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);
#if ENABLE_TEST
fprintf(stderr, " %s test ..... run driver reliability test\n", name);
#endif /* ENABLE_TEST */
}
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[4];
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. Reading device descriptor and setting a
* configuration and interface is done through endpoint 0 after all.
* However, newer versions of Linux require that we claim an interface
* even for endpoint 0. Enable the following code if your operating system
* needs it: */
#if 0
int retries = 1, usbConfiguration = 1, usbInterface = 0;
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
}
#endif
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());
}
#if ENABLE_TEST
}else if(strcasecmp(argv[1], "test") == 0){
int i;
srandomdev();
for(i = 0; i < 50000; i++){
int value = random() & 0xffff, index = random() & 0xffff;
int rxValue, rxIndex;
if((i+1) % 100 == 0){
fprintf(stderr, "\r%05d", i+1);
fflush(stderr);
}
cnt = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CUSTOM_RQ_ECHO, value, index, buffer, sizeof(buffer), 5000);
if(cnt < 0){
fprintf(stderr, "\nUSB error in iteration %d: %s\n", i, usb_strerror());
break;
}else if(cnt != 4){
fprintf(stderr, "\nerror in iteration %d: %d bytes received instead of 4\n", i, cnt);
break;
}
rxValue = ((int)buffer[0] & 0xff) | (((int)buffer[1] & 0xff) << 8);
rxIndex = ((int)buffer[2] & 0xff) | (((int)buffer[3] & 0xff) << 8);
if(rxValue != value || rxIndex != index){
fprintf(stderr, "\ndata error in iteration %d:\n", i);
fprintf(stderr, "rxValue = 0x%04x value = 0x%04x\n", rxValue, value);
fprintf(stderr, "rxIndex = 0x%04x index = 0x%04x\n", rxIndex, index);
}
}
fprintf(stderr, "\nTest completed.\n");
#endif /* ENABLE_TEST */
}else{
usage(argv[0]);
exit(1);
}
usb_close(handle);
return 0;
}

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
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

View File

@@ -0,0 +1,98 @@
/* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: main.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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 ----------------------------- */
/* ------------------------------------------------------------------------- */
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
static uchar dataBuffer[4]; /* buffer must stay valid when usbFunctionSetup returns */
if(rq->bRequest == CUSTOM_RQ_ECHO){ /* echo -- used for reliability tests */
dataBuffer[0] = rq->wValue.bytes[0];
dataBuffer[1] = rq->wValue.bytes[1];
dataBuffer[2] = rq->wIndex.bytes[0];
dataBuffer[3] = rq->wIndex.bytes[1];
usbMsgPtr = dataBuffer; /* tell the driver which data to return */
return 4;
}else 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){
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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,36 @@
/* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: requests.h 692 2008-11-07 15:07:40Z cs $
*/
/* 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_ECHO 0
/* Request that the device sends back wValue and wIndex. This is used with
* random data to test the reliability of the communication.
*/
#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__ */

View File

@@ -0,0 +1,350 @@
/* Name: usbconfig.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 740 2009-04-13 18:23:31Z cs $
*/
#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
*/
/* ---------------------------- Hardware Config ---------------------------- */
#define USB_CFG_IOPORTNAME D
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 2
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0! [You can also use other interrupts, see section
* "Optional MCU Description" below, or you can connect D- to the interrupt, as
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
* 16500 and 20000. The 12.8 MHz and 16.5 MHz versions of the code require no
* crystal, they tolerate +/- 1% deviation from the nominal frequency. All
* other rates require a precision of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
#define USB_CFG_CHECK_CRC 0
/* Define this to 1 if you want that the driver checks integrity of incoming
* data packets (CRC checks). CRC checks cost quite a bit of code size and are
* currently only available for 18 MHz crystal clock. You must choose
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option.
*/
/* ----------------------- Optional Hardware Config ------------------------ */
/* #define USB_CFG_PULLUP_IOPORTNAME D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
/* #define USB_CFG_PULLUP_BIT 4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
*/
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
*/
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
/* Define this to 1 if you want to compile a version with three endpoints: The
* default control endpoint 0, an interrupt-in endpoint 3 (or the number
* configured below) and a catch-all default interrupt-in endpoint as above.
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.
*/
#define USB_CFG_EP3_NUMBER 3
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
* for endpoint 1 (interrupt endpoint). Although you may not need this feature,
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_SUPPRESS_INTR_CODE 0
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
* want to send any data over them. If this macro is defined to 1, functions
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if
* you need the interrupt-in endpoints in order to comply to an interface
* (e.g. HID), but never want to send any data. This option saves a couple
* of bytes in flash memory and the transmit buffers in RAM.
*/
#define USB_CFG_INTR_POLL_INTERVAL 10
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
*/
#define USB_CFG_IS_SELF_POWERED 0
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 40
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
#define USB_CFG_IMPLEMENT_FN_WRITE 0
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 0
/* Set this to 1 if you need to send control replies which are generated
* "on the fly" when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
* usbFunctionSetup(). This saves a couple of bytes.
*/
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
* You must implement the function usbFunctionWriteOut() which receives all
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number
* can be found in 'usbRxToken'.
*/
#define USB_CFG_HAVE_FLOWCONTROL 0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
* If you eat the received message and don't want default processing to
* proceed, do a return after doing your things. One possible application
* (besides debugging) is to flash a status LED on each packet.
*/
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
*/
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
* received.
*/
#define USB_COUNT_SOF 0
/* define this macro to 1 if you need the global variable "usbSofCount" which
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use one of obdev's free shared
* VID/PID pairs. Be sure to read USBID-License.txt for rules!
*/
#define USB_CFG_DEVICE_ID 0xdc, 0x05
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'L', 'E', 'D', 'C', 'o', 'n', 't', 'r', 'o', 'l'
#define USB_CFG_DEVICE_NAME_LEN 10
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Same as above for the serial number. If you don't want a serial number,
* undefine the macros.
* It may be useful to provide the serial number through other means than at
* compile time. See the section about descriptor properties below for how
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is "vendor specific".
*/
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
*/
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* "usbHidReportDescriptor" to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
/* #define USB_PUBLIC static */
/* Use the define above if you #include usbdrv.c instead of linking against it.
* This technique saves a couple of bytes in flash memory.
*/
/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
* provide our own. These can be provided as (1) fixed length static data in
* flash memory, (2) fixed length static data in RAM or (3) dynamically at
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
* information about this function.
* Descriptor handling is configured through the descriptor's properties. If
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
* the driver must know the descriptor's length. The descriptor itself is
* found at the address of a well known identifier (see below).
* List of static descriptor names (must be declared PROGMEM if in flash):
* char usbDescriptorDevice[];
* char usbDescriptorConfiguration[];
* char usbDescriptorHidReport[];
* char usbDescriptorString0[];
* int usbDescriptorStringVendor[];
* int usbDescriptorStringDevice[];
* int usbDescriptorStringSerialNumber[];
* Other descriptors can't be provided statically, they must be provided
* dynamically at runtime.
*
* Descriptor properties are or-ed or added together, e.g.:
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
*
* The following descriptors are defined:
* USB_CFG_DESCR_PROPS_DEVICE
* USB_CFG_DESCR_PROPS_CONFIGURATION
* USB_CFG_DESCR_PROPS_STRINGS
* USB_CFG_DESCR_PROPS_STRING_0
* USB_CFG_DESCR_PROPS_STRING_VENDOR
* USB_CFG_DESCR_PROPS_STRING_PRODUCT
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
* USB_CFG_DESCR_PROPS_HID
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
/* ----------------------- Optional MCU Description ------------------------ */
/* The following configurations have working defaults in usbdrv.h. You
* usually don't need to set them explicitly. Only if you want to run
* the driver on a device which is not yet supported or with a compiler
* which is not fully supported (such as IAR C) or if you use a differnt
* interrupt than INT0, you may have to define some of these.
*/
/* #define USB_INTR_CFG MCUCR */
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */
/* #define USB_INTR_CFG_CLR 0 */
/* #define USB_INTR_ENABLE GIMSK */
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_h_included__ */

View File

@@ -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/

View File

@@ -0,0 +1,48 @@
# Name: Makefile
# Project: hid-custom-rq example
# 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
# 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 = set-led
OBJECTS = opendevice.o $(NAME).o
CC = gcc
CFLAGS = $(CPPFLAGS) $(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)

View File

@@ -0,0 +1,18 @@
# Name: Makefile.windows
# Project: hid-custom-rq example
# 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), GNU GPL v3 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

View File

@@ -0,0 +1,203 @@
/* Name: opendevice.c
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.c 740 2009-04-13 18:23:31Z cs $
*/
/*
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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,77 @@
/* Name: opendevice.h
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.h 740 2009-04-13 18:23:31Z cs $
*/
/*
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 or 3.
*/
#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__ */

View File

@@ -0,0 +1,135 @@
/* Name: set-led.c
* Project: hid-custom-rq 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: set-led.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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);
#if ENABLE_TEST
fprintf(stderr, " %s test ..... run driver reliability test\n", name);
#endif /* ENABLE_TEST */
}
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[4];
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. Reading device descriptor and setting a
* configuration and interface is done through endpoint 0 after all.
* However, newer versions of Linux require that we claim an interface
* even for endpoint 0. Enable the following code if your operating system
* needs it: */
#if 0
int retries = 1, usbConfiguration = 1, usbInterface = 0;
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
}
#endif
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());
}
#if ENABLE_TEST
}else if(strcasecmp(argv[1], "test") == 0){
int i;
srandomdev();
for(i = 0; i < 50000; i++){
int value = random() & 0xffff, index = random() & 0xffff;
int rxValue, rxIndex;
if((i+1) % 100 == 0){
fprintf(stderr, "\r%05d", i+1);
fflush(stderr);
}
cnt = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CUSTOM_RQ_ECHO, value, index, buffer, sizeof(buffer), 5000);
if(cnt < 0){
fprintf(stderr, "\nUSB error in iteration %d: %s\n", i, usb_strerror());
break;
}else if(cnt != 4){
fprintf(stderr, "\nerror in iteration %d: %d bytes received instead of 4\n", i, cnt);
break;
}
rxValue = ((int)buffer[0] & 0xff) | (((int)buffer[1] & 0xff) << 8);
rxIndex = ((int)buffer[2] & 0xff) | (((int)buffer[3] & 0xff) << 8);
if(rxValue != value || rxIndex != index){
fprintf(stderr, "\ndata error in iteration %d:\n", i);
fprintf(stderr, "rxValue = 0x%04x value = 0x%04x\n", rxValue, value);
fprintf(stderr, "rxIndex = 0x%04x index = 0x%04x\n", rxIndex, index);
}
}
fprintf(stderr, "\nTest completed.\n");
#endif /* ENABLE_TEST */
}else{
usage(argv[0]);
exit(1);
}
usb_close(handle);
return 0;
}

View File

@@ -0,0 +1,164 @@
# Name: Makefile
# 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
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

View File

@@ -0,0 +1,121 @@
/* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: main.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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.
*/
/* ------------------------------------------------------------------------- */
usbMsgLen_t 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 */
#if 0 /* this is a bit too aggressive for a debug output */
DBG2(0x02, 0, 0); /* debug output: main loop iterates */
#endif
wdt_reset();
usbPoll();
}
return 0;
}
/* ------------------------------------------------------------------------- */

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: requests.h 692 2008-11-07 15:07:40Z cs $
*/
/* 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__ */

View File

@@ -0,0 +1,350 @@
/* Name: usbconfig.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 740 2009-04-13 18:23:31Z cs $
*/
#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
*/
/* ---------------------------- Hardware Config ---------------------------- */
#define USB_CFG_IOPORTNAME D
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 2
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0! [You can also use other interrupts, see section
* "Optional MCU Description" below, or you can connect D- to the interrupt, as
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
* 16500 and 20000. The 12.8 MHz and 16.5 MHz versions of the code require no
* crystal, they tolerate +/- 1% deviation from the nominal frequency. All
* other rates require a precision of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
#define USB_CFG_CHECK_CRC 0
/* Define this to 1 if you want that the driver checks integrity of incoming
* data packets (CRC checks). CRC checks cost quite a bit of code size and are
* currently only available for 18 MHz crystal clock. You must choose
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option.
*/
/* ----------------------- Optional Hardware Config ------------------------ */
/* #define USB_CFG_PULLUP_IOPORTNAME D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
/* #define USB_CFG_PULLUP_BIT 4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
*/
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
*/
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
/* Define this to 1 if you want to compile a version with three endpoints: The
* default control endpoint 0, an interrupt-in endpoint 3 (or the number
* configured below) and a catch-all default interrupt-in endpoint as above.
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.
*/
#define USB_CFG_EP3_NUMBER 3
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
* for endpoint 1 (interrupt endpoint). Although you may not need this feature,
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_SUPPRESS_INTR_CODE 0
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
* want to send any data over them. If this macro is defined to 1, functions
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if
* you need the interrupt-in endpoints in order to comply to an interface
* (e.g. HID), but never want to send any data. This option saves a couple
* of bytes in flash memory and the transmit buffers in RAM.
*/
#define USB_CFG_INTR_POLL_INTERVAL 100
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
*/
#define USB_CFG_IS_SELF_POWERED 0
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 40
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
#define USB_CFG_IMPLEMENT_FN_WRITE 0
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 0
/* Set this to 1 if you need to send control replies which are generated
* "on the fly" when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
* usbFunctionSetup(). This saves a couple of bytes.
*/
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
* You must implement the function usbFunctionWriteOut() which receives all
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number
* can be found in 'usbRxToken'.
*/
#define USB_CFG_HAVE_FLOWCONTROL 0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
* If you eat the received message and don't want default processing to
* proceed, do a return after doing your things. One possible application
* (besides debugging) is to flash a status LED on each packet.
*/
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
*/
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
* received.
*/
#define USB_COUNT_SOF 0
/* define this macro to 1 if you need the global variable "usbSofCount" which
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use one of obdev's free shared
* VID/PID pairs. Be sure to read USBID-License.txt for rules!
*/
#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'L', 'E', 'D', 'C', 't', 'l', 'H', 'I', 'D'
#define USB_CFG_DEVICE_NAME_LEN 9
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Same as above for the serial number. If you don't want a serial number,
* undefine the macros.
* It may be useful to provide the serial number through other means than at
* compile time. See the section about descriptor properties below for how
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is "vendor specific".
*/
#define USB_CFG_INTERFACE_CLASS 3
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
*/
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* "usbHidReportDescriptor" to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
/* #define USB_PUBLIC static */
/* Use the define above if you #include usbdrv.c instead of linking against it.
* This technique saves a couple of bytes in flash memory.
*/
/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
* provide our own. These can be provided as (1) fixed length static data in
* flash memory, (2) fixed length static data in RAM or (3) dynamically at
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
* information about this function.
* Descriptor handling is configured through the descriptor's properties. If
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
* the driver must know the descriptor's length. The descriptor itself is
* found at the address of a well known identifier (see below).
* List of static descriptor names (must be declared PROGMEM if in flash):
* char usbDescriptorDevice[];
* char usbDescriptorConfiguration[];
* char usbDescriptorHidReport[];
* char usbDescriptorString0[];
* int usbDescriptorStringVendor[];
* int usbDescriptorStringDevice[];
* int usbDescriptorStringSerialNumber[];
* Other descriptors can't be provided statically, they must be provided
* dynamically at runtime.
*
* Descriptor properties are or-ed or added together, e.g.:
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
*
* The following descriptors are defined:
* USB_CFG_DESCR_PROPS_DEVICE
* USB_CFG_DESCR_PROPS_CONFIGURATION
* USB_CFG_DESCR_PROPS_STRINGS
* USB_CFG_DESCR_PROPS_STRING_0
* USB_CFG_DESCR_PROPS_STRING_VENDOR
* USB_CFG_DESCR_PROPS_STRING_PRODUCT
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
* USB_CFG_DESCR_PROPS_HID
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
/* ----------------------- Optional MCU Description ------------------------ */
/* The following configurations have working defaults in usbdrv.h. You
* usually don't need to set them explicitly. Only if you want to run
* the driver on a device which is not yet supported or with a compiler
* which is not fully supported (such as IAR C) or if you use a differnt
* interrupt than INT0, you may have to define some of these.
*/
/* #define USB_INTR_CFG MCUCR */
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */
/* #define USB_INTR_CFG_CLR 0 */
/* #define USB_INTR_ENABLE GIMSK */
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_h_included__ */

View File

@@ -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://www.obdev.at/goto.php?t=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/

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
# 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

View File

@@ -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), GNU GPL v3 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 -lsetupapi
EXE_SUFFIX= .exe

View File

@@ -0,0 +1,324 @@
/* 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: hiddata.c 743 2009-04-15 15:00:49Z cs $
*/
#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));
#if 0
/* If we want to access a mouse our keyboard, we can only use feature
* requests as the device is locked by Windows. It must be opened
* with ACCESS_TYPE_NONE.
*/
handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
#endif
/* 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_DEVICE | 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_DEVICE | 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 ################################################### */
/* ######################################################################## */

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: hiddata.h 692 2008-11-07 15:07:40Z cs $
*/
#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__ */

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: hidsdi.h 692 2008-11-07 15:07:40Z cs $
*/
/*
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

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: hidtool.c 723 2009-03-16 19:04:32Z cs $
*/
#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;
memset(buffer, 0, 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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,164 @@
# Name: Makefile
# Project: hid-data 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
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

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: main.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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 */
}
/* ------------------------------------------------------------------------- */
usbMsgLen_t 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 USB_NO_MSG; /* 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 USB_NO_MSG; /* 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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,350 @@
/* Name: usbconfig.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 740 2009-04-13 18:23:31Z cs $
*/
#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
*/
/* ---------------------------- Hardware Config ---------------------------- */
#define USB_CFG_IOPORTNAME D
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 2
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0! [You can also use other interrupts, see section
* "Optional MCU Description" below, or you can connect D- to the interrupt, as
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
* 16500 and 20000. The 12.8 MHz and 16.5 MHz versions of the code require no
* crystal, they tolerate +/- 1% deviation from the nominal frequency. All
* other rates require a precision of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
#define USB_CFG_CHECK_CRC 0
/* Define this to 1 if you want that the driver checks integrity of incoming
* data packets (CRC checks). CRC checks cost quite a bit of code size and are
* currently only available for 18 MHz crystal clock. You must choose
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option.
*/
/* ----------------------- Optional Hardware Config ------------------------ */
/* #define USB_CFG_PULLUP_IOPORTNAME D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
/* #define USB_CFG_PULLUP_BIT 4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
*/
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
*/
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
/* Define this to 1 if you want to compile a version with three endpoints: The
* default control endpoint 0, an interrupt-in endpoint 3 (or the number
* configured below) and a catch-all default interrupt-in endpoint as above.
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.
*/
#define USB_CFG_EP3_NUMBER 3
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
* for endpoint 1 (interrupt endpoint). Although you may not need this feature,
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_SUPPRESS_INTR_CODE 0
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
* want to send any data over them. If this macro is defined to 1, functions
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if
* you need the interrupt-in endpoints in order to comply to an interface
* (e.g. HID), but never want to send any data. This option saves a couple
* of bytes in flash memory and the transmit buffers in RAM.
*/
#define USB_CFG_INTR_POLL_INTERVAL 100
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
*/
#define USB_CFG_IS_SELF_POWERED 0
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 20
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
#define USB_CFG_IMPLEMENT_FN_WRITE 1
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 1
/* Set this to 1 if you need to send control replies which are generated
* "on the fly" when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
* usbFunctionSetup(). This saves a couple of bytes.
*/
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
* You must implement the function usbFunctionWriteOut() which receives all
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number
* can be found in 'usbRxToken'.
*/
#define USB_CFG_HAVE_FLOWCONTROL 0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
* If you eat the received message and don't want default processing to
* proceed, do a return after doing your things. One possible application
* (besides debugging) is to flash a status LED on each packet.
*/
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
*/
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
* received.
*/
#define USB_COUNT_SOF 0
/* define this macro to 1 if you need the global variable "usbSofCount" which
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use one of obdev's free shared
* VID/PID pairs. Be sure to read USBID-License.txt for rules!
*/
#define USB_CFG_DEVICE_ID 0xdf, 0x05 /* obdev's shared PID for HIDs */
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'D', 'a', 't', 'a', 'S', 't', 'o', 'r', 'e'
#define USB_CFG_DEVICE_NAME_LEN 9
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Same as above for the serial number. If you don't want a serial number,
* undefine the macros.
* It may be useful to provide the serial number through other means than at
* compile time. See the section about descriptor properties below for how
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is "vendor specific".
*/
#define USB_CFG_INTERFACE_CLASS 3
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
*/
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* "usbHidReportDescriptor" to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
/* #define USB_PUBLIC static */
/* Use the define above if you #include usbdrv.c instead of linking against it.
* This technique saves a couple of bytes in flash memory.
*/
/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
* provide our own. These can be provided as (1) fixed length static data in
* flash memory, (2) fixed length static data in RAM or (3) dynamically at
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
* information about this function.
* Descriptor handling is configured through the descriptor's properties. If
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
* the driver must know the descriptor's length. The descriptor itself is
* found at the address of a well known identifier (see below).
* List of static descriptor names (must be declared PROGMEM if in flash):
* char usbDescriptorDevice[];
* char usbDescriptorConfiguration[];
* char usbDescriptorHidReport[];
* char usbDescriptorString0[];
* int usbDescriptorStringVendor[];
* int usbDescriptorStringDevice[];
* int usbDescriptorStringSerialNumber[];
* Other descriptors can't be provided statically, they must be provided
* dynamically at runtime.
*
* Descriptor properties are or-ed or added together, e.g.:
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
*
* The following descriptors are defined:
* USB_CFG_DESCR_PROPS_DEVICE
* USB_CFG_DESCR_PROPS_CONFIGURATION
* USB_CFG_DESCR_PROPS_STRINGS
* USB_CFG_DESCR_PROPS_STRING_0
* USB_CFG_DESCR_PROPS_STRING_VENDOR
* USB_CFG_DESCR_PROPS_STRING_PRODUCT
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
* USB_CFG_DESCR_PROPS_HID
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
/* ----------------------- Optional MCU Description ------------------------ */
/* The following configurations have working defaults in usbdrv.h. You
* usually don't need to set them explicitly. Only if you want to run
* the driver on a device which is not yet supported or with a compiler
* which is not fully supported (such as IAR C) or if you use a differnt
* interrupt than INT0, you may have to define some of these.
*/
/* #define USB_INTR_CFG MCUCR */
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */
/* #define USB_INTR_CFG_CLR 0 */
/* #define USB_INTR_ENABLE GIMSK */
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_h_included__ */

View File

@@ -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://www.obdev.at/goto.php?t=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/

View File

@@ -0,0 +1,164 @@
# Name: Makefile
# Project: hid-mouse 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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
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

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: main.c 692 2008-11-07 15:07:40Z cs $
*/
/*
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;
}
/* ------------------------------------------------------------------------- */
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
/* The following requests are never used. But since they are required by
* the specification, we implement them in this example.
*/
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,350 @@
/* Name: usbconfig.h
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 740 2009-04-13 18:23:31Z cs $
*/
#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
*/
/* ---------------------------- Hardware Config ---------------------------- */
#define USB_CFG_IOPORTNAME D
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 2
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0! [You can also use other interrupts, see section
* "Optional MCU Description" below, or you can connect D- to the interrupt, as
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
* 16500 and 20000. The 12.8 MHz and 16.5 MHz versions of the code require no
* crystal, they tolerate +/- 1% deviation from the nominal frequency. All
* other rates require a precision of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
#define USB_CFG_CHECK_CRC 0
/* Define this to 1 if you want that the driver checks integrity of incoming
* data packets (CRC checks). CRC checks cost quite a bit of code size and are
* currently only available for 18 MHz crystal clock. You must choose
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option.
*/
/* ----------------------- Optional Hardware Config ------------------------ */
/* #define USB_CFG_PULLUP_IOPORTNAME D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
/* #define USB_CFG_PULLUP_BIT 4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
*/
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
*/
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
/* Define this to 1 if you want to compile a version with three endpoints: The
* default control endpoint 0, an interrupt-in endpoint 3 (or the number
* configured below) and a catch-all default interrupt-in endpoint as above.
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.
*/
#define USB_CFG_EP3_NUMBER 3
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
* for endpoint 1 (interrupt endpoint). Although you may not need this feature,
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_SUPPRESS_INTR_CODE 0
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
* want to send any data over them. If this macro is defined to 1, functions
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if
* you need the interrupt-in endpoints in order to comply to an interface
* (e.g. HID), but never want to send any data. This option saves a couple
* of bytes in flash memory and the transmit buffers in RAM.
*/
#define USB_CFG_INTR_POLL_INTERVAL 100
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
*/
#define USB_CFG_IS_SELF_POWERED 0
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 20
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
#define USB_CFG_IMPLEMENT_FN_WRITE 0
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 0
/* Set this to 1 if you need to send control replies which are generated
* "on the fly" when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
* usbFunctionSetup(). This saves a couple of bytes.
*/
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
* You must implement the function usbFunctionWriteOut() which receives all
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number
* can be found in 'usbRxToken'.
*/
#define USB_CFG_HAVE_FLOWCONTROL 0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
* If you eat the received message and don't want default processing to
* proceed, do a return after doing your things. One possible application
* (besides debugging) is to flash a status LED on each packet.
*/
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
*/
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
* received.
*/
#define USB_COUNT_SOF 0
/* define this macro to 1 if you need the global variable "usbSofCount" which
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use one of obdev's free shared
* VID/PID pairs. Be sure to read USBID-License.txt for rules!
*/
#define USB_CFG_DEVICE_ID 0xe8, 0x03 /* VOTI's lab use PID */
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'M', 'o', 'u', 's', 'e'
#define USB_CFG_DEVICE_NAME_LEN 5
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Same as above for the serial number. If you don't want a serial number,
* undefine the macros.
* It may be useful to provide the serial number through other means than at
* compile time. See the section about descriptor properties below for how
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is "vendor specific".
*/
#define USB_CFG_INTERFACE_CLASS 3
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
*/
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 52
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* "usbHidReportDescriptor" to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
/* #define USB_PUBLIC static */
/* Use the define above if you #include usbdrv.c instead of linking against it.
* This technique saves a couple of bytes in flash memory.
*/
/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
* provide our own. These can be provided as (1) fixed length static data in
* flash memory, (2) fixed length static data in RAM or (3) dynamically at
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
* information about this function.
* Descriptor handling is configured through the descriptor's properties. If
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
* the driver must know the descriptor's length. The descriptor itself is
* found at the address of a well known identifier (see below).
* List of static descriptor names (must be declared PROGMEM if in flash):
* char usbDescriptorDevice[];
* char usbDescriptorConfiguration[];
* char usbDescriptorHidReport[];
* char usbDescriptorString0[];
* int usbDescriptorStringVendor[];
* int usbDescriptorStringDevice[];
* int usbDescriptorStringSerialNumber[];
* Other descriptors can't be provided statically, they must be provided
* dynamically at runtime.
*
* Descriptor properties are or-ed or added together, e.g.:
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
*
* The following descriptors are defined:
* USB_CFG_DESCR_PROPS_DEVICE
* USB_CFG_DESCR_PROPS_CONFIGURATION
* USB_CFG_DESCR_PROPS_STRINGS
* USB_CFG_DESCR_PROPS_STRING_0
* USB_CFG_DESCR_PROPS_STRING_VENDOR
* USB_CFG_DESCR_PROPS_STRING_PRODUCT
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
* USB_CFG_DESCR_PROPS_HID
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
/* ----------------------- Optional MCU Description ------------------------ */
/* The following configurations have working defaults in usbdrv.h. You
* usually don't need to set them explicitly. Only if you want to run
* the driver on a device which is not yet supported or with a compiler
* which is not fully supported (such as IAR C) or if you use a differnt
* interrupt than INT0, you may have to define some of these.
*/
/* #define USB_INTR_CFG MCUCR */
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */
/* #define USB_INTR_CFG_CLR 0 */
/* #define USB_INTR_ENABLE GIMSK */
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_h_included__ */

View File

@@ -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), GNU GPL v3 or proprietary (CommercialLicense.txt)
# This Revision: $Id: Makefile 692 2008-11-07 15:07:40Z cs $
# 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 = $(CPPFLAGS) $(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)

View File

@@ -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), GNU GPL v3 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

View File

@@ -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/

View File

@@ -0,0 +1,203 @@
/* Name: opendevice.c
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.c 740 2009-04-13 18:23:31Z cs $
*/
/*
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;
}
/* ------------------------------------------------------------------------- */

View File

@@ -0,0 +1,77 @@
/* Name: opendevice.h
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: opendevice.h 740 2009-04-13 18:23:31Z cs $
*/
/*
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 or 3.
*/
#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__ */

View File

@@ -0,0 +1,356 @@
/* Name: usbtool.c
* Project: V-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), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbtool.c 740 2009-04-13 18:23:31Z cs $
*/
/*
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;
}