Controlling when the phone is the USB host

This functionality is for On-The-Go (OTG)-enabled devices only.

This document tells you how to use the USB Manager's functionality for controlling when the phone becomes the USB host and when it surrenders the host role to a remote device.

For information about surrendering the host role while the phone nevertheless remains an A-device, see Dropping VBus.

Purpose

The USB Manager includes functions for controlling when the phone becomes the USB host. Only the USB control application can call these functions. It must call them in response to the message notifications from USB Manager that are described in Making requests from a local application for the phone to become USB host (where there is also a diagram illustrating the sequence of events leading to an RUsb::BusRequest() call).

When the USB control application receives an RUsb::MessageNotification() response from the USB Manager informing it that:

  • A local application has made a request for the phone to become the USB host, then the USB control application needs to call the USB Manager's RUsb::BusRequest() function.

    The RUsb::BusRequest() function can be called both on the A-device and the B-device to indicate a desire to become the USB host. The consequence of calling it depends on the current state of the device: the call may trigger the raising of VBus, the generation of SRP signalling or the commencement of HNP; or it may have no consequence at all if none of these are appropriate actions in the circumstances. In any case, the action that is taken after a call to RUsb::BusRequest() is decided by the OTG state machine that is built into the kernel of the Symbian platform; the USB control application cannot directly affect the decision.

    However, it is of course possible to describe the possible consequences of calling RUsb:BusRequest():

    • If the local device is the A-device (ID-pin is present), the consequence will be that the OTG core raises VBus and the device becomes the host (the effect is that the host is woken up from an idle state).

    • If the local device is a B-device (ID-pin is not present) that is currently suspended and that has had its b_hnp_enable flag set to True by the A-device, then the consequence will be that the OTG core on the local device triggers HNP (the Host Negotiation Protocol) to attempt to become the USB host.

    • If the local device is a B-device (ID-pin is not present) that detects no session on VBus, then the consequence will be that the OTG core on the local device triggers an SRP (Session Request Protocol) signal to the remote A-device.

    These consequences of calling the RUsb::BusRequest() functions from the USB control application are summarised in the table below.

    Table of outcomes from a call to RUsb::BusRequest()

    Condition of local device:

    VBus present:

    VBus not present:

    A-device:

    A-device does nothing (it is already the host)

    A-device raises VBus and becomes the host (awaking from an idle host state)

    B-device (suspended and b_hnp_enable is True):

    A-device performs HNP

    This is not a possible state

    B-device (not suspended and b_hnp_enabled is False):

    Nothing can happen, because the A-device is not permitting a role-swap

    B-device issues an SRP request to the remote A-device

  • A remotely attached B-device has issued an SRP request, then the USB control application on the receiving A-device attempts to relinquish the role of USB host by calling the USB Manager's RUsb::BusRespondSrp().

    The RUsb::BusRespondSrp() function is essentially a special form of the RUsb::BusRequest() function; it is used only on an A-device that has received an SRP signal from a remote B-device, and its purpose is to allow the A-device to raise VBus and attempt to give the role of USB host to the remote B-device as quickly as possible.

    The possible consequences of calling RUsb::BusRespondSrp() are described below:

    • If the A-device does not support HNP, then the consequence of its USB control application calling RUsb::BusRespondSrp() will be that the OTG core raises VBus and enumerates the remote B-device.

    • If both the A-device and the remote B-device support HNP, then the A-device raises VBus but does not enumerate the B-device; instead it sets the remote B-device's b_hnp_enable flag and then suspends the B-device. The B-device, finding that its VBus is present, that its b_hnp_enable flag is set to True, and that it has been suspended, then initiates HNP signalling (by lowering its D+ pull-up resisitor). This change is detected by the A-device, which infers from it that the B-device has "disconnected" (in the technical sense used by the USB specification, but obviously the cables remain inserted) and wants to perfom HNP signalling. The A-device then completes the HNP signalling transaction by asserting the D+ pull-up resistor on its side of the USB connection. This in turn is detected by the B-device. The result of all this is that, although the A-device continues as the A-device, it is now performing the role of a USB peripheral, and the B-device is its host.

    These consequences of calling the RUsb::BusRespondSrp() functions from the USB control application are summarised in the table below. For a diagram that illustrates the sequence of events where both phones support Host Negotiation Protocol, see Signalling Request Protocol (SRP) sequence diagram.

    Table of outcomes from a call to RUsb::BusRespondSrp()*

    A-device's HNP capability:

    B-device is HNP-capable:

    B-device is not HNP-capable:

    A-device is HNP-capable:

    A-device raises VBus, checks the B-device's OTG descriptor to see if the B-device supports HNP, then performs a role swap.

    A-device raises VBus, checks the B-device's OTG descriptor and finds that the B-device does not support HNP, then enumerates the B-device.

    A-device is not HNP-capable:

    A-device raises VBus and enumerates the B-device.

    A-device raises VBus and enumerates the B-device.

    *This table assumes that the USB control application on the A-device has called RUsb::BusRespondSrp() in response to an in-coming SRP signal from the remote B-device.

