Switched Chatpad to async, still needs some polish

This commit is contained in:
Ingo Ruhnke 2011-05-05 13:43:18 +02:00
parent a123f6c7b3
commit 1a05c597b0
3 changed files with 330 additions and 66 deletions

View file

@ -23,21 +23,32 @@
#include "raise_exception.hpp"
#include "usb_helper.hpp"
struct USBControlMsg
{
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
unsigned char *data;
uint16_t wLength;
};
Chatpad::Chatpad(libusb_device_handle* handle, uint16_t bcdDevice,
bool no_init, bool debug) :
m_init_state(kStateInit1),
m_handle(handle),
m_bcdDevice(bcdDevice),
m_no_init(no_init),
m_debug(debug),
m_quit_thread(false),
//m_read_thread(),
//m_keep_alive_thread(),
m_uinput(),
m_led_state(0)
m_led_state(0),
m_read_transfer(0)
{
if (m_bcdDevice != 0x0110 && m_bcdDevice != 0x0114)
{
throw std::runtime_error("unknown bcdDevice version number, please report this issue to grumbel@gmail.com and include the output of 'lsusb -v'");
throw std::runtime_error("unknown bcdDevice version number, please report this issue "
"to <grumbel@gmail.com> and include the output of 'lsusb -v'");
}
memset(m_keymap, 0, 256);
@ -93,18 +104,23 @@ Chatpad::Chatpad(libusb_device_handle* handle, uint16_t bcdDevice,
m_keymap[CHATPAD_MOD_PEOPLE] = KEY_LEFTMETA;
init_uinput();
if (!no_init)
{
send_command();
}
usb_submit_read(6, 5);
}
Chatpad::~Chatpad()
{
m_quit_thread = true;
//m_read_thread->join();
//m_keep_alive_thread->join();
}
void
Chatpad::init_uinput()
{
log_trace();
struct input_id usbid;
usbid.bustype = 0;
@ -124,23 +140,238 @@ Chatpad::init_uinput()
m_uinput->finish();
}
bool
Chatpad::get_led(unsigned int led)
void
Chatpad::usb_submit_read(int endpoint, int len)
{
return m_led_state & led;
log_trace();
assert(!m_read_transfer);
m_read_transfer = libusb_alloc_transfer(0);
uint8_t* data = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * len));
m_read_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
libusb_fill_interrupt_transfer(m_read_transfer, m_handle,
endpoint | LIBUSB_ENDPOINT_IN,
data, len,
&Chatpad::on_read_data_wrap, this,
0); // timeout
int ret;
ret = libusb_submit_transfer(m_read_transfer);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_submit_transfer(): " << usb_strerror(ret));
}
}
void
Chatpad::on_read_data(libusb_transfer* transfer)
{
log_trace();
assert(transfer);
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
{
log_error("usb transfer failed: " << usb_transfer_strerror(transfer->status));
}
else
{
log_error("chatpad data: " << raw2str(transfer->buffer, transfer->actual_length));
if (transfer->actual_length == 5 && transfer->buffer[0] == 0x00)
{
struct ChatpadKeyMsg msg;
memcpy(&msg, transfer->buffer, transfer->actual_length);
process(msg);
}
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
{
log_error("failed to resubmit USB transfer: " << usb_strerror(ret));
libusb_free_transfer(transfer);
}
}
}
void
Chatpad::send_timeout(int msec)
{
log_trace();
// FIMXE: must keep track of sources and destroy them in ~Chatpad()
//assert(m_timeout_id == -1);
//m_timeout_id =
g_timeout_add(1000, &Chatpad::on_timeout_wrap, this);
}
void
Chatpad::send_command()
{
log_trace();
log_error("send_command: " << m_init_state);
uint8_t code[2] = { 0x01, 0x02 };
switch(m_init_state)
{
case kStateInit1:
send_ctrl(0x40, 0xa9, 0xa30c, 0x4423, NULL, 0,
&Chatpad::on_control_wrap, this);
break;
case kStateInit2:
send_ctrl(0x40, 0xa9, 0x2344, 0x7f03, NULL, 0,
&Chatpad::on_control_wrap, this);
break;
case kStateInit3:
send_ctrl(0x40, 0xa9, 0x5839, 0x6832, NULL, 0,
&Chatpad::on_control_wrap, this);
break;
case kStateInit4:
send_ctrl(0xc0, 0xa1, 0x0000, 0xe416, code, 2,
&Chatpad::on_control_wrap, this);
break;
case kStateInit5:
send_ctrl(0x40, 0xa1, 0x0000, 0xe416, code, 2,
&Chatpad::on_control_wrap, this);
break;
case kStateInit6:
send_ctrl(0xc0, 0xa1, 0x0000, 0xe416, code, 2,
&Chatpad::on_control_wrap, this);
break;
case kStateInit_1e:
send_timeout(1000);
break;
case kStateInit_1f:
send_timeout(1000);
break;
case kStateInit_1b:
send_ctrl(0x41, 0x0, 0x1b, 0x02, NULL, 0,
&Chatpad::on_control_wrap, this);
break;
case kStateKeepAlive_1e:
send_timeout(1000);
break;
case kStateKeepAlive_1f:
send_timeout(1000);
break;
default:
assert(!"unknown state");
break;
}
}
void
Chatpad::on_control(libusb_transfer* transfer)
{
log_trace();
switch(m_init_state)
{
case kStateInit1:
case kStateInit2:
case kStateInit3:
// fail ok
m_init_state = static_cast<State>(m_init_state + 1);
send_command();
break;
case kStateInit4:
case kStateInit5:
case kStateInit6:
case kStateInit_1e:
case kStateInit_1f:
case kStateInit_1b:
case kStateKeepAlive_1e:
case kStateKeepAlive_1f:
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
{
log_error("stuff went wrong");
}
else
{
m_init_state = static_cast<State>(m_init_state + 1);
if (m_init_state == kStateLoop)
{
m_init_state = kStateKeepAlive_1e;
}
send_command();
}
break;
default:
assert(!"unknown state");
break;
}
}
bool
Chatpad::on_timeout()
{
log_trace();
//m_timeout_id = -1;
switch(m_init_state)
{
case kStateInit_1e:
case kStateKeepAlive_1e:
send_ctrl(0x41, 0x0, 0x1f, 0x02, NULL, 0,
&Chatpad::on_control_wrap, this);
return false;
case kStateInit_1f:
case kStateKeepAlive_1f:
send_ctrl(0x41, 0x0, 0x1e, 0x02, NULL, 0,
&Chatpad::on_control_wrap, this);
return false;
default:
assert(!"invalid state");
break;
}
}
void
Chatpad::send_ctrl(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index,
uint8_t* data, uint16_t length)
uint8_t* data_in, uint16_t length,
libusb_transfer_cb_fn callback, void* userdata)
{
int ret = libusb_control_transfer(m_handle, request_type, request, value, index, data, length, 0);
log_trace();
libusb_transfer* transfer = libusb_alloc_transfer(0);
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
// create and fill control buffer
uint8_t* data = static_cast<uint8_t*>(malloc(length + 8));
libusb_fill_control_setup(data, request_type, request, value, index, length);
memcpy(data + 8, data_in, length);
libusb_fill_control_transfer(transfer, m_handle, data,
callback, userdata,
0);
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_control_transfer() failed: " << usb_strerror(ret));
raise_exception(std::runtime_error, "libusb_submit_transfer(): " << usb_strerror(ret));
}
}
bool
Chatpad::get_led(unsigned int led)
{
return m_led_state & led;
}
void
Chatpad::set_led(unsigned int led, bool state)
{
@ -193,50 +424,6 @@ Chatpad::set_led(unsigned int led, bool state)
}
}
void
Chatpad::start_threads()
{
//m_read_thread.reset(new boost::thread(boost::bind(&Chatpad::read_thread, this)));
//m_keep_alive_thread.reset(new boost::thread(boost::bind(&Chatpad::keep_alive_thread, this)));
}
void
Chatpad::read_thread()
{
try
{
uint8_t data[5];
while(!m_quit_thread)
{
int len = 0;
int ret = libusb_interrupt_transfer(m_handle, LIBUSB_ENDPOINT_IN | 6,
data, sizeof(data), &len, 0);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
}
else
{
if (g_logger.get_log_level() > Logger::kDebug)
{
log_debug("read: " << len << "/5: data: " << raw2str(data, len));
}
if (data[0] == 0x00)
{
struct ChatpadKeyMsg msg;
memcpy(&msg, data, sizeof(msg));
process(msg);
}
}
}
}
catch(const std::exception& err)
{
log_error(err.what());
}
}
void
Chatpad::process(const ChatpadKeyMsg& msg)
{
@ -286,6 +473,45 @@ Chatpad::process(const ChatpadKeyMsg& msg)
m_uinput->sync();
}
#if 0
void
Chatpad::read_thread()
{
try
{
uint8_t data[5];
while(!m_quit_thread)
{
int len = 0;
int ret = libusb_interrupt_transfer(m_handle, LIBUSB_ENDPOINT_IN | 6,
data, sizeof(data), &len, 0);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
}
else
{
if (g_logger.get_log_level() > Logger::kDebug)
{
log_debug("read: " << len << "/5: data: " << raw2str(data, len));
}
if (data[0] == 0x00)
{
struct ChatpadKeyMsg msg;
memcpy(&msg, data, sizeof(msg));
process(msg);
}
}
}
}
catch(const std::exception& err)
{
log_error(err.what());
}
}
void
Chatpad::keep_alive_thread()
{
@ -381,5 +607,7 @@ Chatpad::send_init()
libusb_control_transfer(m_handle, 0x41, 0x0, 0x1b, 0x02, 0, 0, 0);
log_debug("0x1b");
}
#endif
/* EOF */

