Create workflow to build kernel module

This workflow runs only if there is a change to the kernel_module path.
Consequently, commits that only impact the kernel_module will be ignored
for the standard userspace driver build.

This commit also updates the CodeQL workflow to only run on a scheduled
basis and on pull requests, but not on every push, since this is a
fairly slow script.

Finally, this commit also removes the obsolete kernel module sources,
since they are no longer maintained, and it also provides a hook for
Github actions to pick up and execute the kernel workflow.
pull/26/head
nirenjan 2020-08-13 03:17:35 -07:00
parent d3c55da89d
commit 4388eceec0
11 changed files with 39 additions and 1459 deletions

View File

@ -0,0 +1,4 @@
#!/bin/bash
# Install dependencies to build kernel modules on Ubuntu runners
sudo apt-get update
sudo apt-get install -y linux-headers-$(uname -r)

View File

@ -5,6 +5,8 @@ on:
branches: branches:
- '*' - '*'
- '!gh-pages' - '!gh-pages'
paths-ignore:
- 'kernel_module/**'
pull_request: pull_request:
branches: [ master ] branches: [ master ]

View File

@ -1,17 +1,14 @@
name: "CodeQL" name: "CodeQL"
on: on:
push:
branches: ['*', '!gh-pages']
pull_request: pull_request:
# The branches below must be a subset of the branches above # The branches below must be a subset of the branches above
branches: [master] branches: [master]
schedule: schedule:
- cron: '0 7 * * 1' - cron: '30 7 * * 1,3,5'
jobs: jobs:
analyse: analyse:
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
name: Analyse name: Analyse
runs-on: ubuntu-latest runs-on: ubuntu-latest

31
.github/workflows/kernel.yml vendored 100644
View File

@ -0,0 +1,31 @@
name: Kernel Module
on:
push:
branches: [ '*' ]
paths:
- 'kernel_module/**'
pull_request:
branches: [ master ]
jobs:
build:
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ['ubuntu-16.04', 'ubuntu-18.04', 'ubuntu-20.04']
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install kernel dependencies
run: ./.github/scripts/install-kernel-dependencies.sh
- name: Build kernel module
run: |
cd kernel_module
make

View File

