Started implementing async for Headset

This commit is contained in:
Ingo Ruhnke 2011-04-12 12:17:11 +02:00
parent 378efc2d38
commit bef8044664
4 changed files with 274 additions and 118 deletions

View file

@ -20,147 +20,86 @@
#include <fstream>
#include <errno.h>
#include <boost/bind.hpp>
#include "helper.hpp"
#include "raise_exception.hpp"
#include "usb_helper.hpp"
Headset::Headset(libusb_device_handle* handle,
bool debug,
const std::string& dump_filename,
const std::string& play_filename) :
bool debug) :
m_handle(handle),
//m_read_thread(),
//m_write_thread(),
m_quit_read_thread(false),
m_quit_write_thread(false)
m_interface(new USBInterface(m_handle, 1)),
m_fout(),
m_fin()
{
int ret = libusb_claim_interface(m_handle, 1);
if (ret != LIBUSB_SUCCESS)
{
std::ostringstream out;
out << "[headset] " << usb_strerror(ret);
throw std::runtime_error(out.str());
}
#ifdef FIXME
m_read_thread.reset(new boost::thread(boost::bind(&Headset::read_thread, this, dump_filename, debug)));
if (!play_filename.empty())
{
m_write_thread.reset(new boost::thread(boost::bind(&Headset::write_thread, this, play_filename)));
}
#endif
}
Headset::~Headset()
{
#ifdef FIXME
if (m_read_thread.get())
m_read_thread->join();
if (m_write_thread.get())
m_write_thread->join();
m_read_thread.release();
m_write_thread.release();
#endif
int ret = libusb_release_interface(m_handle, 1);
if (ret != LIBUSB_SUCCESS)
{
std::ostringstream out;
out << "[headset] " << usb_strerror(ret);
throw std::runtime_error(out.str());
}
m_interface.reset();
}
void
Headset::write_thread(const std::string& filename)
Headset::play_file(const std::string& filename)
{
std::ifstream in(filename.c_str(), std::ios::binary);
m_fin.reset(new std::ifstream(filename.c_str(), std::ios::binary));
if (!in)
if (!*m_fin)
{
std::ostringstream out;
out << "[headset] " << filename << ": " << strerror(errno);
throw std::runtime_error(out.str());
}
log_info("starting playback: " << filename);
uint8_t data[32];
while(in)
{
int len = in.read(reinterpret_cast<char*>(data), sizeof(data)).gcount();
if (len != 32)
{
// ignore short reads
}
else
{
int transferred = 0;
const int ret = libusb_interrupt_transfer(m_handle, LIBUSB_ENDPOINT_OUT | 4,
data, sizeof(data),
&transferred, 0);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_interrupt_transfer failed: " << usb_strerror(ret));
send_data();
}
}
}
log_info("finished playback: " << filename);
}
void
Headset::read_thread(const std::string& filename, bool debug)
Headset::record_file(const std::string& filename)
{
std::auto_ptr<std::ofstream> out;
m_fout.reset(new std::ofstream(filename.c_str(), std::ios::binary));
if (!filename.empty())
{
out.reset(new std::ofstream(filename.c_str(), std::ios::binary));
if (!*out)
if (!*m_fout)
{
raise_exception(std::runtime_error, filename << ": " << strerror(errno));
}
}
while(!m_quit_read_thread)
{
uint8_t data[32];
int len = 0;
const int ret = libusb_interrupt_transfer(m_handle, LIBUSB_ENDPOINT_IN | 3,
reinterpret_cast<uint8_t*>(data), sizeof(data),
&len, 0);
if (ret != LIBUSB_SUCCESS)
{
std::ostringstream outstr;
outstr << "[headset] " << usb_strerror(ret);
throw std::runtime_error(outstr.str());
}
else
{
if (len == 0)
{
log_debug("-- empty read --");
}
else
{
if (out.get())
{
out->write(reinterpret_cast<char*>(data), sizeof(data));
}
log_debug(raw2str(data, len));
}
}
//FIXME:m_interface->submit_read(3, 32);
}
}
void
Headset::send_data()
{
uint8_t data[32];
int len = m_fin->read(reinterpret_cast<char*>(data), sizeof(data)).gcount();
if (len != 32)
{
log_error("short read");
}
else
{
//FIXME: m_interface->submit_write(data, 32,
// boost::bind(&Headset::send_data, this));
}
}
void
Headset::read_data(uint8_t* data, int len)
{
if (m_fout.get())
{
m_fout->write(reinterpret_cast<char*>(data), len);
}
log_debug(raw2str(data, len));
//FIXME: m_interface->submit_read(3, 32,
// boost::bind(&Headset::send_data, this, _1, _2));
}
/* EOF */

View file

