www.xbdev.net xbdev - software development
Friday December 14, 2018
home | about | contact | Donations

     
 

XBOX Programming

More than just a hobby...

 

XBOX - Programming the xpad controller (i.e. Gamepad)

 

This section of xbox-programming is for those who want to develop software for the xbox from the beginning, from the bits and bytes that make up our wonderful software.

Now this tutorial is ment to give you an insight into how a usb works...well the ohci-usb...and how you would code a driver..or maybe develop further apps for the xbox.  The code should compile on the xdk or openxdk..or even the non-xdk (e.g. linux, custom xbe etc).  As we'll be coding at a very low level.

 

So how does the gamepad work?... is it some special new secret way for us to connect gamepads to a console?... nop...it uses the usb protocol...with ohci......uggg...ohci?... ohci = open host control interface.  It was developed by many of the industry companys...ms was one of them.  Its basically the underlaying system of communcating with our hub...which is our usb :)...well basically.... trying to keep it simple here.

 

If you read my tutorial and discover anything new...or god help find an error - please give me an email (bkenwright@xbdev.net).  And as you'll probably read... this tutorial is for educational purposes.  Is there a problem in knowing how your xbox works!  Its your xbox...you payed good money for it.

 

Sweet donations... well I had to say it... if you think my tutorials are worthy...you may contribure to the web site... which will only result in an improved website and more hot and spicy tutorials on all sorts of code.

 

Step one....in a galaxy far far away...

Before we do anything...we need to know some basics...  And the basics, is the PCI.  The Peripheral Component Interface...for those who have forgot ;)  This tells us where all our devices memory are located.  A lot of things in the xbox are memory mapped..... graphics ouput....usb....input/output...etc are all memory mapped.  And the PCI tells us where they are...it also sets up some basic things.

 

 

 

 

Well now....thats a quick sketch of the PCI registers and there layout...assuming you know there base address.  A nice tutorial I found on google which you might want to read... PCI-Tutorial Link.  But don't worry... once..just wanted to show you where I get the ohci-usb offsets from :)

 

Now to show you whats in our PCI registers when the xbox starts up...I wrote a small piece of code...which simply dumps there values to a small txt file...so you can see things like some of the DeviceID's...and memory offsets.  You might want to look around on the net for what the various DeviceID's coinside with.

 

 

 Download Code : PCI Dump

Starting XBOX Code

PCI_HOST -> DeviceID:0x02A5 VendorID: 0x10DE
PCI_MEM -> DeviceID:0x02A6 VendorID: 0x10DE
PCI_ISA -> DeviceID:0x01B2 VendorID: 0x10DE
~_SMBUS -> DeviceID:0x01B4 VendorID: 0x10DE
PCI_USB1 -> DeviceID:0x01C2 VendorID: 0x10DE
PCI_USB2 -> DeviceID:0x01C2 VendorID: 0x10DE
PCI_AGP -> DeviceID:0x01B7 VendorID: 0x10DE
PCI_VGA -> DeviceID:0x02A0 VendorID: 0x10DE
PCI_NONE -> DeviceID:0xFFFF VendorID: 0xFFFF


USB1 - PCI Register Values
0x00 0x01C210DE
0x04 0x00B00006
0x08 0x0C0310B1
0x0C 0x00000000
0x10 0xFED00000  (This will be our vase value for usb1 in memory)
0x14 0x00000000
0x18 0x00000000
0x1C 0x00000000
0x20 0x00000000
0x24 0x00000000
0x28 0x00000000
0x2C 0x00000000
0x30 0x00000000
0x34 0x00000044
0x38 0x00000000
0x3C 0x01030101

Ending XBOX Code

 

There you go!...  What a piece of code.  Well I think it is.  It really tells us something about the xbox that we wouldn't normally know :)  I made the code a little longer than it really needed to be... but you got to see how to get the VGA DeviceID...and with a bit of work you could find its memory offsets to the graphics card registers..  But thats for another tutorial and another time.  Remember as I said before...more or less everything in the xbox is memory mapped..its just a matter of knowing where things are and how to use them.

 

One of the most important value you should remember is offset 0x10...also known as BASE0...and is 0xfed00000.  Ooooo....so whats that?  Well thats the location of our ohci-usb registers....thats where the base value is located.  Yup... and there's a doc called the ohci spec doc.  Which is in pdf form which you can download, and lists what all the registers are and what there offsets are....cool ehh?  Well it will get better..heheh.  I know we haven't achieve much yet...but we will...be patient.

 