@ -2,6 +2,7 @@ Saitek X52Pro joystick driver for Linux
======================================= =======================================
![Build/Test](https://github.com/nirenjan/x52pro-linux/workflows/Build/Test/badge.svg) ![Build/Test](https://github.com/nirenjan/x52pro-linux/workflows/Build/Test/badge.svg)
![Kernel Module](https://github.com/nirenjan/x52pro-linux/workflows/Kernel%20Module/badge.svg)
![CodeQL](https://github.com/nirenjan/x52pro-linux/workflows/CodeQL/badge.svg) ![CodeQL](https://github.com/nirenjan/x52pro-linux/workflows/CodeQL/badge.svg)
[![Join the chat at https://gitter.im/x52pro-linux/community](https://badges.gitter.im/x52pro-linux/community.svg)](https://gitter.im/x52pro-linux/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/x52pro-linux/community](https://badges.gitter.im/x52pro-linux/community.svg)](https://gitter.im/x52pro-linux/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

View File

@ -1,600 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifdef CONFIG_USB_DEBUG
#define DEBUG 1
#endif
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#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"
#define DRIVER_AUTHOR "Nirenjan Krishnan, nirenjan@gmail.com"
#define DRIVER_DESC "Saitek X52Pro HOTAS Driver"
#define DRIVER_VERSION "1.0"
static const struct x52_device {
u16 idVendor;
u16 idProduct;
char *name;
u8 type;
u8 flags;
} x52_devices[] = {
{
VENDOR_ID_SAITEK, PRODUCT_ID_X52_PRO,
"Saitek X52 Pro Flight Control System", X52TYPE_X52PRO,
X52FLAGS_SUPPORTS_MFD | X52FLAGS_SUPPORTS_LED
},
{ /* Terminating entry */
0x0000, 0x0000, "Unknown X52", X52TYPE_UNKNOWN, 0
},
};
/* list of devices that work with this driver */
static struct usb_device_id id_table[] = {
{ USB_DEVICE(VENDOR_ID_SAITEK, PRODUCT_ID_X52_PRO) },
/*
* Future versions of this driver may support the original
* X52 HOTAS joystick, but for now, only the X52 Pro is
* supported.
*/
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
#define CHECK_RETURN(ret, cnt) do { \
if (ret) {\
return ret;\
} else {\
return cnt;\
}\
} while(0)
/**********************************************************************
* MFD Line manipulation functions
*********************************************************************/
static ssize_t show_text_line(struct device *dev, char *buf, u8 line)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
line--; /* Convert to 0-based line number */
if (joy->feat_mfd) {
return sprintf(buf, "%s\n", joy->line[line].text);
} else {
sprintf(buf, "\n");
return -EOPNOTSUPP;
}
return 0;
}
static ssize_t set_text_line(struct device *dev, const char *buf,
size_t count, u8 line)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
u16 i;
u16 length;
int retval;
if (!joy->feat_mfd) {
return -EOPNOTSUPP;
}
line--; /* Convert to 0-based line number */
/* Copy the string from src to dst, upto 16 characters max */
for (i = 0, length = 0; i < X52_MFD_LINE_SIZE && i < count; i++, length++) {
joy->line[line].text[i] = buf[i];
}
joy->line[line].length = length;
/* Append empty bytes until the destination is full */
for ( ; i < X52_MFD_LINE_SIZE; i++) {
/* The X52 pro MFD uses space characters as empty characters */
joy->line[line].text[i] = 32;
}
retval = set_text(joy, line);
CHECK_RETURN(retval, count);
}
#define show_set_text(no) \
static ssize_t show_text_line##no(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_text_line(dev, buf, no); \
} \
static ssize_t set_text_line##no(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
return set_text_line(dev, buf, count, no); \
} \
static DEVICE_ATTR(mfd_line##no, S_IWUSR | S_IWGRP | S_IRUGO, show_text_line##no, set_text_line##no);
show_set_text(1);
show_set_text(2);
show_set_text(3);
/**********************************************************************
* Brightness manipulation functions
*********************************************************************/
#define mfd 0
#define led 1
static int to_hex(const char c)
{
/* Converts a single character to hex */
if (c >= '0' && c <= '9') {
return (c - '0');
} else if (c >= 'A' && c <= 'F') {
return (c - 'A' + 10);
} else if (c >= 'a' && c <= 'f') {
return (c - 'a' + 10);
}
return -1;
}
static ssize_t set_x52_brightness(struct device *dev, const char *buf,
size_t count, u8 target)
{
u16 bri;
u16 ch;
int retval;
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
if (target >= 2 || count != 4 || buf[0] != '0' ||
((buf[1] | 0x20) != 'x')) {
/* Just some sanity checking */
return -EOPNOTSUPP;
}
bri = 0;
ch = to_hex(buf[2]);
if (ch >= 0) {
bri |= ch;
bri <<= 4;
} else {
return -EOPNOTSUPP;
}
ch = to_hex(buf[3]);
if (ch >= 0) {
bri |= ch;
} else {
return -EOPNOTSUPP;
}
if (bri > 0x80) {
return -EOPNOTSUPP;
}
if (target == mfd) {
ch = X52_MFD_BRIGHTNESS;
joy->bri_mfd = bri;
} else {
ch = X52_LED_BRIGHTNESS;
joy->bri_led = bri;
}
retval = set_brightness(joy, target);
CHECK_RETURN(retval, count);
}
static ssize_t show_brightness(struct device *dev, char *buf, u8 target)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
if (target == mfd) {
if (joy->feat_mfd) {
return sprintf(buf, "0x%02x\n", joy->bri_mfd);
}
} else if (target == led) {
if (joy->feat_led) {
return sprintf(buf, "0x%02x\n", joy->bri_led);
}
}
sprintf(buf, "\n");
return -EOPNOTSUPP;
}
static ssize_t show_bri_mfd (struct device *dev, struct device_attribute *attr, char *buf)
{
return show_brightness(dev, buf, mfd);
}
static ssize_t set_bri_mfd (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return set_x52_brightness(dev, buf, count, mfd);
}
static ssize_t show_bri_led (struct device *dev, struct device_attribute *attr, char *buf)
{
return show_brightness(dev, buf, led);
}
static ssize_t set_bri_led (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return set_x52_brightness(dev, buf, count, led);
}
static DEVICE_ATTR(bri_mfd, S_IWUSR | S_IWGRP | S_IRUGO, show_bri_mfd, set_bri_mfd);
static DEVICE_ATTR(bri_led, S_IWUSR | S_IWGRP | S_IRUGO, show_bri_led, set_bri_led);
#undef mfd
#undef led
/**********************************************************************
* LED manipulation functions
*********************************************************************/
#define fire X52_LED_FIRE
#define a_red X52_LED_A_RED
#define a_green X52_LED_A_GREEN
#define b_red X52_LED_B_RED
#define b_green X52_LED_B_GREEN
#define d_red X52_LED_D_RED
#define d_green X52_LED_D_GREEN
#define e_red X52_LED_E_RED
#define e_green X52_LED_E_GREEN
#define t1_red X52_LED_T1_RED
#define t1_green X52_LED_T1_GREEN
#define t2_red X52_LED_T2_RED
#define t2_green X52_LED_T2_GREEN
#define t3_red X52_LED_T3_RED
#define t3_green X52_LED_T3_GREEN
#define pov_red X52_LED_POV_RED
#define pov_green X52_LED_POV_GREEN
#define i_red X52_LED_I_RED
#define i_green X52_LED_I_GREEN
#define throttle X52_LED_THROTTLE
static ssize_t set_x52_led(struct device *dev, const char *buf,
size_t count, u8 target)
{
u8 status;
int retval;
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
if (target > X52_LED_THROTTLE || count < 1 || buf[0] < '0' ||
buf[0] > '1') {
/* Just some sanity checking */
return -EOPNOTSUPP;
}
status = buf[0] - '0';
if (status) {
joy->led_status |= (1 << target);
} else {
joy->led_status &= ~(1 << target);
}
retval = set_led(joy, target);
CHECK_RETURN(retval, count);
}
static ssize_t show_led(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
if (joy->feat_led) {
return sprintf(buf, "0x%08x\n", joy->led_status);
}
sprintf(buf, "\n");
return -EOPNOTSUPP;
}
#define show_set_led(no) \
static ssize_t set_led_##no(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
return set_x52_led(dev, buf, count, no); \
} \
static DEVICE_ATTR(led_##no, S_IWUSR | S_IWGRP | S_IRUGO, show_led, set_led_##no);
show_set_led(fire);
show_set_led(a_red);
show_set_led(a_green);
show_set_led(b_red);
show_set_led(b_green);
show_set_led(d_red);
show_set_led(d_green);
show_set_led(e_red);
show_set_led(e_green);
show_set_led(t1_red);
show_set_led(t1_green);
show_set_led(t2_red);
show_set_led(t2_green);
show_set_led(t3_red);
show_set_led(t3_green);
show_set_led(pov_red);
show_set_led(pov_green);
show_set_led(i_red);
show_set_led(i_green);
show_set_led(throttle);
/**********************************************************************
* Blink manipulation functions
*********************************************************************/
static ssize_t show_blink(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
if (joy->feat_led) {
return sprintf(buf, "%d\n", joy->blink_led);
} else {
sprintf(buf, "\n");
return -EOPNOTSUPP;
}
return 0;
}
static ssize_t set_x52_blink(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct x52_joy *joy = usb_get_intfdata(intf);
int retval;
if (!joy->feat_led) {
return -EOPNOTSUPP;
}
if (count < 1 || buf[0] < '0' || buf[1] > '1') {
return -EINVAL;
}
joy->blink_led = buf[0] - '0';
retval = set_blink(joy);
CHECK_RETURN(retval, count);
}
static DEVICE_ATTR(led_blink, S_IWUSR | S_IWGRP | S_IRUGO, show_blink, set_x52_blink);
/**********************************************************************
* X52 driver functions
*********************************************************************/
static int x52_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
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;
for (i = 0; x52_devices[i].idVendor; i++) {
if ((le16_to_cpu(udev->descriptor.idVendor) == x52_devices[i].idVendor) &&
(le16_to_cpu(udev->descriptor.idProduct) == x52_devices[i].idProduct)) {
break;
}
}
joy = kzalloc(sizeof(*joy), GFP_KERNEL);
if (joy == NULL) {
dev_err(&intf->dev, "Out of memory: Cannot create joystick\n");
goto error;
}
joy->type = x52_devices[i].type;
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);
usb_set_intfdata(intf, joy);
if (joy->feat_mfd) {
device_create_file(&intf->dev, &dev_attr_mfd_line1);
device_create_file(&intf->dev, &dev_attr_mfd_line2);
device_create_file(&intf->dev, &dev_attr_mfd_line3);
device_create_file(&intf->dev, &dev_attr_bri_mfd);
}
if (joy->feat_led) {
device_create_file(&intf->dev, &dev_attr_bri_led);
device_create_file(&intf->dev, &dev_attr_led_fire);
device_create_file(&intf->dev, &dev_attr_led_a_red);
device_create_file(&intf->dev, &dev_attr_led_a_green);
device_create_file(&intf->dev, &dev_attr_led_b_red);
device_create_file(&intf->dev, &dev_attr_led_b_green);
device_create_file(&intf->dev, &dev_attr_led_d_red);
device_create_file(&intf->dev, &dev_attr_led_d_green);
device_create_file(&intf->dev, &dev_attr_led_e_red);
device_create_file(&intf->dev, &dev_attr_led_e_green);
device_create_file(&intf->dev, &dev_attr_led_t1_red);
device_create_file(&intf->dev, &dev_attr_led_t1_green);
device_create_file(&intf->dev, &dev_attr_led_t2_red);
device_create_file(&intf->dev, &dev_attr_led_t2_green);
device_create_file(&intf->dev, &dev_attr_led_t3_red);
device_create_file(&intf->dev, &dev_attr_led_t3_green);
device_create_file(&intf->dev, &dev_attr_led_pov_red);
device_create_file(&intf->dev, &dev_attr_led_pov_green);
device_create_file(&intf->dev, &dev_attr_led_i_red);
device_create_file(&intf->dev, &dev_attr_led_i_green);
device_create_file(&intf->dev, &dev_attr_led_throttle);
device_create_file(&intf->dev, &dev_attr_led_blink);
}
try_module_get(THIS_MODULE);
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;
}
static void x52_disconnect(struct usb_interface *intf)
{
struct x52_joy *joy;
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);
device_remove_file(&intf->dev, &dev_attr_mfd_line3);
device_remove_file(&intf->dev, &dev_attr_bri_mfd);
}
if (joy->feat_led) {
device_remove_file(&intf->dev, &dev_attr_bri_led);
device_remove_file(&intf->dev, &dev_attr_led_fire);
device_remove_file(&intf->dev, &dev_attr_led_a_red);
device_remove_file(&intf->dev, &dev_attr_led_a_green);
device_remove_file(&intf->dev, &dev_attr_led_b_red);
device_remove_file(&intf->dev, &dev_attr_led_b_green);
device_remove_file(&intf->dev, &dev_attr_led_d_red);
device_remove_file(&intf->dev, &dev_attr_led_d_green);
device_remove_file(&intf->dev, &dev_attr_led_e_red);
device_remove_file(&intf->dev, &dev_attr_led_e_green);
device_remove_file(&intf->dev, &dev_attr_led_t1_red);
device_remove_file(&intf->dev, &dev_attr_led_t1_green);
device_remove_file(&intf->dev, &dev_attr_led_t2_red);
device_remove_file(&intf->dev, &dev_attr_led_t2_green);
device_remove_file(&intf->dev, &dev_attr_led_t3_red);
device_remove_file(&intf->dev, &dev_attr_led_t3_green);
device_remove_file(&intf->dev, &dev_attr_led_pov_red);
device_remove_file(&intf->dev, &dev_attr_led_pov_green);
device_remove_file(&intf->dev, &dev_attr_led_i_red);
device_remove_file(&intf->dev, &dev_attr_led_i_green);
device_remove_file(&intf->dev, &dev_attr_led_throttle);
device_remove_file(&intf->dev, &dev_attr_led_blink);
}
usb_put_dev(joy->udev);
kfree(joy);
module_put(THIS_MODULE);
dev_info(&intf->dev, "X52 device now disconnected\n");
}
static struct usb_driver x52_driver = {
.name = "saitek_x52",
.probe = x52_probe,
.disconnect = x52_disconnect,
.id_table = id_table,
};
static int __init x52_init(void)
{
int retval = 0;
retval = usb_register(&x52_driver);
if (retval) {
pr_err("usb_register failed (errno=%d)\n", retval);
}
return retval;
}
static void __exit x52_exit(void)
{
usb_deregister(&x52_driver);
}
module_init (x52_init);
module_exit (x52_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL v2");

View File

@ -1,266 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifdef CONFIG_USB_DEBUG
#define DEBUG 1
#endif
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "x52joy_commands.h"
#include "x52joy_common.h"
/**********************************************************************
* MFD Line manipulation functions
*********************************************************************/
static const u8 line_cmd[X52_MFD_LINES] = {
X52_MFD_LINE1,
X52_MFD_LINE2,
X52_MFD_LINE3,
};
int set_text(struct x52_joy *joy, u8 line_no)
{
u16 i;
u16 ch;
u16 length;
char *text_ptr;
int retval;
if (!joy || line_no >= X52_MFD_LINES) {
/* Just some sanity checking */
return -EINVAL;
}
/* Clear the line first */
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x00,
line_cmd[line_no] | X52_MFD_CLEAR_LINE,
NULL, 0, 1000);
if (retval) {
return retval;
}
length = joy->line[line_no].length;
for (i = 0; i < X52_MFD_LINE_SIZE && i < length; i+= 2) {
text_ptr = &joy->line[line_no].text[i];
if (length - i > 1) {
ch = *(u16 *)text_ptr;
} else {
ch = (*text_ptr) + (0x20 << 8);
}
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
ch,
line_cmd[line_no] | X52_MFD_WRITE_LINE,
NULL, 0, 1000);
if (retval) {
return retval;
}
}
return 0;
}
/**********************************************************************
* Brightness manipulation functions
*********************************************************************/
int set_brightness(struct x52_joy *joy, u8 target)
{
u16 bri;
u8 ch;
int retval;
if (!joy || target > 1) {
/* Just some sanity checking */
return -EINVAL;
}
if (target == 0) { // MFD
ch = X52_MFD_BRIGHTNESS;
bri = joy->bri_mfd;
} else { // LED
ch = X52_LED_BRIGHTNESS;
bri = joy->bri_led;
}
if (bri > 0x80) {
return -EOPNOTSUPP;
}
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
bri, ch,
NULL, 0, 1000);
return retval;
}
/**********************************************************************
* LED manipulation functions
*********************************************************************/
int set_led(struct x52_joy *joy, u8 target)
{
int retval;
u8 status;
if (!joy || target > X52_LED_THROTTLE) {
/* Just some sanity checking */
return -EINVAL;
}
status = (u8)((joy->led_status >> target) & 1);
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
target << 8 | status, X52_LED,
NULL, 0, 1000);
return retval;
}
/**********************************************************************
* Date manipulation functions
*********************************************************************/
int set_date(struct x52_joy *joy)
{
int retval;
u16 ddmm;
u16 yy;
if (!joy) {
/* Just some sanity checking */
return -EINVAL;
}
/*
* The X52 Pro MFD expects the date to be stored as DD-MM-YY.
* However, by tweaking the USB control messages, we can display
* in any format we choose.
*/
if (joy->date.format >= x52_mfd_format_max) {
return -EOPNOTSUPP;
}
switch (joy->date.format) {
case x52_mfd_format_yymmdd:
yy = joy->date.day;
ddmm = (joy->date.year << 8) | joy->date.month;
break;
case x52_mfd_format_mmddyy:
yy = joy->date.year;
ddmm = (joy->date.day << 8) | joy->date.month;
break;
case x52_mfd_format_ddmmyy:
yy = joy->date.year;
ddmm = (joy->date.month << 8) | joy->date.day;
break;
default:
return -EOPNOTSUPP;
}
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
ddmm, X52_DATE_DDMM,
NULL, 0, 1000);
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
yy, X52_DATE_YEAR,
NULL, 0, 1000);
return retval;
}
/**********************************************************************
* Shift manipulation functions
*********************************************************************/
int set_shift(struct x52_joy *joy)
{
int retval;
int val;
if (!joy) {
/* Just some sanity checking */
return -EINVAL;
}
if (!joy->feat_mfd) {
return -EOPNOTSUPP;
}
if (joy->shift_ind) {
val = X52_SHIFT_ON;
} else {
val = X52_SHIFT_OFF;
}
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
val, X52_SHIFT_INDICATOR,
NULL, 0, 1000);
return retval;
}
/**********************************************************************
* Blink manipulation functions
*********************************************************************/
int set_blink(struct x52_joy *joy)
{
int retval;
int val;
if (!joy) {
/* Just some sanity checking */
return -EINVAL;
}
if (!joy->feat_led) {
return -EOPNOTSUPP;
}
if (joy->blink_led) {
val = X52_BLINK_ON;
} else {
val = X52_BLINK_OFF;
}
retval = usb_control_msg(joy->udev,
usb_sndctrlpipe(joy->udev, 0),
X52_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
val, X52_BLINK_INDICATOR,
NULL, 0, 1000);
return retval;
}

