Logo Search packages:      
Sourcecode: xboxdrv version File versions  Download package

usbdebug.cpp

#include <stdio.h>
#include <string.h>
#include <boost/format.hpp>
#include <signal.h>
#include <usb.h>
#include <sstream>
#include <pthread.h>
#include <stdexcept>
#include <string>
#include <vector>
#include <iostream>

class USBDevice;
class EndpointListenerThread;

void print_raw_data(std::ostream& out, uint8_t* data, int len);
bool global_interrupt = false;

struct usb_device*
find_usb_device(uint16_t idVendor, uint16_t idProduct)
{
  struct usb_bus* busses = usb_get_busses();

  for (struct usb_bus* bus = busses; bus; bus = bus->next)
    {
      for (struct usb_device* dev = bus->devices; dev; dev = dev->next) 
        {
          if (dev->descriptor.idVendor  == idVendor &&
              dev->descriptor.idProduct == idProduct)
            {
              return dev;
            }
        }
    }
  return 0;
}

00038 class USBDevice
{
private:
  static USBDevice* current_;
public:
  static USBDevice* current() { return current_; }

private:
  struct usb_device*     dev;
  struct usb_dev_handle* handle;

  std::vector<EndpointListenerThread*> threads;
public: 
  USBDevice(struct usb_device* dev_)
    : dev(dev_)
  {
    USBDevice:: current_ = this;

    handle = usb_open(dev);
    if (!handle)
      {
        throw std::runtime_error("Error opening usb device");
      }
  }

  ~USBDevice()
  {
    usb_close(handle);
  }

  void clear_halt(int ep)
  {
    if (usb_clear_halt(handle, ep) != 0)
      {
        std::cout << "Failure to reset_ep: " << ep << std::endl;
      }    
  }

  void reset()
  {
    if (usb_reset(handle) != 0)
      {
        std::cout << "Failure to reset" << std::endl;
      }
  }

  void detach_kernel_driver(int iface)
  {
    if (usb_detach_kernel_driver_np(handle, iface) < 0)
      {
        std::ostringstream str;
        str << "Couldn't detcach interface " << iface;
        throw std::runtime_error(str.str());
      }
  }

  void claim_interface(int iface)
  {
    if (usb_claim_interface(handle, iface) != 0)
      {
        std::ostringstream str;
        str << "Couldn't claim interface " << iface;
        throw std::runtime_error(str.str());
      }
  }


  void release_interface(int iface)
  {
    if (usb_release_interface(handle, iface) != 0)
      {
        std::ostringstream str;
        str << "Couldn't release interface " << iface;
        throw std::runtime_error(str.str());
      }
  }

  void set_configuration(int configuration)
  {
    if (usb_set_configuration(handle, configuration) != 0)
      {
        std::ostringstream str;
        str << "Couldn't set configuration " << configuration;
        throw std::runtime_error(str.str());
      }
  }

  void set_altinterface(int interface)
  {
    if (usb_set_altinterface(handle, interface) != 0)
      {
        std::ostringstream str;
        str << "Couldn't set alternative interface " << interface;
        throw std::runtime_error(str.str());
      }
  }

  int read(int endpoint, uint8_t* data, int len)
  {
    return usb_interrupt_read(handle, endpoint, (char*)data, len, 0);
  }

  int write(int endpoint, uint8_t* data, int len)
  {
    return usb_interrupt_write(handle, endpoint, (char*)data, len, 0);
  }

  /* uint8_t  requesttype
     uint8_t  request
     uint16_t value;
     uint16_t index;
     uint16_t length;
   */
  int ctrl_msg(int requesttype, int request, 
               int value, int index,
               uint8_t* data, int size) 
  {
    return usb_control_msg(handle, 
                           requesttype,  request, 
                           value,  index,
                           (char*)data, size, 
                           0 /* timeout */);
  }
  
  void print_info()
  {
    for(int i = 0; i < dev->descriptor.bNumConfigurations; ++i)
      {
        std::cout << "Configuration: " << i << std::endl;
        for(int j = 0; j < dev->config[i].bNumInterfaces; ++j)
          {
            std::cout << "  Interface " << j << ":" << std::endl;
            for(int k = 0; k < dev->config[i].interface[j].num_altsetting; ++k)
              {
                for(int l = 0; l < dev->config[i].interface[j].altsetting[k].bNumEndpoints; ++l)
                  {
                    std::cout << "    Endpoint: " 
                              << int(dev->config[i].interface[j].altsetting[k].endpoint[l].bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK)
                              << ((dev->config[i].interface[j].altsetting[k].endpoint[l].bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? " (IN)" : " (OUT)")
                              << std::endl;
                  }
              }
          }
      }
  }

  void launch_listener_thread(int endpoint);
};

USBDevice* USBDevice::current_ = 0;

00189 class EndpointListenerThread
{
private:
  pthread_t thread;
  int  endpoint;
  bool running;
public:
  EndpointListenerThread(int endpoint_)
    : endpoint(endpoint_),
      running(false)
  {
  }

  ~EndpointListenerThread()
  {
    
  }

  void start()
  {
    running = true;
    pthread_create(&thread, NULL, &EndpointListenerThread::callback, this);
  }

  static void* callback(void* userdata)
  {
    EndpointListenerThread* thread = static_cast<EndpointListenerThread*>(userdata);
    thread->run();
    pthread_exit(NULL);
  }

  void run()
  {
    bool this_quit = false;
    std::cout << "Reading from endpoint " << endpoint << std::endl;;
    while(!this_quit)
      {
        uint8_t data[8192];
        int ret = USBDevice::current()->read(endpoint, data, sizeof(data));
        if (ret < 0)
          {
            std::cerr << "USBError: " << ret << "\n" << usb_strerror() << std::endl;
            std::cerr << "Shutting down" << std::endl;
            this_quit = true;
          }
        else
          {
            std::cout << ">>> Ep" << endpoint << ": ";
            print_raw_data(std::cout, data, ret);
            std::cout << std::endl;
          }
      }
  }
};

void 
USBDevice::launch_listener_thread(int endpoint)
{
  try {
    EndpointListenerThread* thread = new EndpointListenerThread(endpoint);
    threads.push_back(thread);
    thread->start();
  } catch(std::exception& err) {
    std::cout << "Error: " << err.what() << std::endl;
  }
}

bool has_prefix(const std::string& lhs, const std::string rhs)
{
  if (lhs.length() < rhs.length())
    return false;
  else
    return lhs.compare(0, rhs.length(), rhs) == 0;
}

bool readline(const std::string& prompt,
              std::string& line)
{
  std::cout << prompt << std::flush;
  bool ret = !std::getline(std::cin, line).eof();
  //line = strip(line);
  return ret;
}

std::vector<std::string> 
tokenize(const std::string& str, const std::string& delimiters = " ")
{
  std::vector<std::string> tokens;  

  // Skip delimiters at beginning.
  std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
  // Find first "non-delimiter".
  std::string::size_type pos     = str.find_first_of(delimiters, lastPos);

  while (std::string::npos != pos || std::string::npos != lastPos)
    {
      // Found a token, add it to the vector.
      tokens.push_back(str.substr(lastPos, pos - lastPos));
      // Skip delimiters.  Note the "not_of"
      lastPos = str.find_first_not_of(delimiters, pos);
      // Find next "non-delimiter"
      pos = str.find_first_of(delimiters, lastPos);
    }

  return tokens;
}

void print_raw_data(std::ostream& out, uint8_t* data, int len)
{
  std::cout << "[" << len 
            << "] { ";
      
  for(int i = 0; i < len; ++i)
    {
      std::cout << boost::format("0x%02x") % int(data[i]);
      if (i != len-1)
        std::cout << ", ";
    }

  std::cout << " }";
}

void console_listen_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 2)
    {
      std::cout << "Usage: listen [ENDPOINT]..." << std::endl;
    }
  else
    {
      for(size_t i = 1; i < args.size(); ++i)
        {
          int endpoint = atoi(args[i].c_str());
          USBDevice::current()->launch_listener_thread(endpoint);
        }
    }
}