Here's a test for you....what do you think the PCI values change to after we init our usb?..hmm...any ideas?...Well let me show you the a PCI register dump of what the PCI registers should hold after we start our usb code:

 

 PCI - Setup Register Values USB-OHCI

  USB1 - PCI Register Values

            0x00 0x01C210DE

            0x04 0x00B00006

            0x08 0x0C0310B1

            0x0C 0x00000000

            0x10 0xFED00000

            0x14 0x00000000

            0x18 0x00000000

            0x1C 0x00000000

            0x20 0x00000000

            0x24 0x00000000

            0x28 0x00000000

            0x2C 0x00000000

            0x30 0x00000000

            0x34 0x00000044

            0x38 0x00000000

            0x3C 0x01030101

 

So take a careful look....see if you can see the difference between the initilised registers and the non-initilised ones above.  Well after 5-10 minutes of lucking you'll tell me...'there the same!'..hehe... and so they are...hehe  All we needed from the PCI was the base value.  Of course the PCI does tell us things like the interrupt IRQ and the line number etc... but all we need to set up a basic Gamepad demo is the OHCI-USB base memory address...which for usb1 is 0xfed00000.

 

OHCI - The regisers?  What are they?

Well no more messing around with that PCI stuff.....but its always useful to know I'd say :)  But for us...well we know what we need to know now...so on we go with our mission...and let for force...I mean code be with us......

 

Its got to be worth giving you the link to the ohci specificaiton doc here...as it I always find it useful at times to look at it... its a big doc...160 odd pages... but still..its got an appendix which outlines each register and what it is...and what values are in them..and why the value means etc.....  Here is the link: OHCI specification

 

Here is a quick sketch of the layout of the various ohci registers in memory...remember base address will be 0xfed00000 for usb1...just incase you've forgot :)

 

 

But us being programmers...and of course us loving C....we can put the ohci registers in a structure....then set up a structure pointer and point it to the base of our bunch of registers :).  Now I chose a popular structure...based on some linux code on the web...why?... well believe me its better to stick to the same thing.... things would be so much easer if things matched :)

 

So using this principle...lets dump the values that we have a boot out to a file and see what they look like...

 

 OHCI Dump Code:

#include <stdio.h> // fopen, fclose, sprintf

 

typedef unsigned int __u32;

 

/**************************************************************************/

/*                                                                        */

/*  Here is our structure for the OHCI controller registers...which       */

/*  we'll memory map to the location that we want :)                      */

/*                                                                        */

/**************************************************************************/

 

#define MAX_ROOT_PORTS 15

 

struct ohci_regs

{

        /* control and status registers */

        __u32   revision;

        __u32   control;

        __u32   cmdstatus;

        __u32   intrstatus;

        __u32   intrenable;

        __u32   intrdisable;

        /* memory pointers */

        __u32   hcca;

        __u32   ed_periodcurrent;

        __u32   ed_controlhead;

        __u32   ed_controlcurrent;

        __u32   ed_bulkhead;

        __u32   ed_bulkcurrent;

        __u32   donehead;

        /* frame counters */

        __u32   fminterval;

            __u32   fmremaining;

            __u32   fmnumber;

        __u32   periodicstart;

        __u32   lsthresh;

        /* Root hub ports */

            struct  ohci_roothub_regs

            {

                  __u32 a;

                  __u32 b;

                  __u32 status;

                  __u32 portstatus[MAX_ROOT_PORTS];

            } roothub;

};

typedef struct ohci_regs ohci_regs_t;

 

/**************************************************************************/

/*                                                                        */

/*  Forward declaration of our function that will dump the ohci reg       */

/*  to file....its declared at the bottom of the file.                    */

/*                                                                        */

/**************************************************************************/

void dump_ohci_regs(ohci_regs_t * ohci);

 

 

/**************************************************************************/

/*                                                                        */

/*  Program Entry Point                                                   */

/*                                                                        */

/**************************************************************************/

 

void main()

{

      // Turn off interupts ...so our xbox kernel doesn't do anything

      // while where checking things out!  Its only for now..we

      // can turn the xbox interrupts back in easily using 'sti'

 

      // STI and CLI can be used to enable/disable interrupts

      __asm

      {

            cli

      }

 

      // Lets declare a pointer of our structure...then we'll do it so it

      // points to the start of our ohci-usb1 hub registers.

      ohci_regs_t * ohci = (ohci_regs_t*)0xfed00000; // ? volatile

 

      dump_ohci_regs( ohci );

 

}// End main()

 

 

/**************************************************************************/

/*                                                                        */

/*  Simple but useful - these couple of functions dump our ohci-usb       */

/*  register values to a file.... you can use functions like this to      */

/*  monitor prgress of your ohci-driver development...while you work      */

/*  towards fixing faults                                                 */

/*                                                                        */

/**************************************************************************/

 

char buf[200];

void dbg(char* str)

{

      FILE *fp = fopen("d:\\output.txt", "a+");

      fprintf(fp, "%s", str);

      fclose(fp);

}// End dbg(..)

 

 

void dump_ohci_regs(ohci_regs_t * ohci)

