diff --git a/src/add-ons/kernel/drivers/joystick/Jamfile b/src/add-ons/kernel/drivers/joystick/Jamfile new file mode 100644 index 0000000000..1bc546be20 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/Jamfile @@ -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 ; \ No newline at end of file diff --git a/src/add-ons/kernel/drivers/joystick/emuxkigameport/Jamfile b/src/add-ons/kernel/drivers/joystick/emuxkigameport/Jamfile new file mode 100644 index 0000000000..a2112ae312 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/emuxkigameport/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel drivers joystick emuxkigameport ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +KernelAddon emuxkigameport : + driver.cpp +; diff --git a/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.cpp b/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.cpp new file mode 100644 index 0000000000..8e46097103 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.cpp @@ -0,0 +1,279 @@ +/* + * Copyright 2008 Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexander Coers Alexander.Coers@gmx.de + * Fredrik Modéen fredrik@modeen.se + * Axel Dörfler axeld@pinc-software.de + */ + +#include "driver.h" + +#include +#include + +#include +#include + +#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; +} diff --git a/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.h b/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.h new file mode 100644 index 0000000000..1f9fec6171 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/emuxkigameport/driver.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexander Coers Alexander.Coers@gmx.de + * Fredrik Modéen fredrik@modeen.se + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#include +#include + +#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 */ diff --git a/src/add-ons/kernel/drivers/joystick/emuxkigameport/joystick_driver.h b/src/add-ons/kernel/drivers/joystick/emuxkigameport/joystick_driver.h new file mode 100644 index 0000000000..cf57e528b4 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/emuxkigameport/joystick_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 +#include +#include + +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 diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/Jamfile b/src/add-ons/kernel/drivers/joystick/usb_joy/Jamfile new file mode 100644 index 0000000000..e670fffb51 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/devlist.c b/src/add-ons/kernel/drivers/joystick/usb_joy/devlist.c new file mode 100644 index 0000000000..65e7ac869b --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/devlist.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); +} + diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/devmgmt.c b/src/add-ons/kernel/drivers/joystick/usb_joy/devmgmt.c new file mode 100644 index 0000000000..5f9baf0ceb --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/devmgmt.c @@ -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); +} diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/driver.c b/src/add-ons/kernel/drivers/joystick/usb_joy/driver.c new file mode 100644 index 0000000000..1a65e003cb --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/driver.c @@ -0,0 +1,949 @@ +/* + USB Joystick driver for BeOS R5 + Copyright 2000 (C) ITO, Takayuki + All rights reserved +*/ + +#include +#include +#include +#include +#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; +} + diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/driver.h b/src/add-ons/kernel/drivers/joystick/usb_joy/driver.h new file mode 100644 index 0000000000..b8bcc01c14 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/driver.h @@ -0,0 +1,147 @@ +/* + USB joystick driver for BeOS R5 + Copyright 2000 (C) ITO, Takayuki + All rights reserved +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#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); + diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.c b/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.c new file mode 100644 index 0000000000..d14a59ce9e --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.c @@ -0,0 +1,484 @@ +/* + HID parser + Copyright 2000 (C) ITO, Takayuki + All rights reserved + + platform independent +*/ + +#include +#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; +} + diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.h b/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.h new file mode 100644 index 0000000000..0316acde7c --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/hidparse.h @@ -0,0 +1,204 @@ +/* + HID parser + Copyright 2000 (C) ITO, Takayuki + All rights reserved +*/ + +#include + +#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); diff --git a/src/add-ons/kernel/drivers/joystick/usb_joy/joystick_driver.h b/src/add-ons/kernel/drivers/joystick/usb_joy/joystick_driver.h new file mode 100644 index 0000000000..cf57e528b4 --- /dev/null +++ b/src/add-ons/kernel/drivers/joystick/usb_joy/joystick_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 +#include +#include + +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