This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

RTS / CTS handshaking with LPC2148 USB VCOM example

How might I implement RTS / CTS handshaking using the dual VCom example (AN10420) for the LPC2148? I have an audio stream on VCOM0 I would like to halt if my input buffer is almost full and continue transfer once it has enough space again. I am only interested in data from the virtual port (NO_UART_CABLE = 1), not sending it to the LPC2148's physical UART.

I thought perhaps I could set the modem0status bits (below) defined in the example to control the CTS line, with bit allocations matching the 16550 uart spec (bit 4 of modem status register = CTS) but transfer from the host side doesn't stop. In the physical implementation, SetSIORTS() controls the LPC2148's UART registers to assert the RTS state, but I don't see how the host driver can detect that the device UART's CTS pin is asserted.

Thanks for any help.

// EP3 is used to report UART0 status, and EP9
//is used to report UART1 status respectively
if ( ReportStatus0 )
{
     ReportStatus0 = 0;

     // For Interrupt IN on EP3
     USB_WriteEP( 3 | 0x80, &Modem0Status, sizeof(Modem0Status) );
}
if ( ReportStatus1 )
{
     ReportStatus1 = 0;

     // For Interrupt IN on EP9
     USB_WriteEP( 9 | 0x80, &Modem1Status, sizeof(Modem1Status) );
}