{

 

      dbg("\nOHCI Reg Values:\n\n");

 

      sprintf( buf, " revision 0x%08x\n",ohci->revision);                           dbg(buf);

      sprintf( buf, " control 0x%08x\n",ohci->control);                             dbg(buf);

      sprintf( buf, " cmdstatus 0x%08x\n",ohci->cmdstatus);                         dbg(buf);

      sprintf( buf, " intrstatus 0x%08x\n",ohci->intrstatus);                       dbg(buf);

      sprintf( buf, " intrenable 0x%08x\n",ohci->intrenable);                       dbg(buf);

      sprintf( buf, " intrdisable 0x%08x\n",ohci->intrdisable);                     dbg(buf);

      sprintf( buf, " ed_periodcurrent 0x%08x\n",ohci->ed_periodcurrent);           dbg(buf);

      sprintf( buf, " ed_controlhead 0x%08x\n",ohci->ed_controlhead);               dbg(buf);

      sprintf( buf, " ed_controlcurrent 0x%08x\n",ohci->ed_controlcurrent);         dbg(buf);

      sprintf( buf, " ed_bulkhead 0x%08x\n",ohci->ed_bulkhead);                     dbg(buf);

      sprintf( buf, " ed_bulkcurrent 0x%08x\n",ohci->ed_bulkcurrent);               dbg(buf);

      sprintf( buf, " donehead 0x%08x\n",ohci->donehead);                           dbg(buf);

      sprintf( buf, " fminterval 0x%08x\n",ohci->fminterval);                       dbg(buf);

      sprintf( buf, " fmremaining 0x%08x\n",ohci->fmremaining);                     dbg(buf);

      sprintf( buf, " periodicstart 0x%08x\n",ohci->periodicstart);                 dbg(buf);

      sprintf( buf, " lsthresh 0x%08x\n",ohci->lsthresh);                           dbg(buf);

      sprintf( buf, " ohci_roothub_regs.a 0x%08x\n",ohci->roothub.a);               dbg(buf);

      sprintf( buf, " ohci_roothub_regs.b 0x%08x\n",ohci->roothub.b);               dbg(buf);

      sprintf( buf, " ohci_roothub_regs.status 0x%08x\n",ohci->roothub.status);     dbg(buf);

 

      sprintf( buf, " ohci_roothub_regs.portstatus[0] 0x%08x\n",ohci->roothub.portstatus[0]);   dbg(buf);

      sprintf( buf, " ohci_roothub_regs.portstatus[1] 0x%08x\n",ohci->roothub.portstatus[1]);   dbg(buf);

      sprintf( buf, " ohci_roothub_regs.portstatus[2] 0x%08x\n",ohci->roothub.portstatus[2]);   dbg(buf);

      sprintf( buf, " ohci_roothub_regs.portstatus[3] 0x%08x\n",ohci->roothub.portstatus[3]);   dbg(buf);

 

} // End dump_ohci_regs(..)

 

Well its not much of a piece of code....but if you run it.....it will create some really useful information... It shows us what our OHCI registers have in them.  I'll show you the output file now, and explain which values you should take notice of.

 

 Output File:

 OHCI Reg Values:

            revision 0x00000010

            control 0x00000602

            cmdstatus 0x00000004

            intrstatus 0x00000004

            intrenable 0x00000040

            intrdisable 0x00000040

            ed_periodcurrent 0x00000000

            ed_controlhead 0x03f58820

            ed_controlcurrent 0x00000000

            ed_bulkhead 0x00000000

            ed_bulkcurrent 0x00000000

            donehead 0x00000000

            fminterval 0xa7782edf

            fmremaining 0x8000079b

            periodicstart 0x00002a2f

            lsthresh 0x00000620

            ohci_roothub_regs.a 0x01001204

            ohci_roothub_regs.b 0x00000000

            ohci_roothub_regs.status 0x00000000

            ohci_roothub_regs.portstatus[0] 0x00000100

            ohci_roothub_regs.portstatus[1] 0x00000100

            ohci_roothub_regs.portstatus[2] 0x00000100

            ohci_roothub_regs.portstatus[3] 0x00000100

 

Hmmmm...just looks like a load of hex values doesn't it.... or does it?  Well first things first...that first value...its got our revision in...which is revision?...yup..did I hear you...1.0...k...now notice that the 'portstatus[0]'...[1]..[2]... etc all have the same value...which if you look it up..just means there's power there...but no device is identified. 

How did I get that from all those 0's and 1's...well don't forget that its a hex value...32 bit hex value...and remember...that means we have 32 of those 1's and 0's.  If you look at 0x00000100....this tells us that bit 8 is a 1...all the other bits are 0.  You can always check that on your calculator if you want ..heheh

