Driver for USB Joystick SBLive and audigy. Work in progress. Right now the USB driver don't work.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26390 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Fredrik Modeen
2008-07-12 10:31:24 +00:00
parent fca1417369
commit 0996e64efd
13 changed files with 2570 additions and 0 deletions
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src add-ons kernel drivers joystick ;
SubInclude HAIKU_TOP src add-ons kernel drivers joystick emuxkigameport ;
SubInclude HAIKU_TOP src add-ons kernel drivers joystick usb_joy ;
@@ -0,0 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers joystick emuxkigameport ;
SetSubDirSupportedPlatformsBeOSCompatible ;
KernelAddon emuxkigameport :
driver.cpp
;
@@ -0,0 +1,279 @@
/*
* Copyright 2008 Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander Coers [email protected]
* Fredrik Modéen [email protected]
* Axel Dörfler [email protected]
*/
#include "driver.h"
#include <stdio.h>
#include <string.h>
#include <KernelExport.h>
#include <PCI.h>
#define TRACE_DRIVER
#ifdef TRACE_DRIVER
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
int32 api_version = B_CUR_DRIVER_API_VERSION;
int num_names = 0;
int num_cards = 0;
char *gDeviceNames[MAX_CARDS + 1];
gameport_info cards[MAX_CARDS];
/* setup_card used to initialize cards, structures or hardware */
static status_t
setup_card (gameport_info* card)
{
char * name = card->name;
uint32 command_reg = 0;
area_id area;
int32 base,size;
dprintf (DRIVER_NAME ": setup_card() trying to init structures \n");
/* enable PCI i/o */
command_reg = PCI_command_io || PCI_command_master ;
(*pci->write_pci_config)(card->info.bus,card->info.device,
card->info.function, PCI_command, 2, 0);
/* disable all i/o -regs and bus_mastering */
base = card->info.u.h0.base_registers[0];
size = card->info.u.h0.base_register_sizes[0];
(*pci->write_pci_config) (card->info.bus,card->info.device,
card->info.function, 0x10, 2, base);
(*pci->write_pci_config) (card->info.bus,card->info.device,
card->info.function, PCI_command, 2, command_reg);
/* enable i/o- regs and possible bus_mastering */
dprintf (DRIVER_NAME ": setup_card() enabled card with i/o regs from "
"0x%04x to 0x%04x \n",base,base+size-1);
if ((*gameport->create_device)((int32)base, &card->joy.driver) < B_OK) {
dprintf (DRIVER_NAME ": setup_card() Gameport setup failed! Failed to "
"load Generic Gameport Module \n");
return B_ERROR;
}
sprintf(card->joy.name1, "joystick/"DRIVER_NAME "/%x", base);
gDeviceNames[num_names++] = card->joy.name1;
gDeviceNames[num_names] = NULL;
return B_OK;
}
extern "C" status_t
init_hardware (void)
{
status_t err = ENODEV;
pci_info info;
int ix = 0;
uint32 buffer;
/* probe PCI bus */
if (get_module (pci_name, (module_info **)&pci))
return ENOSYS;
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
/* look for HARDWARE */
if (info.vendor_id == VENDOR_ID_CREATIVE &&
info.device_id == DEVICE_ID_CREATIVE_EMU10K1) {
err = B_OK;
dprintf (DRIVER_NAME ": init_hardware() found one, Revision %02x\n"
,info.revision);
if (!(info.u.h0.subsystem_id == 0x20 ||
info.u.h0.subsystem_id == 0xc400 ||
(info.u.h0.subsystem_id == 0x21 && info.revision < 6))) {
buffer = (*pci->read_io_32)(info.u.h0.base_registers[0] + HCFG);
buffer |= HCFG_JOYENABLE;
(*pci->write_io_32)(info.u.h0.base_registers[0] + HCFG, buffer);
}
/* Some SB-Live cards need to the Joyenable Bit in the config
Register, others don´t */
}
ix++;
}
put_module (B_PCI_MODULE_NAME);
return err;
}
extern "C" status_t
init_driver (void)
{
area_id area;
area_info ainfo;
pci_info info;
int ix = 0;
dprintf (DRIVER_NAME ": init_driver() " __DATE__ " " __TIME__ "\n");
/* probe PCI bus */
if (get_module (pci_name, (module_info **)&pci))
return ENOSYS;
if (get_module (gameport_name, (module_info **)&gameport)) {
dprintf (DRIVER_NAME ": Failed to load Generic Gameport Module \n");
put_module (pci_name);
return ENOSYS;
}
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
if (info.vendor_id == VENDOR_ID_CREATIVE && (
info.device_id == SBLIVE_ID ||
info.device_id == AUDIGY_ID ||
info.device_id == SBLIVE_DELL_ID)) {
if (num_cards == MAX_CARDS) {
dprintf(DRIVER_NAME ": Too many cards installed!\n");
break;
}
memset(&cards[num_cards], 0, sizeof(gameport_info));
cards[num_cards].info = info;
if (setup_card(&cards[num_cards]))
dprintf(DRIVER_NAME ": Setup of card %ld failed \n",
num_cards + 1);
else
num_cards++;
}
ix++;
}
if (!num_cards) {
dprintf(DRIVER_NAME ": no cards \n");
put_module(pci_name);
return ENODEV;
}
return B_OK;
}
void
uninit_driver (void)
{
int ix = 0;
area_id area;
dprintf (DRIVER_NAME ": uninit_driver()\n");
for (ix = 0; ix < num_cards; ix++) {
area = find_area (AREA_NAME);
if (area >= 0)
delete_area (area);
(*gameport->delete_device)(cards[ix].joy.driver);
}
memset(&cards, 0, sizeof(cards));
put_module (gameport_name);
put_module (pci_name);
num_cards = 0;
}
extern "C" const char**
publish_devices()
{
return (const char **)gDeviceNames;
}
static int
lookup_device_name (const char *name)
{
int i;
for (i = 0; gDeviceNames[i]; i++)
if (!strcmp (gDeviceNames[i], name))
return i;
return -1;
}
static status_t
device_open(const char* name, uint32 flags, void** cookie)
{
int ix;
int offset = -1;
*cookie = NULL;
for (ix = 0; ix < num_cards; ix++) {
if (!strcmp(name, cards[ix].joy.name1)) {
offset = 0;
break;
}
}
if (offset < 0) {
return ENODEV;
}
return (*gameport->open_hook)(cards[ix].joy.driver, flags, cookie);
}
static status_t
device_close(void * cookie)
{
return (*gameport->close_hook)(cookie);
}
static status_t
device_free(void * cookie)
{
return (*gameport->free_hook)(cookie);
}
static status_t
device_control(void * cookie, uint32 iop, void * data, size_t len)
{
return (*gameport->control_hook)(cookie, iop, data, len);
}
static status_t
device_read(void * cookie, off_t pos, void * data, size_t * nread)
{
return (*gameport->read_hook)(cookie, pos, data, nread);
}
static status_t
device_write(void * cookie, off_t pos, const void * data, size_t * nwritten)
{
(*pci->write_io_32) ((int)vaddr,0);
return (*gameport->write_hook)(cookie, pos, data, nwritten);
}
device_hooks gDeviceHooks = {
device_open,
device_close,
device_free,
device_control,
device_read,
device_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
device_hooks*
find_device(const char* name)
{
if (lookup_device_name (name) >= 0) {
return &gDeviceHooks;
}
return NULL;
}
@@ -0,0 +1,52 @@
/*
* Copyright 2008 Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander Coers [email protected]
* Fredrik Modéen [email protected]
*/
#ifndef DRIVER_H
#define DRIVER_H
#include <KernelExport.h>
#include <PCI.h>
#include "joystick_driver.h"
#define VENDOR_ID_CREATIVE 0x1102
#define DEVICE_ID_CREATIVE_EMU10K1 0x0002
#define SBLIVE_ID 0x7002 /*EMU10000*/
#define AUDIGY_ID 0x7003 /*EMU10K2*/
#define SBLIVE_DELL_ID 0x7004 /* Game port for SB Live!, Dell OEM gameport*/
#define DRIVER_NAME "emuxkigameport"
#define AREA_NAME "SB_GAME"
#define MAX_CARDS 2
/* NB global variables are valid only while driver is loaded */
static volatile ulong vaddr; /* PCI */
// use pci manager
static char pci_name[] = B_PCI_MODULE_NAME;
static pci_module_info *pci;
// use generic_gameport_module...
static char gameport_name [] = "generic/gameport/v2";
static generic_gameport_module *gameport;
#define HCFG 0x14 /* Hardware Configuration Register of SB-Live */
#define HCFG_JOYENABLE 0x00000200 /* Mask for enabling Joystick */
struct joystick_dev {
void * driver;
char name1[64];
};
struct gameport_info {
char name[60]; /* used for resources */
pci_info info;
joystick_dev joy;
};
#endif /* DRIVER_H */
@@ -0,0 +1,120 @@
/* ++++++++++
FILE: joystick_driver.h
REVS: $Revision: 1.1 $
NAME: herold
DATE: Tue Jun 4 14:57:25 PDT 1996
+++++ */
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _JOYSTICK_DRIVER_H
#define _JOYSTICK_DRIVER_H
#include <SupportDefs.h>
#include <Drivers.h>
#include <module.h>
typedef struct _joystick {
bigtime_t timestamp;
uint32 horizontal;
uint32 vertical;
bool button1;
bool button2;
} joystick;
/* maximum number of axes on one controller (pads count as 2 axes each) */
#define MAX_AXES 12
/* maximum number of hats on one controller -- PADS SHOULD BE RETURNED AS AXES! */
#define MAX_HATS 8
/* maximum number of buttons on one controller */
#define MAX_BUTTONS 32
/* maximum number of controllers on one port */
#define MAX_STICKS 4
typedef struct _extended_joystick {
bigtime_t timestamp; /* system_time when it was read */
uint32 buttons; /* lsb to msb, 1 == on */
int16 axes[MAX_AXES]; /* -32768 to 32767, X, Y, Z, U, V, W */
uint8 hats[MAX_HATS]; /* 0 through 8 (1 == N, 3 == E, 5 == S, 7 == W) */
} extended_joystick;
#define MAX_CONFIG_SIZE 100
enum { /* flags for joystick module info */
js_flag_force_feedback = 0x1,
js_flag_force_feedback_directional = 0x2
};
typedef struct _joystick_module_info {
char module_name[64];
char device_name[64];
int16 num_axes;
int16 num_buttons;
int16 num_hats;
uint16 _reserved[7];
uint32 flags;
uint16 num_sticks;
int16 config_size;
char device_config[MAX_CONFIG_SIZE]; /* device specific */
} joystick_module_info;
/* Note that joystick_module is something used by the game port driver */
/* to talk to digital joysticks; if you're writing a sound card driver */
/* and want to add support for a /dev/joystick device, use the generic_gameport */
/* module. */
typedef struct _joystick_module {
module_info minfo;
/** "configure" might change the "info" if it auto-detects a device */
int (*configure)(int port, joystick_module_info * info, size_t size, void ** out_cookie);
/** "read" actual data from device into "data" */
int (*read)(void * cookie, int port, extended_joystick * data, size_t size);
/** "crumble" the cookie (deallocate) when done */
int (*crumble)(void * cookie, int port);
/** "force" tells the joystick to exert force on the same axes as input for the specified duration */
int (*force)(void * cookie, int port, bigtime_t duration, extended_joystick * force, size_t size);
int _reserved_;
} joystick_module;
/** Doing force feedback means writing an extended_joystick to the device with force values.
The "timestamp" should be the duration of the feedback. Successive writes will be queued
by the device module. */
enum { /* Joystick driver ioctl() opcodes */
B_JOYSTICK_GET_SPEED_COMPENSATION = B_JOYSTICK_DRIVER_BASE,
/* arg -> ptr to int32 */
B_JOYSTICK_SET_SPEED_COMPENSATION, /* arg -> ptr to int32 */
B_JOYSTICK_GET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_DEVICE_MODULE, /* arg -> ptr to joystick_module; also enters enhanced mode */
B_JOYSTICK_GET_DEVICE_MODULE, /* arg -> ptr to joystick_module */
B_JOYSTICK_SET_RAW_MODE /* arg -> ptr to bool (true or false) */
};
/* Speed compensation is not generally necessary, because the joystick */
/* driver is measuring using real time, not just # cycles. "0" means the */
/* default, center value. + typically returns higher values; - returns lower */
/* A typical range might be from -10 to +10, but it varies by driver */
/* Lower latency will make for more overhead in reading the joystick */
/* ideally, you set this value to just short of how long it takes you */
/* to calculate and render a frame. 30 fps -> latency 33000 */
typedef struct _generic_gameport_module {
module_info minfo;
status_t (*create_device)(int port, void ** out_storage);
status_t (*delete_device)(void * storage);
status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie);
status_t (*close_hook)(void * cookie);
status_t (*free_hook)(void * cookie);
status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len);
status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len);
status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len);
int _reserved_;
} generic_gameport_module;
#endif
@@ -0,0 +1,12 @@
SubDir HAIKU_TOP src add-ons kernel drivers joystick usb_joy ;
SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel bus_managers usb ;
KernelAddon usb_joy :
devlist.c
devmgmt.c
driver.c
hidparse.c
;
@@ -0,0 +1,110 @@
/*
USB joystick driver for BeOS R5
Copyright 2000 (C) ITO, Takayuki
All rights reserved
*/
#include "driver.h"
#define DPRINTF_INFO(x) dprintf x
#define DPRINTF_ERR(x) dprintf x
sem_id my_device_list_lock = -1;
bool my_device_list_changed = true; /* added or removed */
static my_device_info *my_device_list = NULL;
static int my_device_count = 0;
void add_device_info (my_device_info *my_dev)
{
assert (my_dev != NULL);
acquire_sem (my_device_list_lock);
my_dev->next = my_device_list;
my_device_list = my_dev;
my_device_count++;
my_device_list_changed = true;
release_sem (my_device_list_lock);
}
void remove_device_info (my_device_info *my_dev)
{
assert (my_dev != NULL);
acquire_sem (my_device_list_lock);
if (my_device_list == my_dev)
my_device_list = my_dev->next;
else
{
my_device_info *d;
for (d = my_device_list; d != NULL; d = d->next)
{
if (d->next == my_dev)
{
d->next = my_dev->next;
--my_device_count;
my_device_list_changed = true;
break;
}
}
assert (d != NULL);
}
release_sem (my_device_list_lock);
}
my_device_info *search_device_info (int number)
{
my_device_info *my_dev;
acquire_sem (my_device_list_lock);
for (my_dev = my_device_list; my_dev != NULL; my_dev = my_dev->next)
{
if (my_dev->number == number)
break;
}
release_sem (my_device_list_lock);
return my_dev;
}
/*
device names
*/
/* dynamically generated */
char **my_device_names = NULL;
void alloc_device_names (void)
{
assert (my_device_names == NULL);
my_device_names = malloc (sizeof (char *) * (my_device_count + 1));
}
void free_device_names (void)
{
if (my_device_names != NULL)
{
int i;
for (i = 0; my_device_names [i] != NULL; i++)
free (my_device_names [i]);
free (my_device_names);
my_device_names = NULL;
}
}
void rebuild_device_names (void)
{
int i;
my_device_info *my_dev;
assert (my_device_names != NULL);
acquire_sem (my_device_list_lock);
for (i = 0, my_dev = my_device_list; my_dev != NULL; my_dev = my_dev->next)
{
char *p = malloc (strlen (my_base_name) + 4);
assert (p != NULL);
sprintf (p, "%s%d", my_base_name, my_dev->number);
DPRINTF_INFO ((MY_ID "publishing %s\n", p));
my_device_names [i++] = p;
}
my_device_names [i] = NULL;
release_sem (my_device_list_lock);
}
@@ -0,0 +1,82 @@
/*
USB joystick driver for BeOS R5
Copyright 2000 (C) ITO, Takayuki
All rights reserved
*/
#include "driver.h"
#define DPRINTF_INFO(x) dprintf x
#define DPRINTF_ERR(x) dprintf x
static int device_number = 0;
my_device_info *create_device (
const usb_device *dev,
const usb_interface_info *ii)
{
my_device_info *my_dev = NULL;
int number;
area_id area;
sem_id sem;
char area_name [32];
assert (usb != NULL && dev != NULL);
number = device_number++;
my_dev = malloc (sizeof (my_device_info));
if (my_dev == NULL)
return NULL;
my_dev->sem_cb = sem = create_sem (0, DRIVER_NAME "_cb");
if (sem < 0)
{
DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem));
free (my_dev);
return NULL;
}
my_dev->sem_lock = sem = create_sem (1, DRIVER_NAME "_lock");
if (sem < 0)
{
DPRINTF_ERR ((MY_ID "create_sem() failed %d\n", (int) sem));
delete_sem (my_dev->sem_cb);
free (my_dev);
return NULL;
}
sprintf (area_name, DRIVER_NAME "_buffer%d", number);
my_dev->buffer_area = area = create_area (area_name,
(void **) &my_dev->buffer, B_ANY_KERNEL_ADDRESS,
B_PAGE_SIZE, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
if (area < 0)
{
DPRINTF_ERR ((MY_ID "create_area() failed %d\n", (int) area));
delete_sem (my_dev->sem_cb);
delete_sem (my_dev->sem_lock);
free (my_dev);
return NULL;
}
my_dev->buffer_valid = false;
my_dev->dev = dev;
my_dev->number = number;
my_dev->open = 0;
my_dev->open_fds = NULL;
my_dev->active = true;
my_dev->insns = NULL;
my_dev->num_insns = 0;
my_dev->flags = 0;
return my_dev;
}
void remove_device (my_device_info *my_dev)
{
assert (my_dev != NULL);
delete_area (my_dev->buffer_area);
delete_sem (my_dev->sem_cb);
delete_sem (my_dev->sem_lock);
free (my_dev);
}
@@ -0,0 +1,949 @@
/*
USB Joystick driver for BeOS R5
Copyright 2000 (C) ITO, Takayuki
All rights reserved
*/
#include <Debug.h>
#include <driver_settings.h>
#include <limits.h>
#include <stdlib.h>
#include "driver.h"
#define AXIS_VALUE_MIN (-32768)
#define AXIS_VALUE_MAX 32767
#define USB_VID_MICROSOFT 0x045e
#define DPRINTF_INFO(x) dprintf x
#define DPRINTF_ERR(x) dprintf x
/* gameport driver cookie (per open) */
typedef struct driver_cookie
{
struct driver_cookie *next;
my_device_info *my_dev;
bool enhanced;
/* below are valid under enhanced mode */
joystick_module_info joy_mod_info;
} driver_cookie;
/* NB global variables are valid only while driver is loaded */
_EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION;
const char *my_driver_name = "usb_joy";
const char *my_base_name = "joystick/usb/";
usb_module_info *usb;
/*
driver settings
*/
static product_info *prod_info = NULL;
static int num_prod_info = 0;
#define atoi2(s) (int)strtol((s),NULL,0)
static void load_settings_file (void)
{
void *handle;
int entry_idx, param_idx, i;
static product_info null_prod_info;
const driver_settings *settings;
/* make default mapping */
null_prod_info.vendor_id = null_prod_info.product_id = 0;
for (i = 0; i < MAX_AXES; i++)
null_prod_info.axis_tbl [i] = i;
for (i = 0; i < MAX_HATS; i++)
null_prod_info.hat_tbl [i] = i;
for (i = 0; i < MAX_BUTTONS; i++)
null_prod_info.button_tbl [i] = i;
handle = load_driver_settings (my_driver_name);
if (handle == NULL)
{
DPRINTF_ERR ((MY_ID "settings file open error\n"));
prod_info = &null_prod_info;
num_prod_info = 0;
return;
}
settings = get_driver_settings (handle);
/* FIXME: num_prod_info may not be equal to parameter_count */
num_prod_info = settings->parameter_count;
if (num_prod_info == 0)
{
prod_info = &null_prod_info;
return;
}
prod_info = malloc (sizeof (product_info) * (num_prod_info + 1));
if (prod_info == NULL)
{
DPRINTF_ERR ((MY_ID "no memory\n"));
prod_info = &null_prod_info;
num_prod_info = 0;
return;
}
prod_info [0] = null_prod_info;
for (entry_idx = 0; entry_idx < num_prod_info; entry_idx++)
{
product_info *prod;
const driver_parameter *entry;
entry = &settings->parameters [entry_idx];
if (strcmp (entry->name, "version") == 0)
{
/* XXX */
}
else if (strcmp (entry->name, "product") == 0)
{
prod = &prod_info [entry_idx + 1];
*prod = null_prod_info;
assert (entry->value_count == 2);
prod->vendor_id = atoi2 (entry->values [0]);
prod->product_id = atoi2 (entry->values [1]);
for (param_idx = 0; param_idx < entry->parameter_count; param_idx++)
{
const driver_parameter *param;
param = &entry->parameters [param_idx];
if (strcmp (param->name, "button") == 0)
{
int src, dst;
assert (param->value_count >= 2);
src = atoi2 (param->values [0]) - 1;
dst = atoi2 (param->values [1]) - 1;
if (src < MAX_BUTTONS && dst < MAX_BUTTONS)
prod->button_tbl [src] = dst;
}
else if (strcmp (param->name, "axis") == 0)
{
int src, dst;
assert (param->value_count >= 2);
src = atoi2 (param->values [0]) - 1;
dst = atoi2 (param->values [1]) - 1;
if (src < MAX_AXES && dst < MAX_AXES)
prod->axis_tbl [src] = dst;
}
else if (strcmp (param->name, "hat") == 0)
{
int src, dst;
assert (param->value_count >= 2);
src = atoi2 (param->values [0]) - 1;
dst = atoi2 (param->values [1]) - 1;
if (src < MAX_HATS && dst < MAX_HATS)
prod->hat_tbl [src] = dst;
}
}
}
}
unload_driver_settings (handle);
}
/*
callback: got a report, issue next request
*/
static void usb_callback (
void *cookie,
uint32 status,
void *data,
uint32 actual_len)
{
status_t st;
my_device_info *my_dev = cookie;
assert (cookie != NULL);
acquire_sem (my_dev->sem_lock);
my_dev->actual_length = actual_len;
my_dev->bus_status = status; /* B_USB_STATUS_* */
if (status != B_OK)
{
/* request failed */
release_sem (my_dev->sem_lock);
DPRINTF_ERR ((MY_ID "bus status %d\n", (int)status));
//if (status == B_USB_STATUS_IRP_CANCELLED_BY_REQUEST)
//{
/* cancelled: device is unplugged */
return;
//}
#if 1
//st = usb->clear_feature (my_dev->ept->handle, USB_FEATURE_ENDPOINT_HALT);
//if (st != B_OK)
// DPRINTF_ERR ((MY_ID "clear_feature() error %d\n", (int)st));
#endif
}
else
{
/* got a report */
#if 0
int i;
char linbuf [256];
uint8 *buffer = my_dev->buffer;
for (i = 0; i < my_dev->total_report_size; i++)
sprintf (&linbuf[i*3], "%02X ", buffer [i]);
DPRINTF_INFO ((MY_ID "input report: %s\n", linbuf));
#endif
my_dev->buffer_valid = true;
my_dev->timestamp = system_time ();
release_sem (my_dev->sem_lock);
}
/* issue next request */
st = usb->queue_interrupt (my_dev->ept->handle, my_dev->buffer,
my_dev->total_report_size, usb_callback, my_dev);
if (st != B_OK)
{
/* XXX probably endpoint stall */
DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)st));
}
}
/*
USB specific device hooks
*/
static status_t my_device_added (
const usb_device *dev,
void **cookie)
{
my_device_info *my_dev;
const usb_device_descriptor *dev_desc;
const usb_configuration_info *conf;
const usb_interface_info *intf;
status_t st;
usb_hid_descriptor *hid_desc;
uint8 *rep_desc = NULL;
size_t desc_len, actual;
decomp_item *items;
size_t num_items;
int i, ifno, fd, report_id;
assert (dev != NULL && cookie != NULL);
DPRINTF_INFO ((MY_ID "device_added()\n"));
dev_desc = usb->get_device_descriptor (dev);
DPRINTF_INFO ((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n",
dev_desc->vendor_id, dev_desc->product_id));
/* check interface class */
if ((conf = usb->get_nth_configuration
(dev, DEFAULT_CONFIGURATION)) == NULL)
{
DPRINTF_ERR ((MY_ID "cannot get default configuration\n"));
return B_ERROR;
}
for (ifno = 0; ifno < conf->interface_count; ifno++)
{
/* This is C; I can use "class" :-> */
int class, subclass, protocol;
intf = conf->interface [ifno].active;
class = intf->descr->interface_class;
subclass = intf->descr->interface_subclass;
protocol = intf->descr->interface_protocol;
DPRINTF_INFO ((MY_ID "interface %d: class %d, subclass %d, protocol %d\n",
ifno, class, subclass, protocol));
if (class == USB_CLASS_HID && subclass == 0)
break;
}
if (ifno >= conf->interface_count)
{
DPRINTF_INFO ((MY_ID "Non-boot HID interface not found\n"));
return B_ERROR;
}
/* read HID descriptor */
desc_len = sizeof (usb_hid_descriptor);
hid_desc = malloc (desc_len);
st = usb->send_request (dev,
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
USB_REQUEST_GET_DESCRIPTOR,
USB_DESCRIPTOR_HID << 8, ifno, desc_len,
hid_desc, &desc_len);
DPRINTF_INFO ((MY_ID "get_hid_desc: st=%d, len=%d\n",
(int) st, (int)desc_len));
if (st != B_OK)
desc_len = 256; /* XXX */
/* read report descriptor */
desc_len = hid_desc->descriptor_info [0].descriptor_length;
free (hid_desc);
rep_desc = malloc (desc_len);
assert (rep_desc != NULL);
st = usb->send_request (dev,
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
USB_REQUEST_GET_DESCRIPTOR,
USB_DESCRIPTOR_HID_REPORT << 8, ifno, desc_len,
rep_desc, &desc_len);
DPRINTF_INFO ((MY_ID "get_hid_rep_desc: st=%d, len=%d\n",
(int) st, (int)desc_len));
if (st != B_OK)
{
free (rep_desc);
return B_ERROR;
}
/* save report descriptor for troubleshooting */
fd = open ("/tmp/rep_desc.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0)
{
write (fd, rep_desc, desc_len);
close (fd);
}
/* XXX check application type */
/* Generic Desktop : Joystick or Game Pad */
/* or SideWinder Strategic Commander */
if (memcmp (rep_desc, "\x05\x01\x09\x04", 4) != 0 &&
memcmp (rep_desc, "\x05\x01\x09\x05", 4) != 0 &&
memcmp (rep_desc, "\x05\x01\x09\x00", 4) != 0)
{
DPRINTF_INFO ((MY_ID "not a game controller\n"));
free (rep_desc);
return B_ERROR;
}
/* configuration */
if ((st = usb->set_configuration (dev, conf)) != B_OK)
{
DPRINTF_ERR ((MY_ID "set_configuration() failed %d\n", (int)st));
free (rep_desc);
return B_ERROR;
}
if ((my_dev = create_device (dev, intf)) == NULL)
{
free (rep_desc);
return B_ERROR;
}
/* decompose report descriptor */
num_items = desc_len; /* XXX */
items = malloc (sizeof (decomp_item) * num_items);
assert (items != NULL);
decompose_report_descriptor (rep_desc, desc_len, items, &num_items);
free (rep_desc);
/* parse report descriptor */
my_dev->num_insns = num_items; /* XXX */
my_dev->insns = malloc (sizeof (report_insn) * my_dev->num_insns);
assert (my_dev->insns != NULL);
parse_report_descriptor (items, num_items, my_dev->insns,
&my_dev->num_insns, &my_dev->total_report_size, &report_id);
free (items);
realloc (my_dev->insns, sizeof (report_insn) * my_dev->num_insns);
DPRINTF_INFO ((MY_ID "%d items, %d insns, %d bytes\n",
(int)num_items, (int)my_dev->num_insns, (int)my_dev->total_report_size));
/* count axes, hats and buttons */
count_controls (my_dev->insns, my_dev->num_insns,
&my_dev->num_axes, &my_dev->num_hats, &my_dev->num_buttons);
DPRINTF_INFO ((MY_ID "%d axes, %d hats, %d buttons\n",
my_dev->num_axes, my_dev->num_hats, my_dev->num_buttons));
if (my_dev->num_axes > MAX_AXES)
my_dev->num_axes = MAX_AXES;
if (my_dev->num_buttons > MAX_BUTTONS)
my_dev->num_buttons = MAX_BUTTONS;
if (my_dev->num_hats > MAX_HATS)
my_dev->num_hats = MAX_HATS;
/* lookup driver settings */
my_dev->prod_info = &prod_info [0]; /* default mapping */
for (i = 0; i < num_prod_info; i++)
{
const product_info *p = &prod_info [i + 1];
if (p->vendor_id == dev_desc->vendor_id &&
p->product_id == dev_desc->product_id)
{
my_dev->prod_info = p;
DPRINTF_INFO ((MY_ID "settings found for 0x%04x 0x%04x\n",
p->vendor_id, p->product_id));
break;
}
}
/* XXX */
if (dev_desc->vendor_id == USB_VID_MICROSOFT)
{
DPRINTF_INFO ((MY_ID "Microsoft product\n"));
my_dev->flags = FLAG_FORCE_SIGNED;
}
/* get initial state */
st = usb->send_request (dev,
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS,
USB_REQUEST_HID_GET_REPORT,
0x0100 | report_id, ifno, my_dev->total_report_size,
my_dev->buffer, &actual);
if (st == B_OK)
my_dev->buffer_valid = true;
else
DPRINTF_ERR ((MY_ID "Get_Report failed %d\n", (int)st));
my_dev->timestamp = system_time ();
/* issue interrupt transfer */
my_dev->ept = &intf->endpoint [0]; /* interrupt IN */
st = usb->queue_interrupt (my_dev->ept->handle, my_dev->buffer,
my_dev->total_report_size, usb_callback, my_dev);
if (st != B_OK)
{
DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)st));
return B_ERROR;
}
/* create a port */
add_device_info (my_dev);
*cookie = my_dev;
DPRINTF_INFO ((MY_ID "added %d\n", my_dev->number));
return B_OK;
}
static status_t my_device_removed (void *cookie)
{
my_device_info *my_dev = cookie;
assert (cookie != NULL);
DPRINTF_INFO ((MY_ID "device_removed(%d)\n", my_dev->number));
usb->cancel_queued_transfers (my_dev->ept->handle);
remove_device_info (my_dev);
if (my_dev->open == 0)
{
if (my_dev->insns != NULL)
free (my_dev->insns);
remove_device (my_dev);
}
else
{
DPRINTF_INFO ((MY_ID "%s%d still open\n", my_base_name, my_dev->number));
my_dev->active = false;
}
return B_OK;
}
static usb_notify_hooks my_notify_hooks =
{
my_device_added, my_device_removed
};
#define SUPPORTED_DEVICES 1
usb_support_descriptor my_supported_devices [SUPPORTED_DEVICES] =
{
{ USB_CLASS_HID, 0, 0, 0, 0 },
};
/* ----------
my_device_open - handle open() calls
----- */
static status_t my_device_open
(const char *name,
uint32 flags,
driver_cookie **out_cookie)
{
driver_cookie *cookie;
my_device_info *my_dev;
int number;
const char *p;
assert (name != NULL);
assert (out_cookie != NULL);
DPRINTF_INFO ((MY_ID "open(%s)\n", name));
if (strlen (name) < strlen (my_base_name) + 1) /* "0" */
return B_ENTRY_NOT_FOUND;
p = &name [strlen (my_base_name)];
number = atoi (p);
if ((my_dev = search_device_info (number)) == NULL)
return B_ENTRY_NOT_FOUND;
if ((cookie = malloc (sizeof (driver_cookie))) == NULL)
return B_NO_MEMORY;
acquire_sem (my_dev->sem_lock);
cookie->enhanced = false;
cookie->my_dev = my_dev;
cookie->next = my_dev->open_fds;
my_dev->open_fds = cookie;
my_dev->open++;
release_sem (my_dev->sem_lock);
*out_cookie = cookie;
DPRINTF_INFO ((MY_ID "device %d open (%d)\n", number, my_dev->open));
return B_OK;
}
/*
interpret input report
*/
static int sign_extend
(int value,
int size)
{
if (value & (1 << (size - 1)))
return value | (UINT_MAX << size);
else
return value;
}
static void interpret_report
(uint8 *report,
size_t total_report_size,
const report_insn *insns,
size_t num_insns,
const product_info *prod_info,
uint flags,
extended_joystick *joy_data)
{
int i, j, axis_idx, hat_idx;
assert (report != NULL);
assert (insns != NULL);
assert (prod_info != NULL);
assert (joy_data != NULL);
report [total_report_size] = 0; /* guard */
joy_data->axes [0] = 0; /* X and Y axes are handled specially */
joy_data->axes [1] = 0;
axis_idx = 2;
hat_idx = 0;
joy_data->buttons = 0;
for (i = 0; i < num_insns; i++)
{
const report_insn *insn = &insns [i];
int32 value =
(((report [insn->byte_idx + 1] << 8) |
report [insn->byte_idx]) >> insn->bit_pos)
& ((1 << insn->num_bits) - 1);
int32 range;
switch (insn->ctrl_type)
{
case TYPE_BUTTON:
if (insn->attributes & 2) /* variable */
{
if (insn->usage.id_min - 1 < MAX_BUTTONS)
joy_data->buttons |=
(value & 1) << prod_info->button_tbl [insn->usage.id_min - 1];
}
else
{
value = value - insn->log_min + insn->usage.id_min;
if (value - 1 < MAX_BUTTONS)
joy_data->buttons |= 1 << prod_info->button_tbl [value - 1];
}
break;
case TYPE_AXIS_X:
case TYPE_AXIS_Y:
case TYPE_AXIS:
/*
literal values returned by device should be bound by
logical min/max, but actually physical min/max
in case of game controllers
*/
if (insn->is_phy_signed)
{
/* rarely */
range = insn->signed_phy_max - insn->signed_phy_min + 1;
value = sign_extend (value, insn->num_bits);
}
else
{
range = insn->phy_max - insn->phy_min + 1;
if ((flags & FLAG_FORCE_SIGNED) && insn->is_log_signed)
/* MS SideWinder GamePad Pro, FreeStyle Pro,
Strategic Commander */
value = sign_extend (value, insn->num_bits);
else
/* most vendors, MS SideWinder USB GamePad,
SideWinder Joystick */
value -= range >> 1;
}
value = value * (AXIS_VALUE_MAX - AXIS_VALUE_MIN + 1) / range;
switch (insn->ctrl_type)
{
case TYPE_AXIS_X: j = 0; break;
case TYPE_AXIS_Y: j = 1; break;
case TYPE_AXIS: j = axis_idx++; break;
}
joy_data->axes [prod_info->axis_tbl [j]] = value;
break;
case TYPE_HAT:
if (value < insn->log_min || insn->log_max < value)
value = 0; /* null state */
else
{
int num_pos = insn->log_max - insn->log_min + 1;
value -= insn->log_min;
switch (num_pos)
{
case 4:
/* 4-pos (0,1,2,3) to 8-pos (0,2,4,6) */
value <<= 1;
break;
case 8:
/* do nothing */
break;
default:
/* XXX */
DPRINTF_ERR ((MY_ID "%d-position hat unsupported\n", num_pos));
break;
}
value++; /* 1 origin */
}
joy_data->hats [prod_info->hat_tbl [hat_idx++]] = value;
break;
}
}
}
/* ----------
my_device_read - handle read() calls
----- */
static status_t my_device_read
(driver_cookie *cookie,
off_t position,
void *buf,
size_t *num_bytes)
{
status_t err = B_ERROR;
my_device_info *my_dev;
extended_joystick joy_data;
assert (cookie != NULL);
assert (buf != NULL);
assert (num_bytes != NULL);
my_dev = cookie->my_dev;
assert (my_dev != NULL);
// DPRINTF_INFO ((MY_ID "read(%d)\n", my_dev->number));
if (!my_dev->active)
return B_ERROR; /* already unplugged */
memset (&joy_data, 0, sizeof (extended_joystick));
if (my_dev->buffer_valid)
{
interpret_report (my_dev->buffer, my_dev->total_report_size,
my_dev->insns, my_dev->num_insns,
my_dev->prod_info, my_dev->flags, &joy_data);
}
if (cookie->enhanced && *num_bytes >= sizeof (extended_joystick))
{
/* enhanced mode */
joy_data.timestamp = my_dev->timestamp;
memcpy (buf, &joy_data, sizeof (extended_joystick));
*num_bytes = sizeof (extended_joystick);
err = B_OK;
}
if (!cookie->enhanced && *num_bytes >= sizeof (joystick))
{
/* standard mode */
joystick std_joy_data;
std_joy_data.timestamp = my_dev->timestamp;
std_joy_data.horizontal = joy_data.axes [0];
std_joy_data.vertical = joy_data.axes [1];
/* false if pressed */
std_joy_data.button1 = (joy_data.buttons & 1) ? false : true;
std_joy_data.button2 = (joy_data.buttons & 2) ? false : true;
memcpy (buf, &std_joy_data, sizeof (joystick));
*num_bytes = sizeof (joystick);
err = B_OK;
}
return err;
}
/* ----------
my_device_write - handle write() calls
----- */
static status_t my_device_write
(driver_cookie *cookie,
off_t position,
const void *buf,
size_t *num_bytes)
{
return B_ERROR;
}
/* ----------
my_device_control - handle ioctl calls
----- */
static status_t my_device_control
(driver_cookie *cookie,
uint32 op,
void *arg,
size_t len)
{
status_t err = B_ERROR;
my_device_info *my_dev;
assert (cookie != NULL);
my_dev = cookie->my_dev;
assert (my_dev != NULL);
DPRINTF_INFO ((MY_ID "ioctl(%u)\n", (int)op));
if (!my_dev->active)
return B_ERROR; /* already unplugged */
switch (op)
{
case B_JOYSTICK_SET_DEVICE_MODULE:
/* called by BJoystick, Joysticks pref */
/*
if this ioctl returns successfully,
Joysticks preference assumes that
the joystick product may be connected
*/
assert (arg != NULL);
if (!cookie->enhanced &&
strstr (((joystick_module_info*)arg)->device_name, "USB") != NULL) /* XXX */
{
cookie->joy_mod_info = *(joystick_module_info *)arg;
/* currently I don't use a joystick module */
/* set actual # of controls detected */
/* this will be returned in B_JOYSTICK_GET_DEVICE_MODULE */
cookie->joy_mod_info.num_axes = my_dev->num_axes;
cookie->joy_mod_info.num_hats = my_dev->num_hats;
cookie->joy_mod_info.num_buttons = my_dev->num_buttons;
/* enter enhanced mode */
cookie->enhanced = true;
err = B_OK;
}
break;
case B_JOYSTICK_GET_DEVICE_MODULE:
/* return modified info */
assert (arg != NULL);
if (cookie->enhanced)
{
*(joystick_module_info *)arg = cookie->joy_mod_info;
err = B_OK;
}
break;
case B_JOYSTICK_GET_SPEED_COMPENSATION:
case B_JOYSTICK_SET_SPEED_COMPENSATION:
case B_JOYSTICK_GET_MAX_LATENCY:
case B_JOYSTICK_SET_MAX_LATENCY:
case B_JOYSTICK_SET_RAW_MODE:
default:
/* not implemented */
break;
}
return err;
}
/* ----------
my_device_close - handle close() calls
----- */
static status_t my_device_close
(driver_cookie *cookie)
{
my_device_info *my_dev;
assert (cookie != NULL && cookie->my_dev != NULL);
my_dev = cookie->my_dev;
DPRINTF_INFO ((MY_ID "close(%d)\n", my_dev->number));
/* detach the cookie from list */
acquire_sem (my_dev->sem_lock);
if (my_dev->open_fds == cookie)
my_dev->open_fds = cookie->next;
else
{
driver_cookie *p;
for (p = my_dev->open_fds; p != NULL; p = p->next)
{
if (p->next == cookie)
{
p->next = cookie->next;
break;
}
}
}
--my_dev->open;
release_sem (my_dev->sem_lock);
return B_OK;
}
/* -----
my_device_free - called after the last device is closed, and after
all i/o is complete.
----- */
static status_t my_device_free
(driver_cookie *cookie)
{
my_device_info *my_dev;
assert (cookie != NULL && cookie->my_dev != NULL);
my_dev = cookie->my_dev;
DPRINTF_INFO ((MY_ID "free(%d)\n", my_dev->number));
free (cookie);
if (my_dev->open > 0)
DPRINTF_INFO ((MY_ID "%d opens left\n", my_dev->open));
else if (!my_dev->active)
{
DPRINTF_INFO ((MY_ID "removed %d\n", my_dev->number));
if (my_dev->insns != NULL)
free (my_dev->insns);
remove_device (my_dev);
}
return B_OK;
}
/* -----
function pointers for the device hooks entry points
----- */
static device_hooks my_device_hooks = {
(device_open_hook) my_device_open,
(device_close_hook) my_device_close,
(device_free_hook) my_device_free,
(device_control_hook) my_device_control,
(device_read_hook) my_device_read,
(device_write_hook) my_device_write,
NULL, NULL, NULL, NULL
};
/* ----------
init_hardware - called once the first time the driver is loaded
----- */
_EXPORT status_t init_hardware (void)
{
DPRINTF_INFO ((MY_ID "init_hardware() " __DATE__ " " __TIME__ "\n"));
return B_OK;
}
/* ----------
init_driver - optional function - called every time the driver
is loaded.
----- */
_EXPORT status_t init_driver (void)
{
DPRINTF_INFO ((MY_ID "init_driver() " __DATE__ " " __TIME__ "\n"));
if (get_module (B_USB_MODULE_NAME, (module_info **) &usb) != B_OK)
return B_ERROR;
if ((my_device_list_lock = create_sem (1, "dev_list_lock")) < 0)
{
put_module (B_USB_MODULE_NAME);
return my_device_list_lock; /* error code */
}
load_settings_file ();
usb->register_driver (my_driver_name, my_supported_devices,
SUPPORTED_DEVICES, NULL);
usb->install_notify (my_driver_name, &my_notify_hooks);
DPRINTF_INFO ((MY_ID "init_driver() OK\n"));
return B_OK;
}
/* ----------
uninit_driver - optional function - called every time the driver
is unloaded
----- */
_EXPORT void uninit_driver (void)
{
DPRINTF_INFO ((MY_ID "uninit_driver()\n"));
usb->uninstall_notify (my_driver_name);
if (prod_info != NULL && num_prod_info > 0)
free (prod_info);
delete_sem (my_device_list_lock);
put_module (B_USB_MODULE_NAME);
free_device_names ();
}
/*
publish_devices
device names are generated dynamically
*/
_EXPORT const char **publish_devices (void)
{
DPRINTF_INFO ((MY_ID "publish_devices()\n"));
if (my_device_list_changed)
{
free_device_names ();
alloc_device_names ();
if (my_device_names != NULL)
rebuild_device_names ();
my_device_list_changed = false;
}
assert (my_device_names != NULL);
return (const char **) my_device_names;
}
/* ----------
find_device - return ptr to device hooks structure for a
given device name
----- */
_EXPORT device_hooks *find_device
(const char *name)
{
assert (name != NULL);
DPRINTF_INFO ((MY_ID "find_device(%s)\n", name));
if (strncmp (name, my_base_name, strlen (my_base_name)) != 0)
return NULL;
return &my_device_hooks;
}
@@ -0,0 +1,147 @@
/*
USB joystick driver for BeOS R5
Copyright 2000 (C) ITO, Takayuki
All rights reserved
*/
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#include <USB3.h>
#include <bus_manager.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "joystick_driver.h"
#include "hidparse.h"
/* HID class-specific definitions */
#define USB_CLASS_HID 3
#define USB_DESCRIPTOR_HID 0x21
#define USB_DESCRIPTOR_HID_REPORT 0x22
#define USB_REQUEST_HID_GET_REPORT 0x01
typedef struct
{
uint8 length;
uint8 descriptor_type;
uint16 hid_version;
uint8 country_code;
uint8 num_descriptors;
struct
{
uint8 descriptor_type;
uint16 descriptor_length;
} _PACKED descriptor_info [1];
} _PACKED usb_hid_descriptor;
/* driver specific definitions */
#define DRIVER_NAME "usb_joy"
#define MY_ID "\033[34m" DRIVER_NAME ":\033[m "
#define MY_ERR "\033[31merror:\033[m "
#define MY_WARN "\033[31mwarning:\033[m "
#define assert(x) \
((x) ? 0 : dprintf (MY_ID "assertion failed at " __FILE__ ", line %d\n", __LINE__))
/* 0-origin */
#define DEFAULT_CONFIGURATION 0
#define BUF_SIZ B_PAGE_SIZE
struct driver_cookie;
/* read from driver settings file */
typedef struct product_info {
uint16 vendor_id, product_id;
int8 axis_tbl [MAX_AXES];
int8 hat_tbl [MAX_HATS];
int8 button_tbl [MAX_BUTTONS];
} product_info;
#define FLAG_FORCE_SIGNED 1
typedef struct my_device_info
{
/* list structure */
struct my_device_info *next;
/* maintain device */
sem_id sem_cb;
sem_id sem_lock;
area_id buffer_area;
void *buffer;
bool buffer_valid;
const usb_device *dev;
int number;
bool active;
int open;
struct driver_cookie *open_fds;
/* workarea for transfer */
int usbd_status, bus_status, cmd_status;
int actual_length;
const usb_endpoint_info *ept;
report_insn *insns;
size_t num_insns;
size_t total_report_size;
int num_buttons, num_axes, num_hats;
bigtime_t timestamp;
const product_info *prod_info;
uint flags;
} my_device_info;
/* driver.c */
extern usb_module_info *usb;
extern const char *my_driver_name;
extern const char *my_base_name;
/* devmgmt.c */
my_device_info *
create_device (const usb_device *dev, const usb_interface_info *ii);
void
remove_device (my_device_info *my_dev);
/* devlist.c */
extern sem_id my_device_list_lock;
extern bool my_device_list_changed;
void add_device_info (my_device_info *my_dev);
void remove_device_info (my_device_info *my_dev);
my_device_info *search_device_info (int number);
extern char **my_device_names;
void alloc_device_names (void);
void free_device_names (void);
void rebuild_device_names (void);
/* usb_raw.c */
status_t
do_control (my_device_info *my_dev, int request_type, int request,
int value, int index, size_t length, void *buf);
status_t
do_bulk_sg (my_device_info *my_dev, const usb_endpoint_info *ept,
const iovec *vec, size_t vecsiz, size_t length);
status_t
do_bulk (my_device_info *my_dev, const usb_endpoint_info *ept,
void *buf, size_t length);
status_t
do_interrupt (my_device_info *my_dev, const usb_endpoint_info *ept,
void *buf, size_t length);
@@ -0,0 +1,484 @@
/*
HID parser
Copyright 2000 (C) ITO, Takayuki
All rights reserved
platform independent
*/
#include <stdlib.h>
#include "hidparse.h"
/*
decompose report descriptor into array of uniform structure
*/
int decompose_report_descriptor
(const unsigned char *desc,
size_t desc_len,
decomp_item *items,
size_t *num_items)
{
int desc_idx, item_idx;
static int item_size [4] = { 0, 1, 2, 4 };
if (desc == NULL || items == NULL || num_items == NULL ||
desc_len <= 0 || *num_items <= 0)
return FAILURE;
/* process one item per loop */
for (desc_idx = item_idx = 0;
desc_idx < desc_len && item_idx < *num_items;
item_idx++)
{
item_prefix pfx;
int size;
unsigned short value16;
unsigned char value8;
decomp_item *item = &items [item_idx];
item->offset = desc_idx;
item->prefix = pfx.i = desc [desc_idx++];
item->type = pfx.b.bType;
if (pfx.i == LONG_ITEM_PREFIX)
{
/* XXX no long item support */
item->size = desc [desc_idx++];
item->tag = desc [desc_idx++];
item->value = item->signed_value = 0;
desc_idx += item->size;
continue;
}
item->size = size = item_size [pfx.b.bSize];
item->tag = pfx.b.bTag;
switch (size)
{
case 4:
item->signed_value =
item->value =
((unsigned long) desc [desc_idx + 3] << 24) |
((unsigned long) desc [desc_idx + 2] << 16) |
( desc [desc_idx + 1] << 8) |
desc [desc_idx ];
break;
case 2:
value16 = (desc [desc_idx + 1] << 8) | desc [desc_idx];
item->value = value16;
item->signed_value = (signed short) value16;
break;
case 1:
value8 = desc [desc_idx];
item->value = value8;
item->signed_value = (signed char) value8;
break;
case 0:
item->value = item->signed_value = 0;
break;
}
desc_idx += size;
}
*num_items = item_idx;
return SUCCESS;
}
/*
parse decomposed items and generate "instructions" to interpret a report
limitations:
only frequently used items are supported
only first collection is used
report ID is abandoned
output/feature reports are not implemented
*/
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
int parse_report_descriptor
(const decomp_item *items,
size_t num_items,
report_insn *insns,
size_t *num_insns,
size_t *total_report_size,
int *first_report_id)
{
#define ITEM_STACK_SIZE 32
#define USAGE_QUEUE_SIZE 256
/* global items */
typedef struct global_items
{
unsigned int usage_page;
BOOL is_log_signed, is_phy_signed;
unsigned long log_max, log_min,
phy_max, phy_min;
signed long signed_log_max, signed_log_min,
signed_phy_max, signed_phy_min;
int report_size, report_id, report_count;
} global_items;
global_items item_state, item_stack [ITEM_STACK_SIZE];
usage_range usage_queue [USAGE_QUEUE_SIZE];
int item_sp = ITEM_STACK_SIZE, usage_qhead = 0, usage_qtail = 0;
/* local items */
// unsigned int usage_id, usage_min, usage_max;
int bit_pos = 0, byte_idx = 0, insn_idx, item_idx, i, collection = 0;
if (items == NULL || insns == NULL || num_insns == NULL ||
*num_insns <= 0 || num_items <= 0)
return FAILURE;
memset (&item_state, 0, sizeof (item_state));
/* interpret one item per loop */
for (insn_idx = item_idx = 0;
item_idx < num_items && insn_idx < *num_insns;
item_idx++)
{
const decomp_item *item = &items [item_idx];
report_insn *insn = &insns [insn_idx];
int tag = item->tag;
switch (item->type)
{
case ITEM_TYPE_MAIN:
switch (tag)
{
case ITEM_TAG_INPUT:
for (i = 0; i < item_state.report_count && insn_idx < *num_insns; i++)
{
if ((item->value & 1) == 0) /* data */
{
if (item->value & 2) /* variable */
{
if (usage_qhead < usage_qtail)
{
usage_range *u = &usage_queue [usage_qhead];
insn->usage.page = u->page;
insn->usage.id_min =
insn->usage.id_max = u->id_min;
if (u->id_min < u->id_max)
u->id_min++;
else
usage_qhead++;
}
else
{
/*
XXX
usages are fewer than report count
queue exhausted: repeat last values
*/
usage_range *u = &usage_queue [usage_qhead - 1];
insn->usage.page = u->page;
insn->usage.id_min =
insn->usage.id_max = u->id_min;
}
}
else
{
if (usage_qhead < usage_qtail)
{
usage_range *u = &usage_queue [usage_qhead];
if (u->id_min < u->id_max)
{
/* usage min / max */
insn->usage = *u;
usage_qhead++;
}
else
{
/*
XXX
sequence of usage IDs
assume currently queued IDs are continuous
and use all of them
*/
insn->usage.page = u->page;
insn->usage.id_min = u->id_min;
insn->usage.id_max = usage_queue [usage_qtail - 1].id_max;
usage_qhead = usage_qtail = 0;
}
}
/*
XXX
queue exhausted
*/
}
insn->attributes = item->value;
insn->is_log_signed = item_state.is_log_signed;
if (item_state.is_log_signed)
{
insn->signed_log_min = item_state.signed_log_min;
insn->signed_log_max = item_state.signed_log_max;
}
else
{
insn->log_min = item_state.log_min;
insn->log_max = item_state.log_max;
}
insn->is_phy_signed = item_state.is_phy_signed;
if (item_state.is_phy_signed)
{
insn->signed_phy_min = item_state.signed_phy_min;
insn->signed_phy_max = item_state.signed_phy_max;
}
else
{
insn->phy_min = item_state.phy_min;
insn->phy_max = item_state.phy_max;
}
insn->byte_idx = byte_idx;
insn->bit_pos = bit_pos;
insn->num_bits = item_state.report_size;
insn++;
insn_idx++;
}
/* to next bit-field */
bit_pos += item_state.report_size;
if (bit_pos >= 8)
{
byte_idx += bit_pos / 8;
bit_pos = bit_pos % 8;
}
}
break;
case ITEM_TAG_OUTPUT:
/* XXX similar to input */
break;
case ITEM_TAG_COLLECTION:
collection++;
// XXX
usage_qhead = usage_qtail = 0;
break;
case ITEM_TAG_FEATURE:
/* XXX similar to input */
break;
case ITEM_TAG_END_COLLECTION:
if (--collection == 0)
/* exit at end of first collection */
goto end;
break;
default:
break;
}
break;
case ITEM_TYPE_GLOBAL:
switch (tag)
{
case ITEM_TAG_USAGE_PAGE:
item_state.usage_page = item->value;
break;
case ITEM_TAG_LOGICAL_MINIMUM:
item_state.log_min = item->value;
item_state.signed_log_min = item->signed_value;
/* FALLTHROUGH */
case ITEM_TAG_PHYSICAL_MINIMUM:
item_state.phy_min = item->value;
item_state.signed_phy_min = item->signed_value;
break;
case ITEM_TAG_LOGICAL_MAXIMUM:
item_state.log_max = item->value;
item_state.signed_log_max = item->signed_value;
item_state.is_log_signed = item_state.log_min > item_state.log_max;
/* FALLTHROUGH */
case ITEM_TAG_PHYSICAL_MAXIMUM:
item_state.phy_max = item->value;
item_state.signed_phy_max = item->signed_value;
item_state.is_phy_signed = item_state.phy_min > item_state.phy_max;
break;
case ITEM_TAG_UNIT_EXPONENT:
break;
case ITEM_TAG_UNIT:
break;
case ITEM_TAG_REPORT_SIZE:
item_state.report_size = item->value;
break;
case ITEM_TAG_REPORT_ID:
/* FIXME */
if (item_state.report_id == 0)
{
item_state.report_id = item->value;
byte_idx++;
}
else
goto end;
break;
case ITEM_TAG_REPORT_COUNT:
item_state.report_count = item->value;
break;
case ITEM_TAG_PUSH:
if (item_sp > 0)
item_stack [--item_sp] = item_state;
break;
case ITEM_TAG_POP:
if (item_sp < ITEM_STACK_SIZE)
item_state = item_stack [item_sp++];
break;
}
break;
case ITEM_TYPE_LOCAL:
switch (tag)
{
case ITEM_TAG_USAGE:
/* can be sequentially used */
/* XXX should be limited to "practical" usages */
/* XXX should support extended (32-bit) usage */
if (usage_qtail < USAGE_QUEUE_SIZE)
{
usage_queue [usage_qtail].page = item_state.usage_page;
usage_queue [usage_qtail].id_min =
usage_queue [usage_qtail].id_max = item->value;
usage_qtail++;
}
break;
case ITEM_TAG_USAGE_MINIMUM:
#if 0
usage_min = item->value;
#endif
if (usage_qtail < USAGE_QUEUE_SIZE)
{
usage_queue [usage_qtail].page = item_state.usage_page;
usage_queue [usage_qtail].id_min = item->value;
}
break;
case ITEM_TAG_USAGE_MAXIMUM:
#if 0
usage_max = item->value;
#endif
if (usage_qtail < USAGE_QUEUE_SIZE)
{
usage_queue [usage_qtail].id_max = item->value;
usage_qtail++;
}
break;
case ITEM_TAG_DESIGNATOR_INDEX:
break;
case ITEM_TAG_DESIGNATOR_MINIMUM:
break;
case ITEM_TAG_DESIGNATOR_MAXIMUM:
break;
case ITEM_TAG_STRING_INDEX:
break;
case ITEM_TAG_STRING_MINIMUM:
break;
case ITEM_TAG_STRING_MAXIMUM:
break;
case ITEM_TAG_DELIMITER:
break;
}
break;
}
}
end:
*num_insns = insn_idx;
if (total_report_size != NULL)
*total_report_size = byte_idx;
if (first_report_id != NULL)
*first_report_id = item_state.report_id;
return SUCCESS;
}
/*
designed for game controllers
*/
int count_controls
(report_insn *insns,
size_t num_insns,
int *num_axes,
int *num_hats,
int *num_buttons)
{
int insn_idx, hats = 0, buttons = 0,
axes = 2; /* X and Y axes are reserved */
if (insns == NULL || num_insns <= 0)
return FAILURE;
for (insn_idx = 0; insn_idx < num_insns; insn_idx++)
{
report_insn *insn = &insns [insn_idx];
insn->ctrl_type = TYPE_NONE;
switch (insn->usage.page)
{
case USAGE_PAGE_GENERIC_DESKTOP:
switch (insn->usage.id_min)
{
case USAGE_ID_X:
insn->ctrl_type = TYPE_AXIS_X;
break;
case USAGE_ID_Y:
insn->ctrl_type = TYPE_AXIS_Y;
break;
case USAGE_ID_Z:
case USAGE_ID_RX:
case USAGE_ID_RY:
case USAGE_ID_RZ:
case USAGE_ID_SLIDER:
case USAGE_ID_DIAL:
case USAGE_ID_WHEEL:
axes++;
insn->ctrl_type = TYPE_AXIS;
break;
case USAGE_ID_HAT_SWITCH:
hats++;
insn->ctrl_type = TYPE_HAT;
break;
}
break;
case USAGE_PAGE_SIMULATION:
switch (insn->usage.id_min)
{
case USAGE_ID_RUDDER:
case USAGE_ID_THROTTLE:
axes++;
insn->ctrl_type = TYPE_AXIS;
break;
}
break;
case USAGE_PAGE_BUTTONS:
if (insn->attributes & 2)
{
buttons++;
}
else
{
buttons += insn->log_max - insn->log_min + 1;
}
insn->ctrl_type = TYPE_BUTTON;
break;
}
}
if (num_axes != NULL)
*num_axes = axes;
if (num_hats != NULL)
*num_hats = hats;
if (num_buttons != NULL)
*num_buttons = buttons;
return SUCCESS;
}
@@ -0,0 +1,204 @@
/*
HID parser
Copyright 2000 (C) ITO, Takayuki
All rights reserved
*/
#include <sys/types.h>
#define SUCCESS 0
#define FAILURE (-1)
#define BOOL int
#define TRUE 1
#define FALSE 0
/*
Report Descriptor Items
see
5.3 Generic Item Format
6.2.2 Report Descriptor
8. Report Protocol
*/
enum
{
ITEM_SIZE_0,
ITEM_SIZE_1,
ITEM_SIZE_2, /* long item */
ITEM_SIZE_4,
};
enum
{
ITEM_TYPE_MAIN,
ITEM_TYPE_GLOBAL,
ITEM_TYPE_LOCAL,
ITEM_TYPE_RESERVED, /* long item */
};
enum
{
/* main items */
ITEM_TAG_INPUT = 8,
ITEM_TAG_OUTPUT,
ITEM_TAG_COLLECTION,
ITEM_TAG_FEATURE,
ITEM_TAG_END_COLLECTION,
/* global items */
ITEM_TAG_USAGE_PAGE = 0,
ITEM_TAG_LOGICAL_MINIMUM,
ITEM_TAG_LOGICAL_MAXIMUM,
ITEM_TAG_PHYSICAL_MINIMUM,
ITEM_TAG_PHYSICAL_MAXIMUM,
ITEM_TAG_UNIT_EXPONENT,
ITEM_TAG_UNIT,
ITEM_TAG_REPORT_SIZE,
ITEM_TAG_REPORT_ID,
ITEM_TAG_REPORT_COUNT,
ITEM_TAG_PUSH,
ITEM_TAG_POP,
/* local items */
ITEM_TAG_USAGE = 0,
ITEM_TAG_USAGE_MINIMUM,
ITEM_TAG_USAGE_MAXIMUM,
ITEM_TAG_DESIGNATOR_INDEX,
ITEM_TAG_DESIGNATOR_MINIMUM,
ITEM_TAG_DESIGNATOR_MAXIMUM,
/* 6 is missing */
ITEM_TAG_STRING_INDEX = 7,
ITEM_TAG_STRING_MINIMUM,
ITEM_TAG_STRING_MAXIMUM,
ITEM_TAG_DELIMITER,
};
typedef struct
{
unsigned char bSize:2, bType:2, bTag:4;
} item_prefix_bitfields;
typedef union
{
item_prefix_bitfields b;
unsigned char i;
} item_prefix;
#define LONG_ITEM_PREFIX 0xfe
/*
parsed item
*/
typedef struct
{
int offset; /* byte offset in report descriptor */
int prefix, size, type, tag;
unsigned long value;
signed long signed_value; /* sign extended: for logical min/max */
} decomp_item;
/*
report "instruction"
*/
/* control types for game controllers */
enum
{
TYPE_NONE,
TYPE_HAT,
TYPE_AXIS, TYPE_AXIS_X, TYPE_AXIS_Y,
TYPE_BUTTON
};
typedef struct usage_range
{
// id_min == id_max for variables
unsigned int page, id_min, id_max;
} usage_range;
typedef struct
{
usage_range usage;
int ctrl_type; /* TYPE_*; set by count_controls() */
int attributes;
BOOL is_log_signed, is_phy_signed;
unsigned long log_min, log_max;
unsigned long phy_min, phy_max;
signed long signed_log_min, signed_log_max;
signed long signed_phy_min, signed_phy_max;
/* used to extract bit-field from report */
/* value = (report [byte_idx] >> bit_pos) & ((1 << num_bits) - 1) */
/* N.B. you need sign extension if signed */
int byte_idx, bit_pos, num_bits;
} report_insn;
int decompose_report_descriptor
(const unsigned char *desc,
size_t desc_len,
decomp_item *items,
size_t *num_items);
int parse_report_descriptor
(const decomp_item *items,
size_t num_items,
report_insn *insns,
size_t *num_insns,
size_t *total_report_size,
int *first_report_id);
/*
Usage Pages/IDs
*/
enum
{
USAGE_PAGE_GENERIC_DESKTOP = 1,
USAGE_PAGE_SIMULATION = 2,
USAGE_PAGE_GAME = 5,
USAGE_PAGE_KEYBOARD = 7,
USAGE_PAGE_LED,
USAGE_PAGE_BUTTONS,
};
/* Page 1: Generic Desktop */
enum
{
USAGE_ID_POINTER = 1,
USAGE_ID_MOUSE = 2,
USAGE_ID_JOYSTICK = 4,
USAGE_ID_GAMEPAD,
USAGE_ID_KEYBOARD,
USAGE_ID_KEYPAD,
USAGE_ID_X = 0x30,
USAGE_ID_Y,
USAGE_ID_Z,
USAGE_ID_RX,
USAGE_ID_RY,
USAGE_ID_RZ,
USAGE_ID_SLIDER,
USAGE_ID_DIAL,
USAGE_ID_WHEEL,
USAGE_ID_HAT_SWITCH,
};
/* Page 2: Simulation */
enum
{
USAGE_ID_RUDDER = 0xBA,
USAGE_ID_THROTTLE = 0xBB,
};
int count_controls
(report_insn *insns,
size_t num_insns,
int *num_axes,
int *num_hats,
int *num_buttons);
@@ -0,0 +1,120 @@
/* ++++++++++
FILE: joystick_driver.h
REVS: $Revision: 1.1 $
NAME: herold
DATE: Tue Jun 4 14:57:25 PDT 1996
+++++ */
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
#ifndef _JOYSTICK_DRIVER_H
#define _JOYSTICK_DRIVER_H
#include <SupportDefs.h>
#include <Drivers.h>
#include <module.h>
typedef struct _joystick {
bigtime_t timestamp;
uint32 horizontal;
uint32 vertical;
bool button1;
bool button2;
} joystick;
/* maximum number of axes on one controller (pads count as 2 axes each) */
#define MAX_AXES 12
/* maximum number of hats on one controller -- PADS SHOULD BE RETURNED AS AXES! */
#define MAX_HATS 8
/* maximum number of buttons on one controller */
#define MAX_BUTTONS 32
/* maximum number of controllers on one port */
#define MAX_STICKS 4
typedef struct _extended_joystick {
bigtime_t timestamp; /* system_time when it was read */
uint32 buttons; /* lsb to msb, 1 == on */
int16 axes[MAX_AXES]; /* -32768 to 32767, X, Y, Z, U, V, W */
uint8 hats[MAX_HATS]; /* 0 through 8 (1 == N, 3 == E, 5 == S, 7 == W) */
} extended_joystick;
#define MAX_CONFIG_SIZE 100
enum { /* flags for joystick module info */
js_flag_force_feedback = 0x1,
js_flag_force_feedback_directional = 0x2
};
typedef struct _joystick_module_info {
char module_name[64];
char device_name[64];
int16 num_axes;
int16 num_buttons;
int16 num_hats;
uint16 _reserved[7];
uint32 flags;
uint16 num_sticks;
int16 config_size;
char device_config[MAX_CONFIG_SIZE]; /* device specific */
} joystick_module_info;
/* Note that joystick_module is something used by the game port driver */
/* to talk to digital joysticks; if you're writing a sound card driver */
/* and want to add support for a /dev/joystick device, use the generic_gameport */
/* module. */
typedef struct _joystick_module {
module_info minfo;
/** "configure" might change the "info" if it auto-detects a device */
int (*configure)(int port, joystick_module_info * info, size_t size, void ** out_cookie);
/** "read" actual data from device into "data" */
int (*read)(void * cookie, int port, extended_joystick * data, size_t size);
/** "crumble" the cookie (deallocate) when done */
int (*crumble)(void * cookie, int port);
/** "force" tells the joystick to exert force on the same axes as input for the specified duration */
int (*force)(void * cookie, int port, bigtime_t duration, extended_joystick * force, size_t size);
int _reserved_;
} joystick_module;
/** Doing force feedback means writing an extended_joystick to the device with force values.
The "timestamp" should be the duration of the feedback. Successive writes will be queued
by the device module. */
enum { /* Joystick driver ioctl() opcodes */
B_JOYSTICK_GET_SPEED_COMPENSATION = B_JOYSTICK_DRIVER_BASE,
/* arg -> ptr to int32 */
B_JOYSTICK_SET_SPEED_COMPENSATION, /* arg -> ptr to int32 */
B_JOYSTICK_GET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_MAX_LATENCY, /* arg -> ptr to long long */
B_JOYSTICK_SET_DEVICE_MODULE, /* arg -> ptr to joystick_module; also enters enhanced mode */
B_JOYSTICK_GET_DEVICE_MODULE, /* arg -> ptr to joystick_module */
B_JOYSTICK_SET_RAW_MODE /* arg -> ptr to bool (true or false) */
};
/* Speed compensation is not generally necessary, because the joystick */
/* driver is measuring using real time, not just # cycles. "0" means the */
/* default, center value. + typically returns higher values; - returns lower */
/* A typical range might be from -10 to +10, but it varies by driver */
/* Lower latency will make for more overhead in reading the joystick */
/* ideally, you set this value to just short of how long it takes you */
/* to calculate and render a frame. 30 fps -> latency 33000 */
typedef struct _generic_gameport_module {
module_info minfo;
status_t (*create_device)(int port, void ** out_storage);
status_t (*delete_device)(void * storage);
status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie);
status_t (*close_hook)(void * cookie);
status_t (*free_hook)(void * cookie);
status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len);
status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len);
status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len);
int _reserved_;
} generic_gameport_module;
#endif