void console_claim_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 2)
    {
      std::cout << "Usage: claim [INTERFACE]..." << std::endl;
    }
  else
    {
      for(size_t i = 1; i < args.size(); ++i)
        {
          int interface = atoi(args[i].c_str());
          try {
            USBDevice::current()->claim_interface(interface);
          } catch (std::exception& err) {
            std::cout << "Error: " << err.what() << std::endl;
            goto end;
          }
          std::cout << "Interface " << interface << " successfully claimed" << std::endl;
        end:;
        }
    }
}

void console_release_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 2)
    {
      std::cout << "Usage: release [INTERFACE]..." << std::endl;
    }
  else
    {
      for(size_t i = 1; i < args.size(); ++i)
        {
          int interface = atoi(args[i].c_str());
          try {
            USBDevice::current()->release_interface(interface);
          } catch (std::exception& err) {
            std::cout << "Error: " << err.what() << std::endl;
            goto end;
          }
          std::cout << "Interface " << interface << " successfully releaseed" << std::endl;
        end:;
        }
    }
}

void console_detach_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 2)
    {
      std::cout << "Usage: detach [INTERFACE]..." << std::endl;
    }
  else
    {
      for(size_t i = 1; i < args.size(); ++i)
        {
          int interface = atoi(args[i].c_str());
          try {
            USBDevice::current()->detach_kernel_driver(interface);
          } catch (std::exception& err) {
            std::cout << "Error: " << err.what() << std::endl;
            goto end;
          }
          std::cout << "Interface " << interface << " successfully detached" << std::endl;
        end:;
        }
    }
}