Get out the OHCI spec doc....the pdf one :)...and if you go along to the back of it....it lists all the operational registers...the location..and what each bit stands for.  So bit 8 is PPS...which stands for PortPowerStatus...and a 1 indicates that power is on.  Hmmm.

Another interesting thing is bit 0....which is CCS (CurrentConnectStatus)....if there's a 1 in there it means a device is connected.  But of course we've not initialised our ohci-usb registers yet...so there still in there default stage and won't react to connected devices.

 

And for a final piece of info...if you notice the cmdstatus register..its has a value which if you look up in the ohci spec doc...tells us the device is in suspended mode!.

 

Of course we haven't initialized the ohci-usb device yet...so we didn't expect much to be in there....but we'll go about setting it up so its operational...!  Get it ticking as you might say...and have it telling us something we want.

 

Wonder what the working registers look like?....or more correctly...what the correctly set registers would look like after we've gone through the stage of setting up?... hmm..well I thought it might be good to see what to look for...so here they are:

 

 OHCI Operational Register Values

 OHCI Reg Values:

 

            revision 0x00000010

            control 0x000006be

            cmdstatus 0x00000004

            intrstatus 0x00000004

            intrenable 0x80000063

            intrdisable 0x80000063

            ed_periodcurrent 0x00000000

            ed_controlhead 0x03fdf3d0

            ed_controlcurrent 0x00000000

            ed_bulkhead 0x00000000

            ed_bulkcurrent 0x00000000

            donehead 0x00000000

            fminterval 0xa7782edf

            fmremaining 0x80000d32

            periodicstart 0x00002a2f

            lsthresh 0x00000620

            ohci_roothub_regs.a 0x01001204

            ohci_roothub_regs.b 0x00000000

            ohci_roothub_regs.status 0x00000000

            ohci_roothub_regs.portstatus[0] 0x00000103

            ohci_roothub_regs.portstatus[1] 0x00000100

            ohci_roothub_regs.portstatus[2] 0x00000100

            ohci_roothub_regs.portstatus[3] 0x00000100

 

 

The biggest thing to note..is the portstatus[0] register... that 3 on the end has a big meaning.  It says that the last two bits are set to 1's.  Which are the CCS (bit 0) and PES (bit 1) values...which stand for CurrentConnectionStatus and PortEnabledStatus.  Which comes down to..our hub is connected and its enabled.

 

 

 

{ Next stage - Seting up the ohci...getting it operational!}

{ Allocating memory for the hcca memory area 256k aligned}

{ Sending some simple control msg's to get some info from the hub}

 

 

Test Code
 

Well to start things off, I've put together some basic ohci-usb code.  It still has some problems, them being timing or alignment....not sure at this time.  As its for setting the ohci-usb address, and getting the device descriptors.

 

The code is available for download - and if you get any further results, I'd be glad for some feedback...help with it.

 

DownloadCode

 

 

Bugs! & Problems!

Well I simplified the above TestCode...but we have problems...for some reason I can't get the Control EndPoint Descriptor to go into CurrentControlED register....it seems to always go to 0xff02ff00...or 0...but never the memory location that is ED!...hmmm.... Well here is the code is someone wants to take a looksy....its stripped down to the very minimum:

 

- Download Code -

_______________________________________________________________

Step two....solutions come in time

 

Many many nights and days later....a miracle!  a miracle!  Well there I was taking a break from the coding of the ohci-usb stuff, as sometimes you need to rest your mind.  And well if you'll have tried the code above, you'll see that we hit a wall.  We didn't seem to be getting anything back from our Hub :(

But then...late...and I mean very late...I was going through a ton of raw asm/binary...and I came across some useful xbox kernel api's....as show:

 

 

extern "C" __u32 __stdcall MmAllocateContiguousMemory(__u32 a);

extern "C" __u32 __stdcall MmLockUnlockBufferPages(__u32 MemoryAddress, __u32 NumberOfBytes, __u32 a);

extern "C" __u32 __stdcall MmGetPhysicalAddress(__u32  BaseAddress);

 

 

Now I was sitting there staring at the screen...and I realised...I was making a terrible assumption....very silly of me.  I was assuming that all my memory that I was using was where it was suppose to be.  I'd been working in dos earlier...and in dos it is...memory is what it is...but in protected mode on the xbox...all is not what you see.  Even if you read the xbox linux information on the net, on how the new kernel is loaded into memory, those api's are used...as you need to have the 'real' memory location.!

 

So first thing first, I did some tests...just to make sure...always make sure :)  I tried allocating some memory and using 'MmGetPhysicalAddress'  on it...but this crashed it...after examining the asm I got the idea from, I noticed that it called 'MmLockUnlockBufferPages' first...so I decided to try that with the memory first...and yup...I got a different values for the allocated pointer and returned value from 'MmGetPhysicalAddress' .

 

