
                         Ë  Ë              ÉÍÍ» ÉÍÍ»
                         º  º         o       º º  º
                         ÌÍÍ¹ Ë Ë ÉÍ» Ë    ÉÍÍ¼ Ì¼É¹
                         º  º º º º º º    º    º  º
                         Ê  Ê ÈÍ¼ ÈÍ¹ Ê    ÈÍÍ¼ ÈÍÍ¼
                                    º compo
                                  ÈÍ¼

                                        _
                     /\          ______/ \
                   ///\\\       //     \_/
                  //H   \\     //                 _
                 /// Usb\\\----------------------/ \
                 \\\  G ///----------------------\_/
                  \\   I//          \\       _
                   \\\///            \\_____/ \
                     \/                     \_/
    

    Last updated: 25 June 2009 (v4.3.5)

    Greetings Hugi Coders!

    It's been awhile.

    This time your task is to retrieve the device descriptor from an attached
    USB device.  Yep, you must detect  the controller,  find its I/O base,
    and  control the  USB controller, polling  for a device  connection, then
    display the attached device's descriptor.

    For those  who know the USB  hardware, this sounds simple.  For those who
    don't  know the USB  hardware, this may  sound too  difficult, but  don't
    despair.  It really isn't.

    You can find the UHCI specs at
      http://www.frontiernet.net/~fys/incoming/uhci11d.pdf
    and the USB specs at
      http://www.usb.org

    Before you get discouraged about the complexity  of the task, don't worry
    too much.  The  example program will  contain many  comments and  explain
    what it does, and why it does it that way.

    First a glossary of terms:
     - USB    Universal Serial Bus
     - UHCI   Universal Host Controller Interface
     - LS     Low Speed
     - FS     Full Speed
     - PCI    Peripheral Component Interconnect
     - IOC    Interrupt On Completion
     - TD     Transfer Descriptor
     - GRESET Global Reset bit in the Command register
       Please see the UHCI specs above for more on these registers and
       their respective bits.
     - CRLF   Carriage Return Line Feed (ASCII characters 13 10)

    The task:
     1) Enumerate the PCI bus until you find the first UHCI controller.
        Class = 0xC, sub class = 0x3, proto = 0x0
         a) You must assume there are 2 buses, 32 devices per bus,
            and 8 functions per device.
         b) You can not assume that the revision number will be 1
     2) Find the I/O base (base 4 in the PCI configuration space).
         a) You may use the BIOS's PCI interrupt service, or
         b) You may use ports 0xCF8 and 0xCFC
            to read the PCI configuration space.
         c) write 0x0005 to the command register, without modifying
            the status register.  
     3) Reset the UHCI controller
        a) You must reset the controller by setting bit 2 (GRESET) in
           the Command register.
        b) Wait a minimum of 10ms and no more than 100ms
           There is no rule stating how you must delay.  However, it must
            be at least 10ms and no more than 100ms on *any*
            machine.  i.e: You cannot simply set ecx to a large number
            and loop until it's zero.  A slow machine will be more than 100ms
            while a fast machine may be less than 10ms.
        c) Reset (clear) bit 2
     4) Create a UHCI stack
        a) Create an empty UHCI stack frame.  i.e: you need a buffer
           no less than 1024 dwords, and set bit 0 on all dwords.
           Remember that this buffer must be 4K aligned.
        b) Point the "Frame List Base Address" register to this buffer.
     5) Start the UHCI controller.
        a) Set the IOC bit in the Interrupt Enable Register.
        b) Set bit 0 in the Command Register
     6) Reset the port, enable it, retrieve the device descriptor.
*        (Please see the note on resetting the port below)
        1) You must use an 8-byte control packet.
        2) You must only read 8 bytes at a time.
           i.e: each packet must not read more than 8 bytes
        3) You must send the status packet afterward.
 
          Packet:
            SETUP (8 bytes)
            IN    (8 bytes)
            IN    (8 bytes)
            IN    (2 or more, but not more than 8)
            OUT   (0 bytes)
 
        4) The buffer you use for the 18 or more bytes transferred,
           and the setup packet's 8 bytes, must be within your program.
           Either you allocate the space with DOS's allocation services,
           or you point to a buffer in the current memory block your program
           occupies.  See general.txt for more on how much space your
           program occupies.
           i.e: You must not assume any memory space is available outside
           your program.
        5) You must wait for the interrupt to occur.
           You don't have to have a valid ISR, you can simply wait for
            bit 0 in the STATUS register to become 1.  However, please
            note, for Bochs to work this way, you *must* set all four
            bits in the low nibble of the INTERRUPT_ENABLE register.
            Therefore, it is a rule. You must set all four bits.

        6) Stop the Controller
        
        7) Display the device descriptor bytes in the following format:

            CRLFxx xx xx xx xx xx xx xx-xx xx xx xx xx xx xx xx
            CRLFxx xx

           a) You must place a CRLF at the start of each line, not the end
           b) No other characters may be displayed.
           c) The blanks must be spaces (ASCII 20h)
           d) If a value is less than 10h, you must use a prefix of 0.
               i.e:  01 02 03...