void console_info_cmd(const std::vector<std::string>& args)
{
  USBDevice::current()->print_info();
}

00401 class Sequence 
{
private:
  int   start;
  int   end;
  int   idx;

public:
  Sequence(int start_, int end_) 
    : start(start_), end(end_), idx(start_)
  {}

  Sequence(int start_)
    : start(start_), end(start_), idx(start_)
  {}

  int get() {
    if (eol())
      return end;
    else
      return idx;
  }

  void next() {
    if (!eol())
      {
        if (start <= end)
          idx += 1;
        else
          idx -= 1;
      }
  }
    
  bool eol() {
    if (start <= end)
      return idx > end;
    else
      return idx < end;
  }

  void reset() {
    idx = start;
  }

  std::string to_string() const
  {
    std::ostringstream str;
    if (start == end)
      {
        str << start;
      }
    else
      {
        str << start << "-" << end;
      }
    return str.str();
  }
};

00460 class SequenceGenerator
{
private:
  std::vector<Sequence> sequences;
  std::vector<Sequence>::size_type idx;

public:
  /** spec: NUM or [NUM, NUM, NUM-NUM]*/
00468   SequenceGenerator(const std::string& spec)
  {
    std::vector<std::string> tokens = tokenize(spec, ",");

    for(std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
      {
        int start, end;
        if (sscanf(i->c_str(), "%x-%x", &start, &end) == 2)
          {
            sequences.push_back(Sequence(start, end)); 
          }
        else if (sscanf(i->c_str(), "%x", &start) == 1)
          {
            sequences.push_back(Sequence(start));      
          }
        else
          {
            throw std::runtime_error("Syntax Error in sequence, couldn't convert: " + *i);
          }
      }

    idx = 0;
  }

  bool eol() {
    return (idx == sequences.size());
  }
  
  void reset() {
    for(std::vector<Sequence>::iterator i = sequences.begin(); i != sequences.end(); ++i)
      i->reset();
    idx = 0;
  }

  int get() {
    if (eol())
      return sequences.back().get();
    else
      return sequences[idx].get();
  }

  void next() {
    if (!eol())
      {
        if (sequences[idx].eol())
          {
            ++idx;
            if (idx < sequences.size())
              sequences[idx].next();
          } 
        else 
          {
            return sequences[idx].next();
          }
      }
  }

  std::string to_string() const 
  {
    std::ostringstream str;
    std::vector<Sequence>::const_iterator i = sequences.begin();

    str << "{ ";
    while (i != sequences.end())
      {
        const Sequence& seq = *i;
        
        str << seq.to_string();

        ++i;
        if (i != sequences.end())
          str << ", ";
      }
    str << " }";
    
    return str.str();
  }
};

bool eol(std::vector<SequenceGenerator>& sequences)
{
  if (sequences.empty())
    return true;
  else
    return sequences.back().eol();
}

void next(std::vector<SequenceGenerator>& sequences, int idx)
{
  if (idx < int(sequences.size()))
    {
      if (!sequences[idx].eol())
        {
          sequences[idx].next();
          if (sequences[idx].eol())
            {
              if (idx+1 < int(sequences.size()))
                {
                  sequences[idx].reset();
                  next(sequences, idx+1);
                }
            }
        }
    }
}

void next(std::vector<SequenceGenerator>& sequences)
{
  if (!sequences.empty())
    {
      next(sequences, 0);
    }
}

void console_probe_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 3)
    {
      std::cout << "Usage: send [EP] [DATA]..." << std::endl;
    }
  else
    {
      std::vector<uint8_t> data;
      std::vector<SequenceGenerator> sequences;

      int endpoint = atoi(args[1].c_str());
      for(int i = 2; i < int(args.size()); ++i)
        {
          SequenceGenerator gen(args[i]);
          sequences.push_back(gen);
          std::cout << gen.to_string() << std::endl;
        }

      data.resize(sequences.size());
      while(!eol(sequences) && !global_interrupt)
        {
          for(int i = 0; i < int(sequences.size()); ++i)
            data[i] = sequences[i].get();
          
          std::cout << "Data Ep: " << endpoint << ": ";
          print_raw_data(std::cout, &*data.begin(), data.size());
          int ret = USBDevice::current()->write(endpoint, &*data.begin(), data.size());
          std::cout << " -> " << ret;
          std::cout << std::endl;
          
          next(sequences);
          
          //usleep(100 * 1000);
          usleep(10 * 1000);
        }
    }
}