The following morning I modified my earlier code so that the ohci registers all used the real 'physical' memory locations this time!  And yes..."WAahhhooooo"...we had results!  We actually got part of a descriptor back!  It all seemed okay....so bit by bit I added in the other parts so that I got all the descriptors for the hub.

 

Code: DownloadWholeCode
.....

int FindDev(ohci_t * ohci, int Port)

{

      __u32 TDA;

      int Speed=0;

 

 

      s_USB_Devicedescriptor DD;

      s_USB_Devicedescriptor * pDD = &DD;

      memset(pDD, 0, sizeof(DD) );

 

      s_Transferdescriptor * TD;

 

      __u32 GetDescr[2] = { 0x01000680, 0x00080000 };

      __u32 WG  = (__u32)GetDescr;

      MmLockUnlockBufferPages( WG, 0x8, 0);

      __u32 DDA = (__u32)pDD;

      MmLockUnlockBufferPages( DDA, 0x32, 0);

 

      TD = (s_Transferdescriptor*) (((__u32*)ED)+20); // Same as saying TD = EDA+80 ;)

      TDA = EDA + 80;

 

      __u32 realTDA = MmGetPhysicalAddress( (__u32)TDA );

 

      TD[0].Format      = 0xE20050C0;     // Get DeviceDescriptor

      TD[0].Buffer      = MmGetPhysicalAddress(WG);

      TD[0].NextTD      = realTDA + 16;

      TD[0].BufferEnd = MmGetPhysicalAddress(WG+7);

      TD[1].Format      = 0xE31050C1;     // Receive first 8 bytes of DeviceDescriptor

      TD[1].Buffer      = MmGetPhysicalAddress(DDA);

      TD[1].NextTD      = realTDA + 32;

      TD[1].BufferEnd = MmGetPhysicalAddress(DDA + 7);

      TD[2].Format      = 0xE20050C2;     // Queue END

      TD[2].Buffer      = 0;

      TD[2].NextTD      = 0;

      TD[2].BufferEnd = 0;

 

      // Power on + Enable Ports

      ohci->regs->roothub.portstatus[ Port*4 ] = 0x100;

      Sleep(2);

 

      if( (ohci->regs->roothub.portstatus[ Port*4] & 1) == 0 )

            return 0; // No device

 

      if( ohci->regs->roothub.portstatus[ Port*4] & 0x200)  // lowspeed device?

            Speed = 1;

      else

            Speed = 2;

 

      if( ohci->regs->roothub.portstatus[ Port*4 ] & 0x10000 ) // Port Power changed?

      {

            ohci->regs->roothub.portstatus[ Port*4 ] = 0x10000; // Port power Ack

      }

 

      // Port Reset

      // We will try and do this 4 times

      ohci->regs->roothub.portstatus[ Port*4 ] = 0x10;

      Sleep(40);

      for(int i=0; i<4; i++)

      {

            if( (ohci->regs->roothub.portstatus[ Port*4 ] & 0x10) == 0 )

                  break;

           

            sprintf(buf, "\tport: %d, reset failed %d times\n", Port, i);

            dbg(buf);

 

            Sleep(100);

      }

 

      ohci->regs->roothub.portstatus[ Port*4 ] = 0x100000;

 

      if( (ohci->regs->roothub.portstatus[ Port*4 ] & 7) != 3 ) // Port disabled?

      {

            ohci->regs->roothub.portstatus[ Port*4 ] = 2;

      }

 

      // Configure Endpointdescriptor

      if(Speed==2)

            ED[1].Format &= 0xFFFFDFFF;

      else

            ED[1].Format |= 0x2000;

 

      // determine MPS

      ED[1].Headptr = realTDA;

      ED[1].Tailptr = realTDA + 32;

      ED[1].Format &= 0xffffff00;

 

      // set CLF

      ohci->regs->cmdstatus |= 2;         // CommandStatus

 

      ohci->regs->control = 0x90;         // set CLE

 

      __u32 tt = ohci->regs->intrstatus;  // clear all Interruptflags

      ohci->regs->intrstatus = tt;

 

      DebugFile( ohci );

 

      // wait for execution

      dbg("waiting for execution\n");

      do

      {

            ohci->regs->intrstatus = 0x4; // SOF

 

      }while(  (ohci->regs->intrstatus & 2)== 0 );

 

      Sleep(10);

 

      // Errors?

      ohci_hcca *hcca = (ohci_hcca*)ohci->hcca;                   // HCCA

      hcca->done_head &= 0xfffffffe;                              // DoneHead in HCCA

 

      if( (hcca->done_head>>28)==0 )

      {

            ED[1].Format &= 0xF800FFFF;

            ED[1].Format |= (((__u32)DD.MaxPacketSize) << 16);

            found++;

      }

      else

            return 0;

 

     

      sprintf(buf, "\nDescriptor.Length: 0x%x\n",                 DD.Length );                  dbg(buf);

      sprintf(buf, "Descriptor.DescriptorType: 0x%02X\n",   DD.DescriptorType );    dbg(buf);

      sprintf(buf, "Descriptor.USB: 0x%04X\n",              DD.USB );                     dbg(buf);

      sprintf(buf, "Descriptor.DeviceClass: 0x%04X\n",      DD.DeviceClass );       dbg(buf);

      sprintf(buf, "Descriptor.DeviceSubClass: 0x%04X\n",   DD.DeviceSubClass );    dbg(buf);

      sprintf(buf, "Descriptor.DeviceProtocol: 0x%04X\n",   DD.DeviceProtocol );    dbg(buf);

      sprintf(buf, "Descriptor.MaxPacketSize: 0x%04X\n",    DD.MaxPacketSize );           dbg(buf);

      sprintf(buf, "Descriptor.Vendor: 0x%04X\n",                 DD.Vendor );                  dbg(buf);

      sprintf(buf, "Descriptor.ProductID: 0x%04X\n",        DD.ProductID );               dbg(buf);

      sprintf(buf, "Descriptor.Manufacturer: 0x%04X\n",     DD.Manufacturer );            dbg(buf);

      sprintf(buf, "Descriptor.ProductIndex: 0x%04X\n",     DD.ProductIndex );            dbg(buf);

      sprintf(buf, "Descriptor.SerialNumber: 0x%04X\n",     DD.SerialNumber );            dbg(buf);

      sprintf(buf, "Descriptor.ConfigNumber: 0x%04X\n",     DD.ConfigNumber );            dbg(buf);

     

      return (Speed);

  

}// End FindDev(..)

