Move USB control messages to their own file

With this change, the X52 USB control API is moved to it's own
file in order to better separate the code and make it cleaner.

This change also renames the kernel module to saitek_x52.
pull/3/head
Nirenjan Krishnan 2012-10-05 23:21:56 -07:00
parent 60ff5826dd
commit f1fdbe9c6a
5 changed files with 387 additions and 137 deletions

3
.gitignore vendored
View File

@ -3,6 +3,9 @@
*.o
*.mod.*
# Compiled executables
a.out
# Module files
modules.order
Module.symvers

View File

@ -1,4 +1,6 @@
obj-m := x52joy.o
obj-m := saitek_x52.o
saitek_x52-objs := x52joy.o x52joy_commands.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

View File

@ -21,6 +21,7 @@
#include <linux/usb.h>
#include "x52joy_commands.h"
#include "x52joy_common.h"
#define DRIVER_AUTHOR "Nirenjan Krishnan, nirenjan@gmail.com"
#define DRIVER_DESC "Saitek X52Pro HOTAS Driver"
@ -40,107 +41,17 @@ static struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE (usb, id_table);
/*
* The X52 MFD supports the following:
* - 3 lines of 16 characters each
* - Clock with HH:MM
* - Date with YYMMDD (IIRC)
*/
#define DRIVER_LINE_SIZE 256 /* Support upto 256 bytes in the driver */
#define X52_MFD_LINE_SIZE 16
#define X52_MFD_LINES 3
struct x52_mfd_line {
u8 text[DRIVER_LINE_SIZE];
u16 length;
u16 current_pos;
};
struct x52_joy {
struct usb_device *udev;
u32 led_status;
struct x52_mfd_line line[X52_MFD_LINES];
u8 time_hour;
u8 time_min;
u16 time_offs2;
u16 time_offs3;
u8 date_year;
u8 date_month;
u8 date_day;
u8 bri_mfd;
u8 bri_led;
u8 mode_h24:1;
u8 feat_mfd:1;
u8 feat_led:1;
u8 debug:1;
};
#define CHECK_RETURN(ret, cnt) do { \
if (ret) {\
return ret;\
} else {\
return cnt;\
}\
} while(0)
/**********************************************************************
* MFD Line manipulation functions
*********************************************************************/
static const u8 line_cmd[X52_MFD_LINES] = {
X52_MFD_LINE1,
X52_MFD_LINE2,
X52_MFD_LINE3,
};
static void set_text(struct x52_joy *joy, u8 line_no)
{
u16 i;
u16 pos;
u16 ch;
u16 length;
char *text_ptr;
int retval;
if (!joy || line_no >= X52_MFD_LINES) {
/* Just some sanity checking */
return;
}
/* 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;
}
pos = joy->line[line_no].current_pos;
length = joy->line[line_no].length;
for (i = 0; i < X52_MFD_LINE_SIZE; i+= 2) {
text_ptr = &joy->line[line_no].text[pos];
if (length - pos > 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);
pos += 2;
if (pos >= length) {
pos = 0;
break;
}
}
joy->line[line_no].current_pos = pos;
return;
}
static ssize_t show_text_line(struct device *dev, char *buf, u8 line)
{
struct usb_interface *intf = to_usb_interface(dev);
@ -164,6 +75,7 @@ static ssize_t set_text_line(struct device *dev, const char *buf,\
struct x52_joy *joy = usb_get_intfdata(intf);
u16 i;
u16 length;
int retval;
if (!joy->feat_mfd) {
return -EOPNOTSUPP;
@ -172,19 +84,20 @@ static ssize_t set_text_line(struct device *dev, const char *buf,\
line--; /* Convert to 0-based line number */
/* Copy the string from src to dst, upto 16 characters max */
for (i = 0, length = 0; i < DRIVER_LINE_SIZE - 1 && i < count; i++, length++) {
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;
joy->line[line].current_pos = 0;
/* Append empty bytes until the destination is full */
for ( ; i < DRIVER_LINE_SIZE; i++) {
for ( ; i < X52_MFD_LINE_SIZE; i++) {
/* The X52 pro MFD uses space characters as empty characters */
joy->line[line].text[i] = 32;
}
set_text(joy, line);
return count;
retval = set_text(joy, line);
CHECK_RETURN(retval, count);
}
@ -222,7 +135,7 @@ static int to_hex(const char c)
return -1;
}
static ssize_t set_brightness(struct device *dev, const char *buf,
static ssize_t set_x52_brightness(struct device *dev, const char *buf,
size_t count, u8 target)
{
u16 bri;
@ -264,13 +177,9 @@ static ssize_t set_brightness(struct device *dev, const char *buf,
joy->bri_led = bri;
}
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 count;
retval = set_brightness(joy, target);
CHECK_RETURN(retval, count);
}
static ssize_t show_brightness(struct device *dev, char *buf, u8 target)
@ -298,7 +207,7 @@ static ssize_t show_bri_mfd (struct device *dev, struct device_attribute *attr,
}
static ssize_t set_bri_mfd (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return set_brightness(dev, buf, count, mfd);
return set_x52_brightness(dev, buf, count, mfd);
}
static ssize_t show_bri_led (struct device *dev, struct device_attribute *attr, char *buf)
{
@ -306,7 +215,7 @@ static ssize_t show_bri_led (struct device *dev, struct device_attribute *attr,
}
static ssize_t set_bri_led (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
return set_brightness(dev, buf, count, led);
return set_x52_brightness(dev, buf, count, led);
}
static DEVICE_ATTR(bri_mfd, S_IWUGO | S_IRUGO, show_bri_mfd, set_bri_mfd);
static DEVICE_ATTR(bri_led, S_IWUGO | S_IRUGO, show_bri_led, set_bri_led);
@ -339,22 +248,7 @@ static DEVICE_ATTR(bri_led, S_IWUGO | S_IRUGO, show_bri_led, set_bri_led);
#define i_green X52_LED_I_GREEN
#define throttle X52_LED_THROTTLE
/*
static int set_x52_led(struct x52_joy *joy, u8 target, u8 status)
{
int retval;
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;
}
*/
static ssize_t set_led(struct device *dev, const char *buf,
static ssize_t set_x52_led(struct device *dev, const char *buf,
size_t count, u8 target)
{
u8 status;
@ -370,13 +264,15 @@ static ssize_t set_led(struct device *dev, const char *buf,
status = buf[0] - '0';
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 count;
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,
@ -396,7 +292,7 @@ static ssize_t show_led(struct device *dev, struct device_attribute *attr,
#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_led(dev, buf, count, no); \
return set_x52_led(dev, buf, count, no); \
} \
static DEVICE_ATTR(led_##no, S_IWUGO | S_IRUGO, show_led, set_led_##no);

View File

@ -0,0 +1,265 @@
/*
* 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/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

@ -0,0 +1,84 @@
/*
* 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.
*
*/
#ifndef X52JOY_COMMON_H
#define X52JOY_COMMON_H
#include <linux/usb.h>
/*
* 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 */
};
struct x52_joy {
struct usb_device *udev;
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 bri_mfd;
u8 bri_led;
u8 shift_ind:1;
u8 blink_led:1;
u8 :6;
u8 feat_mfd:1;
u8 feat_led:1;
u8 debug:1;
u8 :5;
};
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);
#endif /* !defined X52JOY_COMMON_H */