*          e) No space should be printed after the 18th digit pair
          
        8) Do nothing else to the device.  Do not retrieve anything else.

    The order of the steps above may be changed.

    Assumptions:
     You *may* assume:
     1) There will only be one root hub. i.e: only one PCI UHCI controller.
     2) There will only be one device attached and it will always be attached
         to the first port of the root hub.
     3) Once you retrieve the base I/O address from the PCI, you may assume
         that I/O address for the rest of the task.  Same for the Int number.
     4) You do not have to handle stall's or error codes as long as you
         code your stack correctly.
     5) That the device is a low speed (LS) device

    You *may not* assume:
     1) Anything about the UHCI controller.  You must follow all rules of
         the UHCI and any reserved bits.

    You *must*:
     1) Find the I/O address of the UHCI controller from the PCI.
     2) Setup all 1024 stack frames, even if you only use one of them.
        i.e: all dwords must have bit 0 set on frame creation even if
          you will only use one of the dwords for your frame.
     3) Not modify the SOF register (base + 0x0C).
     4) Not crash the computer or UHCI in any way.
     7) Allow your program to exit and restart in the same DOS session
        (no reboot) and detect the same device.
     8) Use the default address of zero (0) for the device
     9) Use a UHCI queue with your TD's inside the queue.
    10) Clear the breadth-first bit (i.e: use breadth first, not depth first)

    You *must not*:
     1) Assume anything about any memory that is not allocated to you,
         either by int 21h func 48h or at program startup (the 64K+ you are
         allocated for the .com program), unless it is well documented.
     2) Have or use any other USB drivers or assume any other USB code
         is loaded in memory.
     3) Modify any bits in any register other than the ones mentioned above.
         i.e: do not:
          - write to the status register.
          - modify any bit in the INT Enable register except for the low nibble.
          - read or write to/from the frame number register.
          - read or write to/from the SOF register.
          - must only write to the frame list address register once as a
            dword value, or twice as two consecutive word values.
     4) Set the address of the device
     5) Simply delay after the packets are sent.  You must wait for the
        interrupt to occur before you assume the data in the return buffer
        is valid.

*   Note on reseting the port:
     When you reset the port, you can simply set bit 9.  However, due to the
     nature of the UHCI, when you clear bit 9, you must read in the port
     first, clear the bit, then write it back.  This isn't so important when
     clearing the reset bit, as it is when enabling the port.  For example,
     have a look at the following code:

        mov  dx,io_base
        add  dx,10h

        mov  ax,(1<<9)
        out  dx,ax

        call delay  ; wait for the reset to occur

        and  ax,(~(1<<9))
        out  dx,ax

        call delay  ; wait for the enable to occur

        or   ax,(1<<2)
        out  dx,ax

     The above code looks like it may work just fine.  However, due to the
      nature of the UHCI (and most I/O hardware), the port value will change
      during the delay.  Therefore, you can not simply write the value that
      you read before the delay, back to the port expecting the correct values
      to be set/cleared.
     Therefore, you must read in the port value, set the enable bit, and
      write it back to be a legal enable.
     For the sake of this compo, you *must* read in the value from the
      port, change a bit, then write the value back when you are clearing
      the port reset and enabling the port.

    To help clarify these rules an example program (appropriately called
    example.asm) is provided.  The example program still does all of the
    older rules, i.e: still uses DOS to allocate the frame list.  However,
    I will leave it up to you to delete from the example code the items
    that are no longer needed.  That is the fun part anyway, right?

    You must call your program "entry.com".

    Send your entry.com along with its source code to Sniper at:

                  ------->> fys@frontiernet.net <<-------

    The compo deadline is 31st of August 2009 at the stroke of midnight MST.
    You may submit entries as often as you like up until the deadline. In
    fact you're encouraged to do so to add excitement to the compo.

    Besides these rules, you must follow the general rules that apply to all
    compos, which are described in the accompanying file, general.txt.

    If you do not have a test computer with a UHCI controller and/or do not
    have a USB device to test with, you may use the Bochs emulator at

        bochs.sourceforge.net

    It will emulate the UHCI and a mouse, hub, or thumbdrive.  If you use
    this technique for your tests, and it works on Bochs, this doesn't mean
    that your entry will pass my test on real hardware.

    TEST SUITE:  There will be no test suite.  You can send your entry to
    me and I will see if it returns the expected results.  I will let you
    know either way with details about it not passing if that is the case.

    Please Note: Even if it passes my tests as expected, this doesn't mean
    you have a valid entry.  It is up to you to make sure that your entry
    passes all the rules above.  At the end of the compo, each entry
    will be released to the group to verify its validity.

    If a violation is found, 5 bytes will be added for each instance
    plus the number of bytes required to fix it.

    If you have any questions or comments, please feel free to post them at:

        hugi-compo@yahoogroups.com

    Thanks to Boreal for helping with these rules.

    -Sniper