....

 

The above function is the first part that enabled me to get the first details of the HUB....below is the text file of what you should get if you call that function.  Notice that we have the Descriptor Length, Type etc.  We usually call the descriptor first to get its length and that all is okay...we use a similar set of functions next to get the full descriptor and all the other descriptors...Device, Config, Interface Descriptors as you'll soon see.

 

Output File For Above Function
 

revision 0x00000010
control 0x00000690
cmdstatus 0x00000000
intrstatus 0x00000046
intrenable 0xc000006b
intrdisable 0xc000006b
ed_periodcurrent 0x00000000
ed_controlhead 0x001c4110
ed_controlcurrent 0x00000000
ed_bulkhead 0x001c4100
ed_bulkcurrent 0x00000000
donehead 0x00000000
fminterval 0x37782edf
fmremaining 0x00000829
periodicstart 0x000004af
lsthresh 0x00000628
ohci_roothub_regs.a 0x01000204
ohci_roothub_regs.b 0x00000002
ohci_roothub_regs.status 0x00000000
ohci_roothub_regs.portstatus[0] 0x00010103
ohci_roothub_regs.portstatus[1] 0x00010100
ohci_roothub_regs.portstatus[2] 0x00010100
ohci_roothub_regs.portstatus[3] 0x00010100
waiting for execution

Descriptor.Length: 0x12
Descriptor.DescriptorType: 0x01
Descriptor.USB: 0x0110
Descriptor.DeviceClass: 0x0009
Descriptor.DeviceSubClass: 0x0000
Descriptor.DeviceProtocol: 0x0000
Descriptor.MaxPacketSize: 0x0008
Descriptor.Vendor: 0x0000
Descriptor.ProductID: 0x0000
Descriptor.Manufacturer: 0x0000
Descriptor.ProductIndex: 0x0000
Descriptor.SerialNumber: 0x0000
Descriptor.ConfigNumber: 0x0000

 

Here is a dump of the output file and the source code for the Program:

 

-DownloadSourceCode-

 

Output:
revision 0x00000010
control 0x00000690
cmdstatus 0x00000000
intrstatus 0x00000046
intrenable 0xc000006b
intrdisable 0xc000006b
ed_periodcurrent 0x00000000
ed_controlhead 0x001c4110
ed_controlcurrent 0x00000000
ed_bulkhead 0x001c4100
ed_bulkcurrent 0x00000000
donehead 0x00000000
fminterval 0x37782edf
fmremaining 0x00000829
periodicstart 0x000004af
lsthresh 0x00000628
ohci_roothub_regs.a 0x01000204
ohci_roothub_regs.b 0x00000002
ohci_roothub_regs.status 0x00000000
ohci_roothub_regs.portstatus[0] 0x00010103
ohci_roothub_regs.portstatus[1] 0x00010100
ohci_roothub_regs.portstatus[2] 0x00010100
ohci_roothub_regs.portstatus[3] 0x00010100
waiting for execution