Parents
  • See the endpoint descriptors on USB_ConfigDescriptor[] (usbdesc.c).
    Unfortunately, EP3 and EP9 is not used on this example.

    Moreover, though the interrupt IN EPs (EP1 and EP4) are defined in the descriptor, these EPs are not written in anywhere. Search "USB_WriteEP" in the source code globally. You cannot find these code.
    USB_WriteEP(0x80 | 0x01, ...)
    USB_WriteEP(0x80 | 0x04, ...)

    That is, no handshake lines of input (CTS, DSR, DCD) are sent to PC.
    However, the device driver surely expects the interrupt pipe as ModemStatus.

    usbio.c
    StartInterruptUrb( ... )
    {
       ...
    // Initialize the URB we use for reading the interrupt pipe
    
        UsbBuildInterruptOrBulkTransferRequest(
            urb,
            sizeof (struct _URB_BULK_OR_INTERRUPT_TRANSFER),
            PipeHandle,
            &Extension->intdata,
            NULL,
            4,
            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
            NULL);
    
    OnInterrupt( ... )
    {
        if (Extension->DeviceIsOpened == TRUE &&
            (Extension->intdata[2] == 0x60)) {
                SerialHandleModemUpdate( Extension, ... );
    
    modmflow.c
    SerialHandleModemUpdate( ... )
    {
        ModemStatus = Extension->intdata[3];
    

    As of the flow control, this example implements nothing.
    The device driver sends vendor request to the device, expecting the device handles flow control. However, the device ignores the request in SetSIOFlowControl().

    1) Flow control
    1-1) Host app
    SetCommState
    DCB.fRtsControl = RTS_CONTROL_HANDSHAKE (0x02)
    
    1-2) Device driver
    usbcom.h
    #define VendorFlowCtrl  0x07
    
    ioctl.c
    UsbCom_ProcessIOCTL( ... )
    {
        ...
        switch (ioControlCode) {
            ...
            case IOCTL_SERIAL_SET_HANDFLOW: {
                USHORT Value = 0;
                ...
                if (HandFlow->FlowReplace & SERIAL_RTS_HANDSHAKE)
                Value |= 0x11;
                ...
                UsbCom_SendVendor(DeviceObject, VendorFlowCtrl, Value);
    
    1-3) Vendor request
    bRequest = VendorFlowCtrl (0x07)
    wValue = Value (0x11)
    
    
    1-4) firmware
    usbcore.c
    void USB_EndPoint0 (DWORD event) {
        ...
        case REQUEST_VENDOR:
    #if USB_VCOM
            UARTSettingCmd = SetupPacket.bRequest & 0xFF;
            UARTSettingData = SetupPacket.wValue.WB.L;
            if (!VCOM_SetSIOSetup( UARTSettingCmd, UARTSettingData )) {
                goto stall_i;
            }
            USB_DataInStage();
    #endif
            break;
    
    vcomuser.h
    #define FLOWCTRL_SETUP          0x07
    
    vcomuser.c
    BOOL VCOM_SetSIOSetup( BYTE Cmd, BYTE Data ) {
        case FLOWCTRL_SETUP:
            SetSIOFlowControl( ChannelNum, Data );
    
    
    void SetSIOFlowControl( BYTE channelNum, BYTE ConfigValue )
    {
      /* NO flow control setting is neceaasry, unlike the UART on x51 */
      if ( channelNum == 0 ) {
           ConfigValue = ConfigValue;
      }
      else if ( channelNum == 1 ) {
           ConfigValue = ConfigValue;
      }
    }
    

    Therefore, you have to implement the flow control by yourself from scratch.
    Don't worry. It's very easy.

    To halt the bulk OUT transfer, don't call DeviceData2UART() in USB_EndPoint2()
    Instead, raise a flag, Host2Data0, like Data2Host0.

    vcomuser.c
    
    BYTE Host2Data0 = 0;
    
    void USB_EndPoint2 (DWORD event) {
      switch (event) {
        case USB_EVT_IN:
          Data2Host0 = 1;
          break;
        case USB_EVT_OUT:
    //    DeviceData2UART( 0 );  // <--------- comment out
          Host2Data0 = 1;
          break;
      }
      event;
    }
    

    In the main loop, poll this Host2Data0.
    When Host2Data0 is asserted, AND there is enough room on the buffer,
    drop Host2Data0 and call DeviceData2UART(),

    That's all.

    While the endpoint FIFO is not empty, the OUT EP returns NAK to the host. When the host sees NAK, it retries the transaction until the EP accepts it. No data drops.
    That is, host waits for the firmware to unload the data from the FIFO.
    Therefore, the flow control is done on the USB protocol.

    Tsueno

Reply
  • See the endpoint descriptors on USB_ConfigDescriptor[] (usbdesc.c).
    Unfortunately, EP3 and EP9 is not used on this example.

    Moreover, though the interrupt IN EPs (EP1 and EP4) are defined in the descriptor, these EPs are not written in anywhere. Search "USB_WriteEP" in the source code globally. You cannot find these code.
    USB_WriteEP(0x80 | 0x01, ...)
    USB_WriteEP(0x80 | 0x04, ...)

    That is, no handshake lines of input (CTS, DSR, DCD) are sent to PC.
    However, the device driver surely expects the interrupt pipe as ModemStatus.

    usbio.c
    StartInterruptUrb( ... )
    {
       ...
    // Initialize the URB we use for reading the interrupt pipe
    
        UsbBuildInterruptOrBulkTransferRequest(
            urb,
            sizeof (struct _URB_BULK_OR_INTERRUPT_TRANSFER),
            PipeHandle,
            &Extension->intdata,
            NULL,
            4,
            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
            NULL);
    
    OnInterrupt( ... )
    {
        if (Extension->DeviceIsOpened == TRUE &&
            (Extension->intdata[2] == 0x60)) {
                SerialHandleModemUpdate( Extension, ... );
    
    modmflow.c
    SerialHandleModemUpdate( ... )
    {
        ModemStatus = Extension->intdata[3];
    

    As of the flow control, this example implements nothing.
    The device driver sends vendor request to the device, expecting the device handles flow control. However, the device ignores the request in SetSIOFlowControl().

    1) Flow control
    1-1) Host app
    SetCommState
    DCB.fRtsControl = RTS_CONTROL_HANDSHAKE (0x02)
    
    1-2) Device driver
    usbcom.h
    #define VendorFlowCtrl  0x07
    
    ioctl.c
    UsbCom_ProcessIOCTL( ... )
    {
        ...
        switch (ioControlCode) {
            ...
            case IOCTL_SERIAL_SET_HANDFLOW: {
                USHORT Value = 0;
                ...
                if (HandFlow->FlowReplace & SERIAL_RTS_HANDSHAKE)
                Value |= 0x11;
                ...
                UsbCom_SendVendor(DeviceObject, VendorFlowCtrl, Value);
    
    1-3) Vendor request
    bRequest = VendorFlowCtrl (0x07)
    wValue = Value (0x11)
    
    
    1-4) firmware
    usbcore.c
    void USB_EndPoint0 (DWORD event) {
        ...
        case REQUEST_VENDOR:
    #if USB_VCOM
            UARTSettingCmd = SetupPacket.bRequest & 0xFF;
            UARTSettingData = SetupPacket.wValue.WB.L;
            if (!VCOM_SetSIOSetup( UARTSettingCmd, UARTSettingData )) {
                goto stall_i;
            }
            USB_DataInStage();
    #endif
            break;
    
    vcomuser.h
    #define FLOWCTRL_SETUP          0x07
    
    vcomuser.c
    BOOL VCOM_SetSIOSetup( BYTE Cmd, BYTE Data ) {
        case FLOWCTRL_SETUP:
            SetSIOFlowControl( ChannelNum, Data );
    
    
    void SetSIOFlowControl( BYTE channelNum, BYTE ConfigValue )
    {
      /* NO flow control setting is neceaasry, unlike the UART on x51 */
      if ( channelNum == 0 ) {
           ConfigValue = ConfigValue;
      }
      else if ( channelNum == 1 ) {
           ConfigValue = ConfigValue;
      }
    }
    

    Therefore, you have to implement the flow control by yourself from scratch.
    Don't worry. It's very easy.

    To halt the bulk OUT transfer, don't call DeviceData2UART() in USB_EndPoint2()
    Instead, raise a flag, Host2Data0, like Data2Host0.

    vcomuser.c
    
    BYTE Host2Data0 = 0;
    
    void USB_EndPoint2 (DWORD event) {
      switch (event) {
        case USB_EVT_IN:
          Data2Host0 = 1;
          break;
        case USB_EVT_OUT:
    //    DeviceData2UART( 0 );  // <--------- comment out
          Host2Data0 = 1;
          break;
      }
      event;
    }
    

    In the main loop, poll this Host2Data0.
    When Host2Data0 is asserted, AND there is enough room on the buffer,
    drop Host2Data0 and call DeviceData2UART(),

    That's all.

    While the endpoint FIFO is not empty, the OUT EP returns NAK to the host. When the host sees NAK, it retries the transaction until the EP accepts it. No data drops.
    That is, host waits for the firmware to unload the data from the FIFO.
    Therefore, the flow control is done on the USB protocol.

    Tsueno

Children
No data