mirror of https://github.com/nirenjan/libx52.git
Support for input devices - still experimental
This change adds an input device for the Saitek X52 pro flight control system. However, with this change, there is some bug which causes a kernel crash. Still debugging, but committing so that I don't lose data.pull/3/head
parent
a217fa486e
commit
a4ef90d3c1
|
@ -1,5 +1,5 @@
|
|||
obj-m := saitek_x52.o
|
||||
saitek_x52-objs := x52joy.o x52joy_commands.o
|
||||
saitek_x52-objs := x52joy.o x52joy_commands.o x52joy_input.o
|
||||
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
PWD := $(shell pwd)
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#include "x52joy_commands.h"
|
||||
#include "x52joy_common.h"
|
||||
|
@ -27,16 +29,6 @@
|
|||
#define DRIVER_DESC "Saitek X52Pro HOTAS Driver"
|
||||
#define DRIVER_VERSION "1.0"
|
||||
|
||||
#define X52TYPE_X52 1
|
||||
#define X52TYPE_X52PRO 2
|
||||
#define X52TYPE_UNKNOWN 0
|
||||
|
||||
#define VENDOR_ID_SAITEK 0x06a3
|
||||
#define PRODUCT_ID_X52_PRO 0x0762
|
||||
|
||||
#define X52FLAGS_SUPPORTS_MFD (1 << 0)
|
||||
#define X52FLAGS_SUPPORTS_LED (1 << 1)
|
||||
|
||||
static const struct x52_device {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
|
@ -85,7 +77,8 @@ static ssize_t show_text_line(struct device *dev, char *buf, u8 line)
|
|||
line--; /* Convert to 0-based line number */
|
||||
|
||||
if (joy->feat_mfd) {
|
||||
return sprintf(buf, "%s\n", joy->line[line].text);
|
||||
//return sprintf(buf, "%s\n", joy->line[line].text);
|
||||
return sprintf(buf, "%s\n", joy->phys);
|
||||
} else {
|
||||
sprintf(buf, "\n");
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -393,6 +386,8 @@ static int x52_probe(struct usb_interface *intf,
|
|||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct x52_joy *joy = NULL;
|
||||
struct input_dev *idev = NULL;
|
||||
struct usb_endpoint_descriptor *ep_irq_in;
|
||||
int retval = -ENOMEM;
|
||||
int i;
|
||||
|
||||
|
@ -405,12 +400,60 @@ static int x52_probe(struct usb_interface *intf,
|
|||
|
||||
joy = kzalloc(sizeof(*joy), GFP_KERNEL);
|
||||
if (joy == NULL) {
|
||||
dev_err(&intf->dev, "Out of memory\n");
|
||||
dev_err(&intf->dev, "Out of memory: Cannot create joystick\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (idev == NULL) {
|
||||
dev_err(&intf->dev, "Out of memory: Cannot create input\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
joy->idata = usb_alloc_coherent(udev, X52_PACKET_LEN, GFP_KERNEL,
|
||||
&joy->idata_dma);
|
||||
if (!joy->idata) {
|
||||
dev_err(&intf->dev, "Out of memory: Cannot alloc coherent buffer\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
joy->irq_in = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!joy->irq_in) {
|
||||
dev_err(&intf->dev, "Out of memory: Cannot alloc IRQ URB\n");
|
||||
goto error1;
|
||||
}
|
||||
|
||||
joy->udev = usb_get_dev(udev);
|
||||
|
||||
joy->idev = idev;
|
||||
usb_make_path(udev, joy->phys, sizeof(joy->phys));
|
||||
strlcat(joy->phys, "/input0", sizeof(joy->phys));
|
||||
|
||||
idev->name = x52_devices[i].name;
|
||||
idev->phys = joy->phys;
|
||||
usb_to_input_id(udev, &idev->id);
|
||||
idev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(idev, joy);
|
||||
|
||||
idev->open = x52_open;
|
||||
idev->close = x52_close;
|
||||
|
||||
x52_setup_input(idev);
|
||||
|
||||
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(joy->irq_in, udev,
|
||||
usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
|
||||
joy->idata, X52_PACKET_LEN, x52_irq_handler,
|
||||
joy, ep_irq_in->bInterval);
|
||||
joy->irq_in->transfer_dma = joy->idata_dma;
|
||||
joy->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
retval = input_register_device(joy->idev);
|
||||
if (retval) {
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Set the feature bits */
|
||||
joy->feat_mfd = !!(x52_devices[i].flags & X52FLAGS_SUPPORTS_MFD);
|
||||
joy->feat_led = !!(x52_devices[i].flags & X52FLAGS_SUPPORTS_LED);
|
||||
|
@ -457,7 +500,12 @@ static int x52_probe(struct usb_interface *intf,
|
|||
dev_info(&intf->dev, "X52 device now attached\n");
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
usb_free_urb(joy->irq_in);
|
||||
error1:
|
||||
usb_free_coherent(udev, X52_PACKET_LEN, joy->idata, joy->idata_dma);
|
||||
error:
|
||||
input_free_device(idev);
|
||||
kfree(joy);
|
||||
return retval;
|
||||
}
|
||||
|
@ -469,6 +517,11 @@ static void x52_disconnect(struct usb_interface *intf)
|
|||
joy = usb_get_intfdata(intf);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
usb_kill_urb(joy->irq_in);
|
||||
usb_free_urb(joy->irq_in);
|
||||
usb_free_coherent(joy->udev, X52_PACKET_LEN, joy->idata, joy->idata_dma);
|
||||
input_free_device(joy->idev);
|
||||
|
||||
if (joy->feat_mfd) {
|
||||
device_remove_file(&intf->dev, &dev_attr_mfd_line1);
|
||||
device_remove_file(&intf->dev, &dev_attr_mfd_line2);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define X52JOY_COMMON_H
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "x52joy_map.h"
|
||||
|
||||
|
@ -20,7 +21,9 @@ struct x52_joy {
|
|||
struct usb_device *udev;
|
||||
struct urb *irq_in;
|
||||
unsigned char *idata;
|
||||
struct input_dev *dev;
|
||||
dma_addr_t idata_dma;
|
||||
struct input_dev *idev;
|
||||
char phys[64];
|
||||
|
||||
u32 led_status;
|
||||
struct x52_mfd_line line[X52_MFD_LINES];
|
||||
|
@ -49,6 +52,18 @@ struct x52_joy {
|
|||
u8 :5;
|
||||
};
|
||||
|
||||
#define X52_PACKET_LEN 16
|
||||
|
||||
#define X52TYPE_X52 1
|
||||
#define X52TYPE_X52PRO 2
|
||||
#define X52TYPE_UNKNOWN 0
|
||||
|
||||
#define VENDOR_ID_SAITEK 0x06a3
|
||||
#define PRODUCT_ID_X52_PRO 0x0762
|
||||
|
||||
#define X52FLAGS_SUPPORTS_MFD (1 << 0)
|
||||
#define X52FLAGS_SUPPORTS_LED (1 << 1)
|
||||
|
||||
int set_text(struct x52_joy *joy, u8 line_no);
|
||||
int set_brightness(struct x52_joy *joy, u8 target);
|
||||
int set_led(struct x52_joy *joy, u8 target);
|
||||
|
@ -56,4 +71,9 @@ int set_date(struct x52_joy *joy);
|
|||
int set_shift(struct x52_joy *joy);
|
||||
int set_blink(struct x52_joy *joy);
|
||||
|
||||
void x52_irq_handler(struct urb *urb);
|
||||
void x52_setup_input(struct input_dev *idev);
|
||||
int x52_open (struct input_dev *idev);
|
||||
void x52_close (struct input_dev *idev);
|
||||
|
||||
#endif /* !defined X52JOY_COMMON_H */
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Saitek X52 Pro HOTAS driver
|
||||
*
|
||||
* Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
#define DEBUG 1
|
||||
#endif
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#include "x52joy_commands.h"
|
||||
#include "x52joy_common.h"
|
||||
|
||||
#define X52PRO_X_SHIFT 0
|
||||
#define X52PRO_X_MASK 0x3ff
|
||||
#define X52PRO_Y_SHIFT 10
|
||||
#define X52PRO_Y_MASK 0x3ff
|
||||
#define X52PRO_RZ_SHIFT 20
|
||||
#define X52PRO_RZ_MASK 0x3ff
|
||||
|
||||
static void x52pro_decode_urb(struct x52_joy *joy, unsigned char *data)
|
||||
{
|
||||
struct input_dev *idev = joy->idev;
|
||||
u32 stick_axis = le32_to_cpu(&data[0]);
|
||||
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY1, data[8] & 0x01);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY2, data[8] & 0x02);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY3, data[8] & 0x04);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY4, data[8] & 0x08);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY5, data[8] & 0x10);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY6, data[8] & 0x20);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY7, data[8] & 0x40);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY8, data[8] & 0x80);
|
||||
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY9, data[9] & 0x01);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY10, data[9] & 0x02);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY11, data[9] & 0x04);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY12, data[9] & 0x08);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY13, data[9] & 0x10);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY14, data[9] & 0x20);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY15, data[9] & 0x40);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY16, data[9] & 0x80);
|
||||
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY17, data[10] & 0x01);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY18, data[10] & 0x02);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY19, data[10] & 0x04);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY20, data[10] & 0x08);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY21, data[10] & 0x10);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY22, data[10] & 0x20);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY23, data[10] & 0x40);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY24, data[10] & 0x80);
|
||||
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY25, data[11] & 0x01);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY26, data[11] & 0x02);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY27, data[11] & 0x04);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY28, data[11] & 0x08);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY29, data[11] & 0x10);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY30, data[11] & 0x20);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY31, data[11] & 0x40);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY32, data[11] & 0x80);
|
||||
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY33, data[12] & 0x01);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY34, data[12] & 0x02);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY35, data[12] & 0x04);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY36, data[12] & 0x08);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY37, data[12] & 0x10);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY38, data[12] & 0x20);
|
||||
input_report_key(idev, BTN_TRIGGER_HAPPY39, data[12] & 0x40);
|
||||
|
||||
input_report_abs(idev, ABS_X,
|
||||
(stick_axis >> X52PRO_X_SHIFT) & X52PRO_X_MASK);
|
||||
input_report_abs(idev, ABS_Y,
|
||||
(stick_axis >> X52PRO_Y_SHIFT) & X52PRO_Y_MASK);
|
||||
input_report_abs(idev, ABS_RZ,
|
||||
(stick_axis >> X52PRO_RZ_SHIFT) & X52PRO_RZ_MASK);
|
||||
|
||||
input_report_abs(idev, ABS_THROTTLE, data[4]);
|
||||
input_report_abs(idev, ABS_RX, data[5]);
|
||||
input_report_abs(idev, ABS_RY, data[6]);
|
||||
input_report_abs(idev, ABS_Z, data[7]);
|
||||
|
||||
input_report_abs(idev, ABS_TILT_X, data[14] & 0xF);
|
||||
input_report_abs(idev, ABS_TILT_Y, data[14] >> 4);
|
||||
|
||||
switch(data[13]) {
|
||||
case 0x00:
|
||||
input_report_abs(idev, ABS_HAT0X, 0);
|
||||
input_report_abs(idev, ABS_HAT0Y, 0);
|
||||
break;
|
||||
case 0x10:
|
||||
input_report_abs(idev, ABS_HAT0X, 0);
|
||||
input_report_abs(idev, ABS_HAT0Y, -1);
|
||||
break;
|
||||
case 0x20:
|
||||
input_report_abs(idev, ABS_HAT0X, 1);
|
||||
input_report_abs(idev, ABS_HAT0Y, -1);
|
||||
break;
|
||||
case 0x30:
|
||||
input_report_abs(idev, ABS_HAT0X, 1);
|
||||
input_report_abs(idev, ABS_HAT0Y, 0);
|
||||
break;
|
||||
case 0x40:
|
||||
input_report_abs(idev, ABS_HAT0X, 1);
|
||||
input_report_abs(idev, ABS_HAT0Y, 1);
|
||||
break;
|
||||
case 0x50:
|
||||
input_report_abs(idev, ABS_HAT0X, 0);
|
||||
input_report_abs(idev, ABS_HAT0Y, 1);
|
||||
break;
|
||||
case 0x60:
|
||||
input_report_abs(idev, ABS_HAT0X, -1);
|
||||
input_report_abs(idev, ABS_HAT0Y, 1);
|
||||
break;
|
||||
case 0x70:
|
||||
input_report_abs(idev, ABS_HAT0X, -1);
|
||||
input_report_abs(idev, ABS_HAT0Y, 0);
|
||||
break;
|
||||
case 0x80:
|
||||
input_report_abs(idev, ABS_HAT0X, -1);
|
||||
input_report_abs(idev, ABS_HAT0Y, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
void x52_irq_handler(struct urb *urb)
|
||||
{
|
||||
struct x52_joy *joy = urb->context;
|
||||
int retval, status;
|
||||
|
||||
status = urb->status;
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* This URB is terminated, clean up */
|
||||
dbg("%s - URB shutting down with status: %d",
|
||||
__func__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero URB status received: %d",
|
||||
__func__, status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (joy->type) {
|
||||
case X52TYPE_X52PRO:
|
||||
x52pro_decode_urb(joy, joy->idata);
|
||||
break;
|
||||
case X52TYPE_X52:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
err ("%s - usb_submit_urb failed with result %d",
|
||||
__func__, retval);
|
||||
|
||||
}
|
||||
|
||||
void x52_setup_input(struct input_dev *idev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!idev) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable event inputs.
|
||||
*
|
||||
* EV_KEY for buttons (and keyboard events in the future)
|
||||
* EV_ABS for the axis
|
||||
* EV_REL for future mouse support by the mouse stick
|
||||
*/
|
||||
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
|
||||
/* For now, map the buttons directly */
|
||||
for (i = BTN_TRIGGER_HAPPY1; i <= BTN_TRIGGER_HAPPY39; i++) {
|
||||
__set_bit(i, idev->keybit);
|
||||
}
|
||||
|
||||
/* Map the axes */
|
||||
input_set_abs_params(idev, ABS_X, 0, 1023, 16, 512);
|
||||
input_set_abs_params(idev, ABS_Y, 0, 1023, 16, 512);
|
||||
input_set_abs_params(idev, ABS_RZ, 0, 1023, 16, 512);
|
||||
|
||||
input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
|
||||
input_set_abs_params(idev, ABS_RX, 0, 255, 16, 128);
|
||||
input_set_abs_params(idev, ABS_RY, 0, 255, 16, 128);
|
||||
input_set_abs_params(idev, ABS_Z, 0, 255, 0, 0);
|
||||
|
||||
/* Mouse stick */
|
||||
input_set_abs_params(idev, ABS_TILT_X, 0, 15, 0, 8);
|
||||
input_set_abs_params(idev, ABS_TILT_Y, 0, 15, 0, 8);
|
||||
|
||||
/* Hat switch */
|
||||
input_set_abs_params(idev, ABS_HAT0X, -1, 1, 0, 0);
|
||||
input_set_abs_params(idev, ABS_HAT0Y, -1, 1, 0, 0);
|
||||
}
|
||||
|
||||
int x52_open (struct input_dev *idev)
|
||||
{
|
||||
struct x52_joy *joy = input_get_drvdata(idev);
|
||||
|
||||
joy->irq_in->dev = joy->udev;
|
||||
if (usb_submit_urb(joy->irq_in, GFP_KERNEL)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x52_close (struct input_dev *idev)
|
||||
{
|
||||
struct x52_joy *joy = input_get_drvdata(idev);
|
||||
|
||||
usb_kill_urb(joy->irq_in);
|
||||
}
|
||||
|
Loading…
Reference in New Issue