Wednesday, September 25, 2013

The Return of Teensy - Software

The USB stack on the teensy is great if you are emulating one of the devices that are already set up: USB Serial, keyboard, mouse, joystick, etc. If you want to be able to emulate a totally random device a few changes are needed.

I may miss some details here, but these are the broad strokes.

The bulk of the low level USB stuff happens in two files: usb_dev.c (the actual interrupt loop and functions to handle and send requests) and usb_desc.c (the data structures and device descriptors).

A lot of the information the USB code uses is done through compiler defines. This obviously won't work if you don't even know what device will be plugged in at compile time.

I added a "USB_RAW" type, to define some of these basic values (very few of these will actually be used).

#if defined(USB_RAW)
  #define NUM_ENDPOINTS 6
  #define NUM_USB_BUFFERS 30
  #define VENDOR_ID 0x0000
  #define PRODUCT_ID 0x0000
  #define CONFIG_DESC_SIZE 9
  #define MANUFACTURER_NAME    {}
  #define MANUFACTURER_NAME_LEN    0
  #define PRODUCT_NAME        {}
  #define PRODUCT_NAME_LEN    0
  #define NUM_INTERFACE 0
  #define DEVICE_CLASS 0
  #define DEVICE_SUBCLASS 0
  #define DEVICE_PROTOCOL 0
#endif

EP0_SIZE (the maximum packet size on the control endpoint) was also set via a define, and needed to be changed to a variable, and then endpoint_config_table and list of descriptors (from usb_desc.h) needed to be changed from constants to something I could actually change, since they are populated at run time.

The host shield code basically pulls all of the descriptors from the device and uses them to populate the usb_descriptor_list structure (usb_dev.c searches this for a matching descriptor when a request comes in).  After that is complete, it sets an address, waits about 200ms for things to settle down, and then activates the USB connection on the Teensy. Whatever it's connected to starts sending requests and they are either handled directly by usb_dev.c or passed along through the host shield to the physical device.

A couple of stumbling blocks.

There seems to be some subtle difference between how the Xbox and a PC send a SET ADDRESS request to the device. The teensy uses two pair of buffers per endpoint, one to receive and one to transmit. These get toggled every time a non-setup packet is sent or received. Normally a 0 byte reply will be sent back after a setup request. For whatever reason, the Xbox causes the buffer the processor is expecting you to use, not to match up with what the program thinks it should be using. So sending this reply to the opposite buffer works for the Xbox, or putting the same reply in both buffers will work on both.

The existing USB device options on the Teensy don't have any need for control packets with a data payload being sent to the Teensy (OUT transfers, where in/out directions are relative to the USB host). It took a little while to figure out the correct initial state and proper toggling of the two flags (one for which buffer to use, and one for the USB DATA0/1 flag, which is basically just a 1 bit sequence number to help avoid dropped packets).

But now I'm up against another roadblock.
Normally the security handshake process looks like this (the numbers are the USB request types):

81: this sends 29 bytes of data (always the same on a given controller) to the Xbox

82: this sends a 34 byte security challenge to the controller
(wait 200ms)
86: this is a status check and 2 bytes (01-00) are sent to the xbox indicating it is not ready with the response
(wait 100ms)
86: this is a status check and 2 bytes (02-00) are sent to the xbox indicating it IS ready with the response (you might think the xbox would keep trying if a 1 is sent back again, but it will only try 2 times, then will reset the device and start over from the beginning)
83: a 46 byte security response is sent back to the Xbox

84: this seems to be an indicator that the 1st check was passed

87: this is another security challenge 22 bytes long
86: we have the same thing here as before, 2 of these requests
83: another security response, but only 22 bytes

87: this is another security challenge 22 bytes long, but this time the controller will send back a stall response

84: this seems to be an indicator that the 1st check was passed
87: this is another security challenge 22 bytes long
86: we have the same thing here as before, 2 of these requests
83: another security response, but only 22 bytes

And we're done.

The problem here lies in the fact that the controller will respond to that 1st 87 command properly if attached to the PC (using the facedancer to communicate to the Xbox) but will not on the Teensy. I'm not sure what exactly causes it. But what seems to be happening is the 87 request will stall if it isn't preceded by the 84 request. And the Xbox will only allow the stall if it did not send the 84 request. I'll need to verify that the 84 request is actually making it through the host shield to the controller.

No comments:

Post a Comment