void console_wait_cmd(const std::vector<std::string>& args)
{
  if (args.size() != 2)
    {
      std::cout << "Usage: wait [SECONDS]" << std::endl;
    }
  else
    {
      float seconds = atof(args[1].c_str());
      std::cout << "Waiting for " << seconds << " seconds... " << std::flush;
      usleep(static_cast<useconds_t>(1000 * 1000 * seconds));
      std::cout << "done" << std::endl;
    }
}

void console_send_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 3)
    {
      std::cout << "Usage: send [EP] [DATA]..." << std::endl;
    }
  else
    {
      int endpoint = atoi(args[1].c_str());
      std::vector<uint8_t> data;
      for(int i = 2; i < int(args.size()); ++i)
        {
          data.push_back(strtol(args[i].c_str(), NULL, 16));
        }

      std::cout << "Sending to endpoint " << endpoint << ": ";
      print_raw_data(std::cout, &*data.begin(), data.size());
      int ret = USBDevice::current()->write(endpoint, &*data.begin(), data.size());
      std::cout << " -> " << ret << std::endl;
    }
}

void console_ctrlreq_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 5)
    {
      std::cout << "Usage: ctrlreq REQUESTTYPE REQUEST VALUE INDEX LEN" << std::endl;
    }
  else
    { // See USB Specification (usb_20.pdf) page 248
      int requesttype = strtol(args[1].c_str(), NULL, 16);
      int request     = strtol(args[2].c_str(), NULL, 16);
      int value       = strtol(args[3].c_str(), NULL, 16);
      int index       = strtol(args[4].c_str(), NULL, 16);
      int len         = strtol(args[5].c_str(), NULL, 16);

      std::cout << "Sending to ctrl: "
                << requesttype << " "
                << request << " "
                << value << " "
                << index << " "
                << len << ": ";

      uint8_t* data = (len == 0) ? NULL : new uint8_t[len];
      memset(data, 0, len*sizeof(uint8_t));
      
      int ret = USBDevice::current()->ctrl_msg(requesttype, request,
                                               value, index,
                                               data,
                                               len);
      std::cout << " -> " << ret << " '" << strerror(-ret) << "'" << std::endl;

      if (!data)
        std::cout << "no data";
      else
        print_raw_data(std::cout, data, len);
      
      std::cout << std::endl;

      delete[] data;
    }
}