Descriptor.Length: 0x12
Descriptor.DescriptorType: 0x01
Descriptor.USB: 0x0110
Descriptor.DeviceClass: 0x0009
Descriptor.DeviceSubClass: 0x0000
Descriptor.DeviceProtocol: 0x0000
Descriptor.MaxPacketSize: 0x0008
Descriptor.Vendor: 0x0000
Descriptor.ProductID: 0x0000
Descriptor.Manufacturer: 0x0000
Descriptor.ProductIndex: 0x0000
Descriptor.SerialNumber: 0x0000
Descriptor.ConfigNumber: 0x0000

fullspeed-device found at Port 0


revision 0x00000010
control 0x00000690
cmdstatus 0x00000000
intrstatus 0x00000046
intrenable 0xc000006b
intrdisable 0xc000006b
ed_periodcurrent 0x00000000
ed_controlhead 0x001c4110
ed_controlcurrent 0x00000000
ed_bulkhead 0x001c4100
ed_bulkcurrent 0x00000000
donehead 0x00000000
fminterval 0x37782edf
fmremaining 0x00000540
periodicstart 0x000004af
lsthresh 0x00000628
ohci_roothub_regs.a 0x01000204
ohci_roothub_regs.b 0x00000002
ohci_roothub_regs.status 0x00000000
ohci_roothub_regs.portstatus[0] 0x00010103
ohci_roothub_regs.portstatus[1] 0x00010100
ohci_roothub_regs.portstatus[2] 0x00010100
ohci_roothub_regs.portstatus[3] 0x00010100
waiting for execution
good to go
waiting for execution

*Descriptor*
Descriptor.Length: 0x12
Descriptor.DescriptorType: 0x01
Descriptor.USB: 0x0110
Descriptor.DeviceClass: 0x0009
Descriptor.DeviceSubClass: 0x0000
Descriptor.DeviceProtocol: 0x0000
Descriptor.MaxPacketSize: 0x0008
Descriptor.Vendor: 0x0451
Descriptor.ProductID: 0x2046
Descriptor.Device: 0x0125
Descriptor.Manufacturer: 0x00
Descriptor.ProductIndex: 0x00
Descriptor.SerialNumber: 0x00
Descriptor.ConfigNumber: 0x01
ConfigNumber:1
waiting for execution
waiting for execution

+Configuration Descriptor+
ConfDesc.Length: 0x09
ConfDescDescriptorType: 0x02
ConfDesc.TotalLength: 0x0019
ConfDesc.NumberofInterfaces: 0x01
ConfDesc.ConfigValue: 0x01
ConfDesc.Configuration: 0x00
ConfDesc.Attributes: 0xE0
ConfDesc.MaxPower: 0x00
waiting for execution

+Configuration Descriptor+
ConfDesc.Length: 0x09
ConfDescDescriptorType: 0x02
ConfDesc.TotalLength: 0x0019
ConfDesc.NumberofInterfaces: 0x01
ConfDesc.ConfigValue: 0x01
ConfDesc.Configuration: 0x00
ConfDesc.Attributes: 0xE0
ConfDesc.MaxPower: 0x00

#Interface Descriptor#
InterDesc.Length: 0x09
InterDesc.DescriptorType: 0x04
InterDesc.Interfacenumber: 0x00
InterDesc.AlternateSetting: 0x00
InterDesc.NumberofEndpoints: 0x01
InterDesc.InterfaceClass: 0x09
InterDesc.InterfaceSubClass: 0x00
InterDesc.InterfaceProtocol: 0x00
InterDesc.InterfaceIndex: 0x00

#EndPointDescriptor Descriptor#
EndPointDes.Length: 0x07
EndPointDes.DescriptorType: 0x05
EndPointDes.EndpointAddress: 0x81
EndPointDes.Attributes: 0x03
EndPointDes.MaxPacketSize: 0x01
EndPointDes.Interval: 0xFF

*NO* device found at Port 1

*NO* device found at Port 2

*NO* device found at Port 3

 

 

This is a big step....from here we should be able to get the gamepad descriptors and then...once we have that!  Poll for device changes..new gamepads being plugged in/unplugged...buttons being pressed etc.....which is what we hope to do next!

 

Step three....we found the gamepad?

Wahoo....Well it just shows that if you stick with it, that the evil xbox will do our bidding, and give us what we want to see!  After many hours of code hacking and crashing...reseting the xbox etc....I got these few wonderful lines of txt:

 

--gamepad_0---

 

*Descriptor*

Descriptor.Length: 0x12

Descriptor.DescriptorType: 0x01

Descriptor.USB: 0x0110

Descriptor.DeviceClass: 0x0000

Descriptor.DeviceSubClass: 0x0000

Descriptor.DeviceProtocol: 0x0000

Descriptor.MaxPacketSize: 0x0040

Descriptor.Vendor: 0x045E

Descriptor.ProductID: 0x0202

