/*
* Copyright 2007-2008 by Sascha Hlusiak. <saschahlusiak@freedesktop.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Sascha Hlusiak not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Sascha Hlusiak makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* SASCHA HLUSIAK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL SASCHA HLUSIAK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
*/
/**
* This provides the backend for USB-HIDs for NetBSD, OpenBSD and FreeBSD
* Needs the uhid module loaded. Device names are /dev/uhid?
**/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <xorg-server.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <xf86.h>
#include <xf86_OSproc.h>
#include <usbhid.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#ifdef HAVE_DEV_USB_USB_IOCTL_H
#include <dev/usb/usb_ioctl.h>
#endif
#include "jstk.h"
#include "backend_bsd.h"
struct jstk_bsd_hid_data {
int dlen; /* Length of one data chunk */
char *data_buf; /* Data buffer with right size */
struct hid_item axis_item[MAXAXES]; /* Axis HID items */
struct hid_item button_item[MAXBUTTONS]; /* Button HID items */
struct hid_item hat_item[MAXAXES]; /* HID items for hats */
int hats; /* Number of hats */
int hotdata; /* Is unprocessed data available
in data_buf? */
};
static void jstkCloseDevice_bsd(JoystickDevPtr joystick);
static int jstkReadData_bsd(JoystickDevPtr joystick,
JOYSTICKEVENT *event,
int *number);
/***********************************************************************
*
* jstkOpenDevice --
*
* Open and initialize a joystick device
* Returns the filedescriptor, or -1 in case of error
*
***********************************************************************
*/
int
jstkOpenDevice_bsd(JoystickDevPtr joystick, Bool probe)
{
int cur_axis;
int is_joystick, report_id = 0;
int got_something;
struct hid_data *d;
struct hid_item h;
report_desc_t rd;
struct jstk_bsd_hid_data *bsddata;
if ((joystick->fd = open(joystick->device, O_RDWR | O_NDELAY, 0)) < 0) {
xf86Msg(X_ERROR, "Cannot open joystick '%s' (%s)\n", joystick->device,
strerror(errno));
return -1;
}
if ((rd = hid_get_report_desc(joystick->fd)) == 0) {
xf86Msg(X_ERROR, "Joystick: hid_get_report_desc failed: %s\n",
strerror(errno));
close(joystick->fd);
joystick->fd = -1;
return -1;
}
if (ioctl(joystick->fd, USB_GET_REPORT_ID, &report_id) < 0) {
xf86Msg(X_ERROR, "Joystick: ioctl USB_GET_REPORT_ID failed: %s\n",
strerror(errno));
close(joystick->fd);
joystick->fd = -1;
return -1;
}
bsddata = (struct jstk_bsd_hid_data*)
malloc(sizeof(struct jstk_bsd_hid_data));
bsddata->dlen = hid_report_size(rd, hid_input, report_id);
if ((bsddata->data_buf = malloc(bsddata->dlen)) == NULL) {
fprintf(stderr, "error: couldn't malloc %d bytes\n", bsddata->dlen);
hid_dispose_report_desc(rd);
free(bsddata);
close(joystick->fd);
joystick->fd = -1;
return -1;
}
is_joystick = 0;
got_something = 0;
cur_axis = 0;
bsddata->hats = 0;
joystick->num_axes = 0;
joystick->num_buttons = 0;
for (d = hid_start_parse(rd, 1 << hid_input, report_id);
hid_get_item(d, &h); )
{
int usage, page;
page = HID_PAGE(h.usage);
usage = HID_USAGE(h.usage);
is_joystick = is_joystick ||
(h.kind == hid_collection &&
page == HUP_GENERIC_DESKTOP &&
(usage == HUG_JOYSTICK || usage == HUG_GAME_PAD));
if (h.kind != hid_input)
continue;
if (!is_joystick)
continue;
if (page == HUP_GENERIC_DESKTOP) {
if (usage == HUG_HAT_SWITCH) {
if ((bsddata->hats < MAXAXES) && (joystick->num_axes <= MAXAXES-2)) {
got_something = 1;
memcpy(&bsddata->hat_item[bsddata->hats], &h, sizeof(h));
bsddata->hats++;
joystick->num_axes += 2;
}
} else {
if (joystick->num_axes < MAXAXES) {
got_something = 1;
memcpy(&bsddata->axis_item[cur_axis], &h, sizeof(h));
cur_axis++;
joystick->num_axes++;
}
}
} else if (page == HUP_BUTTON) {
if (joystick->num_buttons < MAXBUTTONS) {
got_something = 1;
memcpy(&bsddata->button_item[joystick->num_buttons], &h, sizeof(h));
joystick->num_buttons++;
}
}
}
hid_end_parse(d);
if (!got_something) {
free(bsddata->data_buf);
xf86Msg(X_ERROR, "Joystick: Didn't find any usable axes.\n");
free(bsddata);
close(joystick->fd);
joystick->fd = -1;
return -1;
}
bsddata->hotdata = 0;
joystick->devicedata = (void*) bsddata;
if (probe == TRUE) {
xf86Msg(X_INFO, "Joystick: %d buttons, %d axes\n",
joystick->num_buttons, joystick->num_axes);
}
joystick->open_proc = jstkOpenDevice_bsd;
joystick->read_proc = jstkReadData_bsd;
joystick->close_proc = jstkCloseDevice_bsd;
return joystick->fd;
}
/***********************************************************************
*
* jstkCloseDevice --
*
* close the handle.
*
***********************************************************************
*/
static void
jstkCloseDevice_bsd(JoystickDevPtr joystick)
{
if ((joystick->fd >= 0)) {
xf86CloseSerial(joystick->fd);
joystick->fd = -1;
}
if (joystick->devicedata != NULL) {
if (((struct jstk_bsd_hid_data*)joystick->devicedata)->data_buf)
free(((struct jstk_bsd_hid_data*)joystick->devicedata)->data_buf);
free(joystick->devicedata);
}
}
/***********************************************************************
*
* jstkReadData --
*
* Reads data from fd and stores it in the JoystickDevRec struct
* fills in the type of event and the number of the button/axis
* return 1 if success, 0 otherwise. Success does not neccessarily
* mean that there is a new event waiting.
*
***********************************************************************
*/
static int
jstkReadData_bsd(JoystickDevPtr joystick,
JOYSTICKEVENT *event,
int *number)
{
int j,d;
struct jstk_bsd_hid_data *bsddata =
(struct jstk_bsd_hid_data*)(joystick->devicedata);
if (event != NULL) *event = EVENT_NONE;
if (bsddata->hotdata == 0) {
j= xf86ReadSerial(joystick->fd,
bsddata->data_buf,
bsddata->dlen);
if (j != bsddata->dlen) {
ErrorF("Read: %d byte! Should be %d\n",j,bsddata->dlen);
return 0;
}
bsddata->hotdata = 1;
}
for (j=0; j<joystick->num_axes - (bsddata->hats * 2); j++) {
d = hid_get_data(bsddata->data_buf, &bsddata->axis_item[j]);
/* Scale the range to our expected range of -32768 to 32767 */
d = d - (bsddata->axis_item[j].logical_maximum
- bsddata->axis_item[j].logical_minimum) / 2;
d = d * 65536 / (bsddata->axis_item[j].logical_maximum
- bsddata->axis_item[j].logical_minimum);
if (abs(d) < joystick->axis[j].deadzone) d = 0;
if (d != joystick->axis[j].value) {
joystick->axis[j].value = d;
if (event != NULL) *event = EVENT_AXIS;
if (number != NULL) *number = j;
return 2;
}
}
for (j=0; j<bsddata->hats; j++) {
int a;
int v1_data[9] =
{ 0, 32767, 32767, 32767, 0, -32768, -32768, -32768, 0 };
int v2_data[9] =
{ -32768, -32768, 0, 32767, 32767, 32767, 0, -32767, 0 };
a = j*2 + joystick->num_axes - bsddata->hats *2;
d = hid_get_data(bsddata->data_buf, &bsddata->hat_item[j])
- bsddata->hat_item[j].logical_minimum;
if (joystick->axis[a].value != v1_data[d]) {
joystick->axis[a].value = v1_data[d];
if (event != NULL) *event = EVENT_AXIS;
if (number != NULL) *number = a;
return 2;
}
if (joystick->axis[a+1].value != v2_data[d]) {
joystick->axis[a+1].value = v2_data[d];
if (event != NULL) *event = EVENT_AXIS;
if (number != NULL) *number = a+1;
return 2;
}
}
for (j=0; j<joystick->num_buttons; j++) {
int pressed;
d = hid_get_data(bsddata->data_buf, &bsddata->button_item[j]);
pressed = (d == bsddata->button_item[j].logical_minimum) ? 0 : 1;
if (pressed != joystick->button[j].pressed) {
joystick->button[j].pressed = pressed;
if (event != NULL) *event = EVENT_BUTTON;
if (number != NULL) *number = j;
return 2;
}
}
bsddata->hotdata = 0;
return 1;
}