Difference between revisions of "83Plus:Ports:91"
(Port 91 - pipe writing status (or, Port of Doom #1)) |
(No difference)
|
Revision as of 20:36, 24 November 2010
Contents
Synopsis
Port Number: 91h
Function: USB Write Command/Status
This port controls the current state of a USB pipe, and allows you to send and receive data packets.
This port only exists on the the TI-84 Plus and the TI-84 Plus Silver Edition. On the standard TI-83 Plus, it acts as a shadow of port 11. On the TI-83 Plus Silver Edition, this port has no effect. |
Read Values (control pipe)
- Bit 0: Set if a packet has been received. Might also be set at other times.
- Bit 1: Set if we currently have a packet queued to be sent.
- Bit 2: Set if we have sent or received a STALL.
- Bit 3: Unknown
- Bit 4: Set if a low-level error has occurred (device not responding, or possibly other errors.)
- Bit 5: Unknown
- Bit 6: Unknown
- Bit 7: Unknown
Write Values (control pipe)
- Bit 1: Set to send the currently-buffered data.
- Host mode only:
- Bit 3: Set (along with bit 1) to send a SETUP packet.
- Bit 5: Set to request a packet from the peripheral.
- Peripheral mode only:
- Bit 3: Set if the transfer will be complete after sending the current packet, if any.
- Bit 5: Set to flag an error (STALL.)
- Bit 6: Set to reset the pipe's toggle state to DATA1.
- Other bits' effects are unknown, and should be set to 0.
Read Values (bulk/interrupt/iso pipes)
- Bit 0: Unknown
- Bit 1: Unknown
- Bit 2: Set in host mode if we have received a STALL. Might also be set in peripheral mode if we have sent a STALL.
- Bit 3: Unknown
- Bit 4: Set if the pipe is halted.
- Bit 5: Set if a low-level error has occurred (device not responding, or possibly other errors.)
- Bit 6: Unknown
- Bit 7: Unknown
Write Values (bulk/interrupt/iso pipes)
- Bit 0: Set to send the currently-buffered data.
- Bit 3: Unknown, but sometimes used by the OS.
- Bit 4: Set to halt the pipe.
- Bit 6: Set (along with bit 3) to reset the pipe's toggle state to DATA0.
- Other bits' effects are unknown, and should be set to 0.
Comments
This port reports and controls the I/O state of the current pipe. (The current pipe is selected by port 8E.) This port is used for sending and receiving data on control pipes, as well as for sending data on bulk, interrupt, and isochronous pipes. For receiving bulk/interrupt/iso data, see port 94.
Not all of the bits of this port are understood, and more experimentation would be useful. The output values suggested below are the ones used by USB8x and the OS, but they may not be the only values that work.
All of the notes below assume that port 8E is set appropriately. Also be aware that unless you have interrupts disabled, or you're using a USB hook to handle interrupts yourself, the system interrupt handler will get in the way of whatever you're trying to do.
Bulk and interrupt transfers
The following notes apply to bulk and interrupt pipes; as seen above, the control pipe works quite differently. Isochronous pipes probably work similarly to bulk and interrupt pipes, but this also needs testing, since the low-level protocol is so different.
Before using the pipe for bulk, interrupt, or isochronous transfers, be sure to configure it using ports 90, 98, and 99.
Resetting the pipe
To reset the pipe (un-halt it, and, for bulk and interrupt pipes, reset the toggle state), write a value of 48h to port 91. In peripheral mode, you are expected to do this whenever you receive a Set Configuration request, and whenever you receive a Clear Feature (ENDPOINT_HALT) request (even if the endpoint wasn't halted to begin with.) If you don't, the next packet you send may be lost.
In host mode, you should reset the pipe whenever you send a Set Configuration request or a Clear Feature (ENDPOINT_HALT) request. USB8x and the OS don't do this, presumably because they only ever send one Set Configuration request after turning on the USB controller, and never send Clear Feature requests.
Halting the pipe (peripheral mode)
In peripheral mode, to indicate an error condition on the pipe, or in response to a Set Feature (ENDPOINT_HALT) request, write a value of 10h to port 91. This will cause the controller to send a STALL in response to any IN request on that endpoint.
Sending data
To send data to the pipe, first write the data bytes - at most wMaxPacketSize of them - to the appropriate Ax port, then write a value of 01h to port 91.
The USB controller will attempt to send the data as soon as it has an opportunity. When the packet is sent, or an error occurs, the appropriate bit of port 82 will be set (and an interrupt will be generated.) You can then check port 91 to see what happened:
- Bit 5 will be set if a low-level error occurred. This probably means that the destination endpoint doesn't exist, the device isn't configured, or the device was unplugged.
- Host mode only: Bit 2 will be set if the destination endpoint is halted (the peripheral device is signalling an error condition.) You will need either to reset the endpoint using a Clear Feature (ENDPOINT_HALT) request, or reset the entire device, in order to proceed.
- Some other types of errors will be reported by port 86 instead.
- Otherwise, you can assume the packet was transferred successfully.
After sending a series of data packets, the OS writes a value of 08h to port 91. I don't know what effect this has.
Control transfers (host mode)
The following notes apply to pipe zero in host mode, and would probably also apply to any non-zero control pipes.
A control transfer consists of 3 "stages" - Setup, Data, and Status. For more information about these stages, see section 8.5.2 (1.1) / 8.5.3 (2.0) of the USB specification. Note, in particular, that Data and Status packets need to be handled slightly differently, due to the DATA0/DATA1 toggling behavior.
Sending a setup packet
To send a Setup packet, first write the eight data bytes to port A0, then write a value of 0Ah to port 91. As with bulk transactions, the USB controller will attempt to send the data as soon as it has an opportunity. When the packet is sent, or an error occurs, bit 0 of port 82 will be set (and an interrupt will be generated.) You can then check port 91 to see what happened:
- Hypothetically, bit 2 would be set if the device rejected the packet (which is not permitted by the USB specification.)
- Bit 4 will be set if a low-level error occurred.
- Some other types of errors will be reported by port 86 instead.
- Otherwise, you can assume the packet was transferred successfully.
Sending a data packet
To send a "normal" packet (as part of the Data stage of an OUT transfer; note that this is a very uncommon thing to do), first write the data bytes - at most bMaxPacketSize0 of them - to port A0, then write a value of 02h to port 91. This works the same as above, except that the peripheral is allowed to send a STALL response indicating an error; in that case, bit 2 of port 91 will be set.
Sending a status packet
To send a status packet (as the Status stage of an IN transfer), write a value of 42h to port 91. This packet should always be empty, so you don't do anything with port A0. As with the above cases, port 82 will indicate when the transaction is completed, and port 91 will indicate any errors.
Receiving a data packet
To request a "normal" packet (as part of the Data stage of an IN transfer), write a value of 20h to port 91. The USB controller will request data from the peripheral, several times if necessary, until it either receives a valid data packet, a STALL response, or a low-level error occurs. Once any of these things happens, bit 0 of port 82 will be set. You can then check port 91 to see what happened:
- Bit 2 will be set if the device sent a STALL (indicating an invalid or unsupported control request.)
- Bit 4 will be set if a low-level error occurred.
- Some other types of errors will be reported by port 86 instead.
- Otherwise, you can assume the packet was transferred successfully.
If the packet was transferred successfully, port 96 will indicate the number of bytes received, which you can read from port A0. Note that neither USB8x nor the OS actually checks port 96; instead, they assume that the packet was the expected size. I don't know the reason for this, and it doesn't seem like a good idea in general.
Remember that in many cases, the data to be transferred is larger than bMaxPacketSize0, so you may need to receive several packets; stop only when you receive one that is smaller than bMaxPacketSize0.
Receiving a status packet
To request a status packet (as the Status stage of an OUT transfer), write a value of 60h to port 91. As with the above case, port 82 will indicate when the transaction is completed, and port 91 will indicate any errors. This packet should always be empty.
Control transfers (peripheral mode)
The following notes apply to pipe zero in peripheral mode, and would probably also apply to any non-zero control pipes. A lot more experimentation would be helpful in this area.
In peripheral mode, if you want to communicate with a standard USB host such as a PC, you must be able to handle all of the mandatory requests defined in section 9.4 of the USB specification. These include:
- Set Address (→ device)
- Set Configuration (→ device)
- Set Interface (→ interface)
- Set Feature (ENDPOINT_HALT) (→ endpoint)
- Clear Feature (ENDPOINT_HALT) (→ endpoint)
- Get Status (← device, ← interface, ← endpoint)
- Get Descriptor (← device)
- Get Configuration (← device)
- Get Interface (← interface)
Receiving a setup packet
When a setup packet is received, bit 0 of port 82 will be set (and an interrupt will be generated.) Bit 0 of port 91 will also be set. Port 96 indicates the size of the packet - always 8 for a setup packet - and you can read the packet contents from port A0. You must then decide what to do in response:
Stalling
If you receive an invalid or unsupported request, write a value of 60h to port 91. This is a "protocol stall," and unlike halting a bulk or interrupt endpoint, is not considered a permanent error condition. I think the stall condition will be cleared automatically whenever a setup packet is received; this should be tested.
Handling an IN request
If the request is an IN request (bit 7 of bRequestType set), such as a Get Descriptor request, you are expected to send data back to the host. The wLength given in the setup packet indicates the maximum total number of bytes the host will accept; you can send fewer than this if you like. Every packet you send, except the last one, must be exactly bMaxPacketSize0 bytes; when you send a packet that's smaller than this, the host will know that you're finished.
Before sending any data packets, it might be necessary to write a value of 40h to port 91 to clear the pipe's toggle state.
Then, for each packet except the last one, write the data bytes to port A0, then write a value of 02h to port 91. For the final packet, use a value of 0Ah instead (this will handle the Status stage as well.)
When each data packet is sent, bit 0 of port 82 will be set and an interrupt will be generated.
I'm not sure what happens if the host stops requesting data and moves on to the Status stage, or begins a new control request, earlier than expected. Possibly port 91 bit 4 would be set in this case.
Handling a zero-length OUT request
Most OUT requests (such as Set Address and Set Configuration) have zero length: all of the parameters are provided by the wValue and/or wIndex values in the setup packet. In these cases, after performing whatever changes are needed, write a value of 48h to port 91 to acknowledge the request.
A special case is Set Address: in that case, you must only change your address after the Status stage is completed. So when you receive a Set Address request, write 48h to port 91, wait until the Status stage finishes (bit 0 of port 82 is set), and then write the new device address to port 80.
Handling an OUT request with data
Some OUT requests, such as Set Descriptor, can include additional data after the setup packet. You would presumably need to write some value, possibly 00h, to port 91, to read the next packet in the sequence. This has not been tested.
After receiving and handling the data, you would use a value of 48h, as above, to acknowledge the request.