Becoming the USB host

Basic Procedure

The high-level steps involved in controlling when the phone is and is not the USB host are shown here:

  1. Request message notifications from the USB Manager.

    The notifications you receive will include messages indicating that:

  2. Attempt to make the phone become the USB host.

  3. Check on the success of your attempt to make the phone become the USB host.

Requesting to become the USB host

To control when the phone becomes the USB host or surrenders the role of USB host to a remote device:

  1. In your USB control application, subscribe to the OTG state publish-and-subscribe key.

    You will need to use this value to check whether your request for the phone to become host has been successful. For more information about how to do this, see Monitoring the phone's USB host/OTG state.

  2. In your USB control application, watch for message notifications from the USB Manager

  3. There are two possible message notifications that need to result in function calls from your USB control application that affect the phone's USB role (in other words, that will determine whether the phone is to be a USB host or peripheral in relation to a remote device):

    • If an RUsb::MessageNotification() call completes by indicating that a local application has made a session request, then, in your USB control application, call:

      RUsb::BusRequest();

      For the possible consequences of this function call under different conditions, see the table of outcomes for the RUsb::BusRequest() function above.

    • If an RUsb::MessageNotification() call completes by indicating that a remote peripheral has issued an SRP request, then, in your USB control application, call:

      RUsb::BusRespondSrp();

      For the possible consequences of this function call under different conditions, see the table of outcomes for the RUsb::BusRespondSrp() function above.

    Note: USB services need to be started before you call either RUsb::BusRequest() or RUsb::BusRespondSrp(). This is because the phone needs to be ready to perform USB communication as soon as it becomes a USB host.

The following sample code shows a possible implementation for handling local session and remote SRP requests. It includes comments about how to handle error conditions, and, in the case of RUsb::BusRequest(), this includes information about how to clear a VBus error. For more information about clearing VBus errors, see Clearing VBus errors.

RUsb usb;
User::LeaveIfError(usb.Connect());
...

TRequestStatus status;
TInt message;
usb.MessageNotification(status, message);  // Subscribe for notification of host events 
User::WaitForRequest(status);              // Note: this is an instance when it 
                                           // is appropriate to use a Symbian active object
                                    
