Using Pipes

This tutorial explains how to use pipes to transfer data.

Introduction

USB Pipes represent the connection between the USB Host and a USB Device’s endpoints. The main use of a pipe is for transferring data to and from a device.

Basic steps

This guide covers the following topics:

  • Opening and closing pipes

  • Performing transfers

Opening and closing pipes

To open a RUsbPipe instance, call RUsbInterface::OpenPipeForEndpoint.

RUsbPipe pipe;
TInt endpointNum = 0x81; // Example endpoint number
TBool useDma = ETrue;
TInt err = interface.OpenPipeForEndpoint(pipe, endpointNum, useDma);
if(err != KErrNone)
    {
    return err;
    }

Note: The specification for using DMA for transfers on a pipe is a best effort request.

When the RUsbPipe session is no longer needed, close it with RUsbPipe::Close.

pipe.Close();

Performing transfers

Transfers are made using the transfer descriptors registered to the interface the pipe is associated with. Only a transfer descriptor of the appropriate type may be issued on a pipe, for example only interrupt transfer descriptors may be used on interrupt endpoints. Attempts to use a transfer descriptor that is not the appropriate type results in the client being panicked.

To issue a transfer, provide a RUsbTransferDescriptor instance to the RUsbPipe::Transfer function as in the following example.

TRequestStatus status;
pipe.Transfer(isocTrans, status);
//This asynchronous operation may be encapsulated in an active object
User::WaitForRequest(status);
if(status.Int() != KErrNone)
    {
    return status.Int();
    }

To cancel all transfers outstanding on a USB pipe, use RUsbPipe::CancelAllTransfers.

Note: It is not possible to cancel individual transfers.

pipe.CancelAllTransfers();

As discussed in Creating transfer descriptors there are three types of transfer descriptor:

  • Interrupt transfer descriptors

  • Isochronous transfer descriptors

  • Bulk transfer descriptors.

For information on signifying the end of a bulk data transfer, see Zero Length Packets Technology Guide.

Interrupt transfer descriptors

Using an interrupt transfer descriptor is relatively easy. The API provides access to a buffer readable and/or writable, and to a method of “committing” data written into the buffer.

IN direction

For transfers on an IN-bound pipes only a length is required (to indicate how much data is to be received). The length must be less than or equal to the size of the buffer represented by the transfer descriptors.

Use the RUsbIntrTransferDescriptor::SaveData function to set the length for the desired transfer.

// RUsbIntrTransferDescriptor intrTrans; // Already registered and initialised
const TInt KBytesToReceive = 0x10;

__ASSERT_ALWAYS(KBytesToReceive <= intrTrans.WritableBuffer().MaxSize(), 
User::Panic(_L(“FDI”), EBufferToSmallForRequest));
intrTrans.SaveData(KBytesToReceive);

Do the transfer.

TRequestStatus status;
pipe.Transfer(intrTrans, status);
//This asynchronous operation may be encapsulated in an active object
User::WaitForRequest(status);
if(status.Int() != KErrNone)
    {
    return status.Int();
    }

Once the transfer has been completed, read the data with the RUsbIntrTransferDescriptor::Buffer function.

TPtrC8 dataIn = intrTrans.Buffer();

OUT direction

OUT-bound pipe transfers are similar to IN-bound transfers.

The data must be written into the buffer through the TPtr8 provided by the RUsbIntrTransferDescriptor::WritableBuffer function before the transfer is requested.

// RUsbIntrTransferDescriptor intrTrans; // Already registered and initialised
_LIT8(KDataToSend, “This is just example data”);

__ASSERT_ALWAYS(KDataToSend().Size() <= intrTrans.WritableBuffer().MaxSize(), 
User::Panic(_L(“FDI”), EBufferToSmallForRequest));
intrTrans.WritableBuffer() = KDataToSend();

The length of the data to be transferred must be specified (like in the IN case) using RUsbIntrTransferDescriptor::SaveData.


intrTrans.SaveData(KDataToSend.Size());

Do the transfer.

TRequestStatus status;
pipe.Transfer(intrTrans, status);
User::WaitForRequest(status);
if(status.Int() != KErrNone)
    {
    return status.Int();
    }

Stalled endpoints

An endpoint might signal an error through stalling an endpoint. For the function driver to resume operation after handling the error case, it needs to clear the stall. This is an endpoint 0 operation, however a utility function (RUsbPipe::ClearRemoteStall) is provided to help the writer of a function driver.

TInt err = pipe.ClearRemoteStall();
if(err != KErrNone)
    {
    return err;
    }

After clearing the stall, the pipe should resume normal operation (although the state of the device after a stall is class/device dependent).

Related information