Descriptor.Device: 0x0100

Descriptor.Manufacturer: 0x00

Descriptor.ProductIndex: 0x00

Descriptor.SerialNumber: 0x00

                Descriptor.ConfigNumber: 0x01

 

What on earth is that?  Well its our gamepad descriptor....not the hub...the gamepad :)  As with a lot of effort I managed to set the gamepad usb address.  Most of the code is just bullying the hub into giving us information - so I put this code in hub.cpp/.h.

Again its a rough version of the code at this point...a lot of redundant functions and the code could deal with tidying up a bit.  But where so close!  Very very close.  We have our gamepad...its there...its so close we can feel it.... its just a matter of hopefully a small piece of code to make it do something!  Make it rumble!  Shake with fear at our power...mhahaha...it will be our slave..hehe

 

We can now detect how many usb gamepads are plugged into our xbox....I've made the assumption that 1 gamepad is plugged in, and we'll use that test gamepad as our guinipig - where we'll try and send and receive some small bulk messages.

 

DownloadCode
Now I'm not going to paste the whole code yet...not until I've tidied it up and got ride of so much redundant code...but you can take a looksy and see what I've done now...maybe you might figure out how the bulk msg can be send with a basic rumble data?

 

DownloadCode

 

 

 

Step Four: Bulk Message!  Lets see you rumble Gamepad

Its not easy!  But we now need to send a bulk message to our gamepad so that it does somethign...its okay having all this information about hubs and gamepad id's etc...but we want results!  So our next big mission is to make our gamepad vibrate...mhahahah.....  I know we can do it if we put our minds to it.

 

Its all a matter of sending a bulk msg with our ohci registers to the correct address.  We've got our device descriptors, which don't change - with this information at hand we can rull the xbox gamepad!

 

I've implemented TWO versions of the code....as the usb gamepad code will work on both the xdk and the openxdk without any changes...its that nice.  It works...fully...we can send and get information from the USB!  I've done it so it dumps the contents the the data we get back from the gamepad...the first one it finds on the screen.  So as you press buttons you can see it!...woowWWW.  Also if you press the 'A' button hard...it will start the gamepad rummbling....wahoo...coolll

 

~Code~

Download OpenXDK GamePad Code

 Download XDK GamePad Code

Includes the full OpenXDK library's as 2 of the files have been changed mm.cpp/mm.h which where uncommented so we could use the GetPhscialMemory(..) location call. Has a single file dx.cpp which uses the xdk lib's to display gamepad information on the screen so we can see that its all working.

 

Now there was of couse a few bumps on the road to success!  And I can't say how great to see a great number of linux ohci/usb examples codes on the net.  It wasnt' easy though - as my code doesn't rely on interrupts, instead it polls the ohci regitsers for data.  We could improve this now by adding interrupt feedback, but for the xbox our game loop is running at 50-60 fps, so we don't mind polling our gamepad at this stage.

 

I've stuck all that sweet input code in a folder....xinput....and all we have to do now is include the "#include "xinput/xinput.h" and where ready to go.  For the openxdk, its probably better if we build a library and just add it...e.g. xinput.lib...and a xinput.h file :)  But we can do that later!  We have a gamepad working...Wahoooo

 

Data is read from the gamepad into a structure which is defined at the top of pad.h...and it looks like this:

 

Code Snippet: pad.h
...

struct stXPAD               // Packed to 1 byte alignment.

{

      char reserved1;

      unsigned char structsize;

     

      char pad;              /* 1 up 2 down 4 left 8 right */

      char reserved2;

      unsigned char keys[6]; /* A B X Y Black White */

     

      unsigned char trig_left;

      unsigned char trig_right;

      short stick_left_x;

      short stick_left_y;

      short stick_right_x;

      short stick_right_y;

 

      char padding[0x40];

};

...

 

You don't need that padding on the end....but I put that there just in-case I added something to the stXPAD structure later on.  The structure is pretty self explanatory...but we will of course add some binary defines, so we can more easily test for inputs....but its easily done from here.

 

 

Remember now, if you run up the code...press the 'A' button to start the rumble effect so that you can really be sure of our great gamepads power!  Our next goal...is to tidy up the internal workings...so we can check for additional devices...web cam's...multiple usb devices...and most of all for connection and reconnection of usb things... ;)

 

 

If anyone gets any further with this, please feel free to give me feedback anytime: bkenwright@xbdev.net

 

 

_______________________________________________________________

 

References / Resources

PCI

Programmable Interrupt Controller Programming

XBOX Linux PCI Overview

PCI Configuration Headers/Tutorial

xbox.sparcy.net

 

XBOX USB

Iniside the XBOX Controller

 

OHCI

Step-by-step OHCI setup

OHCI Official Specification Document

 

Misc

OpenXDK Home Page

Msg Board Posting

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.