View file

@ -20,6 +20,7 @@
#define HEADER_XBOXDRV_CHATPAD_HPP
#include <libusb.h>
#include <glib.h>
#include <memory>
class LinuxUinput;
@ -94,6 +95,23 @@ enum {
class Chatpad
{
private:
enum State {
kStateInit1,
kStateInit2,
kStateInit3,
kStateInit4,
kStateInit5,
kStateInit6,
kStateInit_1e,
kStateInit_1f,
kStateInit_1b,
kStateKeepAlive_1e,
kStateKeepAlive_1f,
kStateLoop
};
State m_init_state;
struct ChatpadMsg
{
uint8_t type;
@ -138,6 +156,7 @@ private:
int m_keymap[256];
bool m_state[256];
unsigned int m_led_state;
libusb_transfer* m_read_transfer;
public:
Chatpad(libusb_device_handle* handle, uint16_t bcdDevice,
@ -145,7 +164,6 @@ public:
~Chatpad();
void send_init();
void start_threads();
void set_led(unsigned int led, bool state);
bool get_led(unsigned int led);
@ -154,10 +172,31 @@ public:
void init_uinput();
private:
void read_thread();
void keep_alive_thread();
void send_command();
void send_timeout(int msec);
void send_ctrl(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index,
uint8_t* data, uint16_t length);
uint8_t* data_in = NULL, uint16_t length = 0,
libusb_transfer_cb_fn callback = NULL, void* userdata = NULL);
void usb_submit_read(int endpoint, int len);
private:
bool on_timeout();
static gboolean on_timeout_wrap(gpointer data) {
return static_cast<Chatpad*>(data)->on_timeout();
}
void on_control(libusb_transfer* transfer);
static void on_control_wrap(libusb_transfer* transfer)
{
static_cast<Chatpad*>(transfer->user_data)->on_control(transfer);
}
void on_read_data(libusb_transfer* transfer);
static void on_read_data_wrap(libusb_transfer* transfer)
{
static_cast<Chatpad*>(transfer->user_data)->on_read_data(transfer);
}
private:
Chatpad(const Chatpad&);

View file

@ -49,7 +49,6 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev,
usb_claim_interface(0, try_detach);
usb_submit_read(endpoint_in, 32);
#ifdef FIXME
// create chatpad
if (chatpad)
{
@ -58,15 +57,13 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev,
int ret = libusb_get_device_descriptor(dev, &desc);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_get_config_descriptor() failed: " << usb_strerror(ret)); }
raise_exception(std::runtime_error, "libusb_get_config_descriptor() failed: " << usb_strerror(ret));
}
else
{
m_chatpad.reset(new Chatpad(m_handle, desc.bcdDevice, chatpad_no_init, chatpad_debug));
m_chatpad->send_init();
m_chatpad->start_threads();
}
}
#endif
// create headset
if (headset)