if ( status == KerrNone )
    {
    // use it
    switch (message)
        {
         ...

         case KUsbMessageSrpReceived:          // An SRP request has been received from a remote device
                                                  // Start USB services (if they are not already running)
              Tint err = usb.BusRespondSrp(); //Call the RUsb::BusRespondSrp() function 
              if (err != KerrNone)
               {  
               // Handle error
               switch (err)
                    {
                case KErrUsbOtgStackNotStarted:
                    // The stack has not started properly; panic your current thread.
                    break;
                case KErrUsbOtgVbusAlreadyRaised:
                    // No action required - VBus is already up. 
                    // This is perfectly legitimate as
                    // the A-host can raise VBus at any time.
                    break;
                case KerrUsbOtgBadState:
                    // If the device has successfully received an SRP signal,
                    // this error probably means that the application has raised 
                    // VBus itself in another thread. In that case, the application 
                    // is probably in error and you are advised to panic the current thread.
                    break;
                default:
                    // You might receive errors such as KErrNoMemory.
                    // Application-specific handling is required to process these.
                    }
               }
               break;

         case KUsbMessageRequestSession:      // A request for the phone to become host has been 
                                                 // received from a local application 
                                           // Start USB services (if they are not already running)
              Tint err = usb.BusRequest(); // Call the RUsb::BusRequest() function 
              if (err != KerrNone)
               {  
                //Handle error
               switch (err)
                    {
                case KErrUsbOtgStackNotStarted:
                    // The stack has not started properly; panic your current thread.
                    break;
                case KErrUsbOtgVbusAlreadyRaised:
                    // No action required - VBus is already up. 
                    // This is perfectly legitimate as
                    // the A-host can raise VBus at any time.
                    break;
                case KerrUsbOtgBadState:
                    // This means that VBus is in an error state and needs to be cleared.
                    // A B-device has drawn too much current from the A-device.
                    // You need to trigger some User Interface activity telling the 
                    // phone's user to unplug the offending device.
                    // When this is has been done, continue by clearing the bus error

                    err = usb.BusClearError();
                    if (err != KErrNone)
                        {
                        // Handle error. 
                        // You cannot receive a stack-not-started error at this point.
                        // Therefore the bad state can only come from the user ignoring the
                        // user-interface message advising him or her to disconnect the 
                        // offending device. (You must also handle out-of-memory errors 
                        // when you call RUsb::BusClearError().)
                        }

                    // Once the VBus error is cleared, the application can try calling
                    // the RUsb::BusRequest() function again.
                    break;
                default:
                    // You might receive errors such as KErrNoMemory.
                    // Application-specific handling is required to process these.
                    }
                }
               break;
         ...

            default:
                       // default processing
        }
    }
else
   {
   // handle an error
   }

...
usb.Close();

Clearing VBus errors

The sample code in the previous section includes information about restoring USB services on an A-device after a B-device has been drawing too much power from it.

When this happens, the A-device is unable to maintain the necessary voltage on VBus, and the low-level OTG stack automatically drops VBus to prevent damage to the A-device. In the code in the previous section, if this has happened, VBus will be down when RUsb::BusRequest() is called, and an error condition will therefore exist that makes further USB connectivity impossible.

To restore USB connectivity, as the code in the previous section shows, you need to call RUsb::BusClearError(). This restores the A-device to an A-idle state from which it can pass to a host state again when required (see The OTG state transitions). You must also display a message telling the phone's user to remove the offending device from the phone.

This VBus error condition is reported by the publish-and-subscribe property key for the OTG state. The code below shows you how to subscribe to this property value and inspect it, which you might want to do, for example, after calling RUsb::BusRequest(), to see whether your request for the phone to become the host has been successful (see the sample code below). This VBus error condition is also indicated by the KerrUsbOtgBadState error, which can be returned by RUsb::BusRequest() (see the code in the previous section).

For general information about using the OTG state property key, see Monitoring the phone's USB host/OTG state.

Note:

RProperty otgStateProp;                    // Create a otg state property object
                                           // Attach to it
User::LeaveIfError(otgStateProp.Attach(KUidUsbManCategory, KUsbOtgStateProperty));
TRequestStatus status2;
otgStateProp.Subscribe(status2); //Subscribe for notification of the next OTG state P&S key value change

if (status2 == KErrNone)                
    {
    TInt state_message;
    otgStateProp.Get(state_message) //Inspect the OTG state P&S key to see whether a VBus error occurred

    switch(state_message) 
        {
    ...
    case EUsbOtgStateAVbusError:            // If the OTG state indicates a VBus error
        TInt err = usb.BusClearError();     // you must run RUsb::BusClearError()
        if (err != KErrNone)
            {
            // Handle Error
            // The RUsb::BusClearError() function can return "KErrUsbOtgStackNotStarted"
            // but will not in this specific case because the OTG stack had to have
            // been started for the control application to receive the OTG state change notification.

            // The function can also return "KErrUsbOtgBadState" but only if another 
            // thread has sucessfully cleared the VBus error and the two threads were 
            // in a race condition.

            // The most likely error is KErrNoMemory in the out-of-memory case.
            }
    ...
        }
    }
...