void console_setconfiguration_cmd(const std::vector<std::string>& args)
{
  if (args.size() != 2)
    {
      std::cout << "Usage: setconfiguration CFG" << std::endl;
    }
  else
    {
      USBDevice::current()->set_configuration(atoi(args[1].c_str()));
    }
}

void console_setaltinterface_cmd(const std::vector<std::string>& args)
{
  if (args.size() != 2)
    {
      std::cout << "Usage: " << args[0] << " INTERFACE" << std::endl;
    }
  else
    {
      USBDevice::current()->set_altinterface(atoi(args[1].c_str()));
    }
}

void console_reset_ep_cmd(const std::vector<std::string>& args)
{
  if (args.size() != 2)
    {
      std::cout << "Usage: clear_halt ENDPOINT" << std::endl;
    }
  else
    {
      USBDevice::current()->clear_halt(atoi(args[1].c_str()));
    }
}

void console_reset_cmd(const std::vector<std::string>& args)
{
  USBDevice::current()->reset();
}

void console_ctrl_cmd(const std::vector<std::string>& args)
{
  if (args.size() < 5)
    {
      std::cout << "Usage: ctrl REQUESTTYPE REQUEST VALUE INDEX [DATA]..." << std::endl;
    }
  else
    { // See USB Specification (usb_20.pdf) page 248
      int requesttype = strtol(args[1].c_str(), NULL, 16);
      int request     = strtol(args[2].c_str(), NULL, 16);
      int value       = strtol(args[3].c_str(), NULL, 16);
      int index       = strtol(args[4].c_str(), NULL, 16);

      std::vector<uint8_t> data;
      for(int i = 5; i < int(args.size()); ++i)
        data.push_back(strtol(args[i].c_str(), NULL, 16));

      std::cout << "Sending to ctrl: "
                << requesttype << " "
                << request << " "
                << value << " "
                << index << ": ";
      if (data.empty())
        std::cout << "no data";
      else
        print_raw_data(std::cout, &*data.begin(), data.size());
      
      int ret = USBDevice::current()->ctrl_msg(requesttype, request,
                                               value, index,
                                               data.empty() ? NULL : &*data.begin(),
                                               data.size());
      std::cout << " -> " << ret << " '" << strerror(-ret) << "'" << std::endl;
    }
}

std::string strip_comment(const std::string& line)
{
  std::string::size_type p = line.find_first_of('#');
  if (p != std::string::npos)
    {
      return line.substr(0, p);
    }
  else
    {
      return line;
    }
}

bool quit = false;