View File

@ -1,76 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef X52JOY_COMMANDS_H
#define X52JOY_COMMANDS_H
/* X52 vendor API commands */
/* Vendor request - all commands must have this request ID */
#define X52_VENDOR_REQUEST 0x91
/* MFD Text commands */
#define X52_MFD_CLEAR_LINE 0x08
#define X52_MFD_WRITE_LINE 0x00
#define X52_MFD_LINE1 0xd1
#define X52_MFD_LINE2 0xd2
#define X52_MFD_LINE3 0xd4
/* Brightness commands */
#define X52_MFD_BRIGHTNESS 0xb1
#define X52_LED_BRIGHTNESS 0xb2
/* LED set commands */
#define X52_LED 0xb8
/* Time commands */
#define X52_TIME_CLOCK1 0xc0
#define X52_OFFS_CLOCK2 0xc1
#define X52_OFFS_CLOCK3 0xc2
/* Date commands */
#define X52_DATE_DDMM 0xc4
#define X52_DATE_YEAR 0xc8
/* Shift indicator on MFD */
#define X52_SHIFT_INDICATOR 0xfd
#define X52_SHIFT_ON 0x51
#define X52_SHIFT_OFF 0x50
/* Blink throttle & POV LED */
#define X52_BLINK_INDICATOR 0xb4
#define X52_BLINK_ON 0x51
#define X52_BLINK_OFF 0x50
/* LED indices */
#define X52_LED_FIRE 0x01
#define X52_LED_A_RED 0x02
#define X52_LED_A_GREEN 0x03
#define X52_LED_B_RED 0x04
#define X52_LED_B_GREEN 0x05
#define X52_LED_D_RED 0x06
#define X52_LED_D_GREEN 0x07
#define X52_LED_E_RED 0x08
#define X52_LED_E_GREEN 0x09
#define X52_LED_T1_RED 0x0a
#define X52_LED_T1_GREEN 0x0b
#define X52_LED_T2_RED 0x0c
#define X52_LED_T2_GREEN 0x0d
#define X52_LED_T3_RED 0x0e
#define X52_LED_T3_GREEN 0x0f
#define X52_LED_POV_RED 0x10
#define X52_LED_POV_GREEN 0x11
#define X52_LED_I_RED 0x12
#define X52_LED_I_GREEN 0x13
#define X52_LED_THROTTLE 0x14
#endif /* !defined X52JOY_COMMANDS_H */