@ -22,26 +22,27 @@
#include <libusb.h>
#include <string>
#include "usb_interface.hpp"
class Headset
{
private:
libusb_device_handle* m_handle;
//std::auto_ptr<boost::thread> m_read_thread;
//std::auto_ptr<boost::thread> m_write_thread;
std::auto_ptr<USBInterface> m_interface;
bool m_quit_read_thread;
bool m_quit_write_thread;
std::auto_ptr<std::ofstream> m_fout;
std::auto_ptr<std::ifstream> m_fin;
public:
Headset(libusb_device_handle* handle,
bool debug,
const std::string& dump_filename,
const std::string& play_filename);
Headset(libusb_device_handle* handle, bool debug);
~Headset();
void play_file(const std::string& play_filename);
void record_file(const std::string& dump_filename);
private:
void write_thread(const std::string& filename);
void read_thread(const std::string& filename, bool debug);
void send_data();
void read_data(uint8_t* data, int len);
private:
Headset(const Headset&);

149
src/usb_interface.cpp Normal file
View file

@ -0,0 +1,149 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 Ingo Ruhnke <grumbel@gmx.de>
**
** 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, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "usb_interface.hpp"
#include "raise_exception.hpp"
#include "usb_helper.hpp"
USBInterface::USBInterface(libusb_device_handle* handle, int interface) :
m_handle(handle),
m_interface(interface),
m_endpoints()
{
int ret = libusb_claim_interface(handle, m_interface);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "error claiming interface: " << interface << ": " << usb_strerror(ret));
}
}
USBInterface::~USBInterface()
{
// cancel all transfer that might still be running
for(Endpoints::iterator it = m_endpoints.begin(); it != m_endpoints.end(); ++it)
{
if (it->second)
{
libusb_cancel_transfer(it->second);
libusb_free_transfer(it->second);
}
}
m_endpoints.clear();
libusb_release_interface(m_handle, m_interface);
}
void
USBInterface::submit_read(int endpoint, int len,
const boost::function<void (uint8_t, int)>& callback)
{
assert(m_endpoints.find(endpoint) == m_endpoints.end());
libusb_transfer* transfer = libusb_alloc_transfer(0);
uint8_t* data = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * len));
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
libusb_fill_interrupt_transfer(transfer, m_handle,
endpoint | LIBUSB_ENDPOINT_IN,
data, len,
&USBInterface::on_read_data_wrap, this,
0); // timeout
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
{
libusb_free_transfer(transfer);
raise_exception(std::runtime_error, "libusb_submit_transfer(): " << usb_strerror(ret));
}
else
{
// transfer is send on its way, so store it
m_endpoints[endpoint | LIBUSB_ENDPOINT_IN] = transfer;
}
}
void
USBInterface::submit_write(int endpoint, uint8_t* data_in, int len)
{
libusb_transfer* transfer = libusb_alloc_transfer(0);
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
// copy data into a newly allocated buffer
uint8_t* data = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * len));
memcpy(data, data_in, len);
libusb_fill_interrupt_transfer(transfer, m_handle,
endpoint | LIBUSB_ENDPOINT_OUT,
data, len,
&USBInterface::on_write_data_wrap, this,
0); // timeout
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
{
libusb_free_transfer(transfer);
raise_exception(std::runtime_error, "libusb_submit_transfer(): " << usb_strerror(ret));
}
else
{
m_endpoints[endpoint | LIBUSB_ENDPOINT_OUT] = transfer;
}
}
void
USBInterface::cancel_transfer(int endpoint)
{
Endpoints::iterator it = m_endpoints.find(endpoint);
if (it == m_endpoints.end())
{
raise_exception(std::runtime_error, "endpoint " << (endpoint & LIBUSB_ENDPOINT_ADDRESS_MASK) << "not found");
}
else
{
libusb_cancel_transfer(it->second);
libusb_free_transfer(it->second);
m_endpoints.erase(it);
}
}
void
USBInterface::cancel_read(int endpoint)
{
cancel_transfer(endpoint | LIBUSB_ENDPOINT_IN);
}
void
USBInterface::cancel_write(int endpoint)
{
cancel_transfer(endpoint | LIBUSB_ENDPOINT_OUT);
}
void
USBInterface::on_read_data(libusb_transfer *transfer)
{
}
void
USBInterface::on_write_data(libusb_transfer *transfer)
{
}
/* EOF */

67
src/usb_interface.hpp Normal file
View file

@ -0,0 +1,67 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 Ingo Ruhnke <grumbel@gmx.de>
**
** 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, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADER_XBOXDRV_USB_INTERFACE_HPP
#define HEADER_XBOXDRV_USB_INTERFACE_HPP
#include <libusb.h>
#include <boost/function.hpp>
#include <map>
class USBInterface
{
private:
libusb_device_handle* m_handle;
int m_interface;
typedef std::map<int, libusb_transfer*> Endpoints;
Endpoints m_endpoints;
public:
USBInterface(libusb_device_handle* handle, int interface);
~USBInterface();
void submit_read(int endpoint, int len,
const boost::function<void (uint8_t, int)>& callback);
void cancel_read(int endpoint);
void submit_write(int endpoint, uint8_t* data, int len);
void cancel_write(int endpoint);
private:
void cancel_transfer(int endpoint);
void on_read_data(libusb_transfer *transfer);
static void on_read_data_wrap(libusb_transfer *transfer)
{
static_cast<USBInterface*>(transfer->user_data)->on_read_data(transfer);
}
void on_write_data(libusb_transfer *transfer);
static void on_write_data_wrap(libusb_transfer *transfer)
{
static_cast<USBInterface*>(transfer->user_data)->on_write_data(transfer);
}
private:
USBInterface(const USBInterface&);
USBInterface& operator=(const USBInterface&);
};
#endif
/* EOF */