void dispatch_command(const std::vector<std::string>& args)
{
  if (0)
    {
      for(int i = 0; i < int(args.size()); ++i)
        {
          std::cout << i << ":'" << args[i] << "' ";
        }
      std::cout << std::endl;
    }

  if (args[0] == "help")
    {
      std::cout << "help:\n   Print this help\n" << std::endl;
      std::cout << "quit:\n   Exit usbdebug\n" << std::endl;
      std::cout << "claim [INTERFACE]...\n   Claim the given interfaces\n" << std::endl;
      std::cout << "release [INTERFACE]...\n   Release the given interfaces\n" << std::endl;
      std::cout << "detach [INTERFACE]...\n   Detach kernel driver from interfaces\n" << std::endl;
      std::cout << "listen [ENDPOINT]...\n   On the given endpoints\n" << std::endl;
      std::cout << "info\n   Print some info on the current device\n" << std::endl;
      std::cout << "send [ENDPOINT] [DATA]...\n   Send data to an USB Endpoint\n" << std::endl;
    }
  else if (args[0] == "listen")
    {
      console_listen_cmd(args);
    }
  else if (args[0] == "info")
    {
      console_info_cmd(args);
    }
  else if (args[0] == "claim")
    {
      console_claim_cmd(args);
    }
  else if (args[0] == "release")
    {
      console_release_cmd(args);
    }
  else if (args[0] == "probe")
    {
      console_probe_cmd(args);
    }
  else if (args[0] == "detach")
    {
      console_detach_cmd(args);
    }
  else if (args[0] == "ctrl")
    {
      console_ctrl_cmd(args);
    }
  else if (args[0] == "ctrlreq")
    {
      console_ctrlreq_cmd(args);
    }
  else if (args[0] == "wait")
    {
      console_wait_cmd(args);
    }
  else if (args[0] == "send")
    {
      console_send_cmd(args);
    }
  else if (args[0] == "reset")
    {
      console_reset_cmd(args);
    }
  else if (args[0] == "clear_halt")
    {
      console_reset_ep_cmd(args);
    }
  else if (args[0] == "setconfiguration")
    {
      console_setconfiguration_cmd(args);
    }
  else if (args[0] == "setaltinterface")
    {
      console_setaltinterface_cmd(args);
    }
  else if (args[0] == "quit")
    {
      std::cout << "Exiting" << std::endl;
      quit = true;
    }
  else
    {
      std::cout << "Unknown command '" << args[0] << "', type 'help' to list all available commands" << std::endl;
    }
}

void eval_console_cmd(const std::string& line_)
{
  std::string line = strip_comment(line_);

  std::vector<std::string> lines = tokenize(line, ";");
  for(std::vector<std::string>::iterator i = lines.begin(); i != lines.end(); ++i)
    {
      std::vector<std::string> args = tokenize(*i);
      if (!args.empty())
        {
          dispatch_command(args);
        }
    }
}

void run_console()
{
  std::cout << "Type 'help' to list all available commands" << std::endl;
  
  std::string line;

  while (!quit && readline("\033[32musb\033[0m> ", line))
    {
      std::vector<std::string> cmds = tokenize(line, ";");
      for(std::vector<std::string>::iterator i = cmds.begin(); i != cmds.end(); ++i)
        eval_console_cmd(*i);
      global_interrupt = false;
    }
  std::cout << std::endl; 
}



void signal_callback(int)
{
  if (global_interrupt)
    {
      exit(EXIT_FAILURE);
    }
  else
    {
      global_interrupt = true;
      std::cout << "INTERRUPT" << std::endl;
    }
}

int main(int argc, char** argv)
{
  if (argc != 2)
    {
      std::cout << "Usage: " << argv[0] << " [idVendor:idProduct]" << std::endl;
    }
  else
    {
      usb_init();
      usb_find_busses();
      usb_find_devices();

      uint16_t idVendor;
      uint16_t idProduct;
      if (sscanf(argv[1], "%hx:%hx", &idVendor, &idProduct) == 2)
        {
          struct usb_device* dev = find_usb_device(idVendor, idProduct);
            
          if (dev)
            {
              std::cout << boost::format("Opening device with idVendor: 0x%h04x, idProduct: 0x%h04x") % idVendor % idProduct << std::endl;
              USBDevice* usbdev = new USBDevice(dev);
              signal(SIGINT, signal_callback);
              run_console();
              delete usbdev;
            }
          else
            {
              std::cout << "Couldn't device with " << argv[1] << std::endl;
            }
        }
      else
        {
          std::cout << "Syntax error, must be idVendor:idProduct (ex: 045e:028e)" << std::endl;
        }
    }

  return 0;
}

/* EOF */

Generated by  Doxygen 1.6.0   Back to index