View File

@ -1,80 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef X52JOY_COMMON_H
#define X52JOY_COMMON_H
#include <linux/usb.h>
#include <linux/input.h>
#include "x52joy_map.h"
struct x52_joy {
struct usb_device *udev;
struct urb *irq_in;
unsigned char *idata;
dma_addr_t idata_dma;
struct input_dev *idev;
char phys[64];
u32 led_status;
struct x52_mfd_line line[X52_MFD_LINES];
struct x52_mfd_date date;
struct x52_mfd_time time;
struct x52_mfd_offs time_offs2;
struct x52_mfd_offs time_offs3;
u8 type;
u8 bri_mfd;
u8 bri_led;
u8 shift_ind:1;
u8 blink_led:1;
u8 clutch_mode:1;
u8 shift_state_enabled:1;
u8 clock_enabled:1;
u8 clutch_enabled:1;
u8 clutch_latched:1;
u8 :1;
u8 feat_mfd:1;
u8 feat_led:1;
u8 debug:1;
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);
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 */

View File

@ -1,243 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#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 = data[3];
stick_axis = (stick_axis << 8) | data[2];
stick_axis = (stick_axis << 8) | data[1];
stick_axis = (stick_axis << 8) | 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 */
dev_dbg(joy->idev->dev.parent,
"%s - URB shutting down with status: %d",
__func__, status);
return;
default:
dev_dbg(joy->idev->dev.parent,
"%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)
dev_err(joy->idev->dev.parent,
"%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, 0, 0);
input_set_abs_params(idev, ABS_Y, 0, 1023, 0, 0);
input_set_abs_params(idev, ABS_RZ, 0, 1023, 0, 0);
input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_Z, 0, 255, 0, 0);
/* Mouse stick */
input_set_abs_params(idev, ABS_TILT_X, 0, 15, 0, 0);
input_set_abs_params(idev, ABS_TILT_Y, 0, 15, 0, 0);
/* 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);
}

View File

@ -1,190 +0,0 @@
/*
* 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.
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#ifndef X52JOY_MAP_H
#define X52JOY_MAP_H
#include <linux/usb.h>
/**********************************************************************
* X52 enumerations
*********************************************************************/
enum x52_button {
x52_button_trigger1, /* Trigger primary */
x52_button_launch, /* Launch/Fire */
x52_button_a,
x52_button_b,
x52_button_c,
x52_button_pinkie, /* Pinkie shift switch */
x52_button_d, /* D and E buttons are */
x52_button_e, /* on the throttle */
x52_button_t1_up, /* Toggle 1 up/down */
x52_button_t1_dn,
x52_button_t2_up, /* Toggle 2 up/down */
x52_button_t2_dn,
x52_button_t3_up, /* Toggle 3 up/down */
x52_button_t3_dn,
x52_button_trigger2, /* Trigger secondary */
x52_button_mouse_left,
x52_button_mouse_scroll_dn, // Down & up are from the POV of
x52_button_mouse_scroll_up, // the user
x52_button_mouse_right,
x52_button_stick_pov_N, // These two POVs should really
x52_button_stick_pov_E, // be represented by hats, but
x52_button_stick_pov_S, // the joystick reports them as
x52_button_stick_pov_W, // button presses.
x52_button_throttle_pov_N, // For the throttle, N, E, S, W
x52_button_throttle_pov_E, // are taken if you look at it
x52_button_throttle_pov_S, // from the position of the user
x52_button_throttle_pov_W, // NOT from the rear of the throttle
x52_button_mode_select_1, // The mode selection switch is not
x52_button_mode_select_2, // a push button but a selector -
x52_button_mode_select_3, // only one can be active at a time
x52_button_clutch,
x52_button_function,
x52_button_start_stop,
x52_button_reset,
x52_button_pg_up,
x52_button_pg_dn,
x52_button_up,
x52_button_dn,
x52_button_select,
x52_button_max
};
enum x52_axis {
x52_axis_x, /* Stick X axis */
x52_axis_y, /* Stick Y axis */
x52_axis_rz, /* Stick twist axis */
x52_axis_z, /* Throttle axis */
x52_axis_rx, /* Rotary X axis */
x52_axis_ry, /* Rotary Y axis */
x52_axis_slider, /* Slider axis */
x52_axis_mouse_x, /* Mouse stick X axis */
x52_axis_mouse_y, /* Mouse stick Y axis */
x52_axis_max
};
/**********************************************************************
* X52 joystick settings
*********************************************************************/
/* Deadzone settings - no response if value ... */
struct x52_axis_deadzone {
u16 dead_lo; /* value < dead_lo */
u16 dead_hi; /* value > dead_hi */
u16 dead_fuzz; /* dead_fuzz > 0 && */
u16 dead_flat; /* flat - fuzz < value < flat + fuzz */
};
/*
* The X52 MFD supports the following:
* - 3 lines of 16 characters each
* - Clock with HH:MM
* - Date with YYMMDD (IIRC)
*/
#define X52_MFD_LINE_SIZE 16
#define X52_MFD_LINES 3
struct x52_mfd_line {
u8 text[X52_MFD_LINE_SIZE];
u8 length;
};
enum x52_mfd_date_format {
x52_mfd_format_yymmdd, /* YY-MM-DD */
x52_mfd_format_mmddyy, /* MM-DD-YY */
x52_mfd_format_ddmmyy, /* DD-MM-YY */
x52_mfd_format_max,
};
struct x52_mfd_date {
u8 year;
u8 month;
u8 day;
u8 format; /* See format enum */
};
struct x52_mfd_time {
u8 hour;
u8 minute;
u8 h24; /* 24 hour format if 1 */
};
struct x52_mfd_offs {
u8 min_off; /* Minute offset from clock 0 */
u8 neg_off; /* Negative offset if 1 */
u8 h24; /* 24 hour format if 1 */
};
/**********************************************************************
* X52 mappings
*********************************************************************/
/*
* Modes to map
*
* Right now, the module supports only 6 modes, listed as follows:
* - Mode 1
* - Mode 2
* - Mode 3
* - Mode 1 + Pinkie
* - Mode 2 + Pinkie
* - Mode 3 + Pinkie
* The last 3 modes use the pinkie switch as a shift selector.
*/
enum x52_map_modes {
x52_map_mode1,
x52_map_mode2,
x52_map_mode3,
x52_map_mode1shift,
x52_map_mode2shift,
x52_map_mode3shift,
x52_map_max
};
/* Structure to map joystick button input to a keyboard or mouse event */
struct x52_event {
u16 type;
u16 code;
s32 value;
};
/*
* This defines the maximum number of keyboard or mouse events that can
* be programmed.
*/
#define X52_MAP_MAX_EVENTS 64
struct x52_map_event {
u8 mode_selector;
u8 flags;
u8 button_or_axis;
u8 fallback;
u16 range_low;
u16 range_high;
struct x52_event events[X52_MAP_MAX_EVENTS];
};
#endif /* !defined X52JOY_MAP_H */