VirtualBox

Changeset 69980 in vbox for trunk/src/VBox/Runtime/r3/win


Ignore:
Timestamp:
Dec 7, 2017 2:17:47 PM (7 years ago)
Author:
vboxsync
Message:

Runtime/serialport-win.cpp: Updates, implement non blocking reads and writes, querying and changing the port config and the event poll API

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/win/serialport-win.cpp

    r69966 r69980  
    6666    /** The event handle to wait on for waking up waiting threads externally. */
    6767    HANDLE              hEvtIntr;
    68     /** Events pending which were not queried yet. */
    69     volatile uint32_t   fEvtsPending;
     68    /** Events currently waited for. */
     69    uint32_t            fEvtMask;
     70    /** Flag whether a write is currently pending. */
     71    bool                fWritePending;
     72    /** Bounce buffer for writes. */
     73    uint8_t            *pbBounceBuf;
     74    /** Amount of used buffer space. */
     75    size_t              cbBounceBufUsed;
     76    /** Amount of allocated buffer space. */
     77    size_t              cbBounceBufAlloc;
    7078    /** The current active port config. */
    7179    DCB                 PortCfg;
     
    7987*   Defined Constants And Macros                                                                                                 *
    8088*********************************************************************************************************************************/
     89/** The pipe buffer size we prefer. */
     90#define RTSERIALPORT_NT_SIZE      _32K
    8191
    8292
     
    91101*   Internal Functions                                                                                                           *
    92102*********************************************************************************************************************************/
     103
     104/**
     105 * Updatest the current event mask to wait for.
     106 *
     107 * @returns IPRT status code.
     108 * @param   pThis                   The internal serial port instance data.
     109 * @param   fEvtMask                The new event mask to change to.
     110 */
     111static int rtSerialPortWinUpdateEvtMask(PRTSERIALPORTINTERNAL pThis, uint32_t fEvtMask)
     112{
     113    DWORD dwEvtMask = 0;
     114
     115    if (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
     116        dwEvtMask |= EV_RXCHAR;
     117    if (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
     118        dwEvtMask |= EV_TXEMPTY;
     119    if (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
     120        dwEvtMask |= EV_BREAK;
     121    if (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
     122        dwEvtMask |= EV_CTS | EV_DSR | EV_RING | EV_RLSD;
     123
     124    int rc = VINF_SUCCESS;
     125    if (!SetCommMask(pThis->hDev, dwEvtMask))
     126        rc = RTErrConvertFromWin32(GetLastError());
     127    else
     128        pThis->fEvtMask = fEvtMask;
     129
     130    return rc;
     131}
    93132
    94133
     
    123162
    124163
     164/**
     165 * Common worker for handling I/O completion.
     166 *
     167 * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB.
     168 *
     169 * @returns IPRT status code.
     170 * @param   pThis               The pipe instance handle.
     171 */
     172static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis)
     173{
     174    int rc = VINF_SUCCESS;
     175    DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
     176    if (dwRc == WAIT_OBJECT_0)
     177    {
     178        DWORD cbWritten = 0;
     179        if (GetOverlappedResult(pThis->hDev, &pThis->Overlapped, &cbWritten, TRUE))
     180        {
     181            for (;;)
     182            {
     183                if (cbWritten >= pThis->cbBounceBufUsed)
     184                {
     185                    pThis->fWritePending = false;
     186                    rc = VINF_SUCCESS;
     187                    break;
     188                }
     189
     190                /* resubmit the remainder of the buffer - can this actually happen? */
     191                memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
     192                rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
     193                if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
     194                               &cbWritten, &pThis->Overlapped))
     195                {
     196                    if (GetLastError() == ERROR_IO_PENDING)
     197                        rc = VINF_TRY_AGAIN;
     198                    else
     199                    {
     200                        pThis->fWritePending = false;
     201                        rc = RTErrConvertFromWin32(GetLastError());
     202                    }
     203                    break;
     204                }
     205                Assert(cbWritten > 0);
     206            }
     207        }
     208        else
     209        {
     210            pThis->fWritePending = false;
     211            rc = RTErrConvertFromWin32(GetLastError());
     212        }
     213    }
     214    else if (dwRc == WAIT_TIMEOUT)
     215        rc = VINF_TRY_AGAIN;
     216    else
     217    {
     218        pThis->fWritePending = false;
     219        if (dwRc == WAIT_ABANDONED)
     220            rc = VERR_INVALID_HANDLE;
     221        else
     222            rc = RTErrConvertFromWin32(GetLastError());
     223    }
     224    return rc;
     225}
     226
     227
    125228RTDECL(int)  RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
    126229{
     
    135238    if (pThis)
    136239    {
    137         pThis->fOpenFlags   = fFlags;
    138         pThis->fEvtsPending = 0;
    139         pThis->hEvtDev      = CreateEvent(NULL, FALSE, FALSE, NULL);
     240        pThis->fOpenFlags       = fFlags;
     241        pThis->fEvtMask         = 0;
     242        pThis->fWritePending    = false;
     243        pThis->pbBounceBuf      = NULL;
     244        pThis->cbBounceBufUsed  = 0;
     245        pThis->cbBounceBufAlloc = 0;
     246        pThis->hEvtDev          = CreateEvent(NULL, FALSE, FALSE, NULL);
    140247        if (pThis->hEvtDev)
    141248        {
     249            pThis->Overlapped.hEvent = pThis->hEvtDev,
    142250            pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL);
    143251            if (pThis->hEvtIntr)
     
    194302    AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
    195303
     304    if (pThis->fWritePending)
     305        rtSerialPortWriteCheckCompletion(pThis);
     306
    196307    CloseHandle(pThis->hDev);
    197308    CloseHandle(pThis->hEvtDev);
     
    217328RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
    218329{
    219     RT_NOREF(hSerialPort, pvBuf, cbToRead, pcbRead);
    220     return VERR_NOT_IMPLEMENTED;
     330    PRTSERIALPORTINTERNAL pThis = hSerialPort;
     331    AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
     332    AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
     333    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     334    AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
     335
     336    RT_NOREF(pcbRead);
     337    int rc = VERR_NOT_IMPLEMENTED;
     338    return rc;
    221339}
    222340
     
    233351    *pcbRead = 0;
    234352
    235     return VERR_NOT_IMPLEMENTED;
     353    /*
     354     * Kick of an overlapped read.  It should return immediately if
     355     * there is bytes in the buffer.  If not, we'll cancel it and see
     356     * what we get back.
     357     */
     358    int rc = VINF_SUCCESS;
     359    BOOL fSucc = ResetEvent(pThis->Overlapped.hEvent); Assert(fSucc == TRUE);
     360    DWORD cbRead = 0;
     361    if (   cbToRead == 0
     362        || ReadFile(pThis->hDev, pvBuf,
     363                    cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
     364                    &cbRead, &pThis->Overlapped))
     365    {
     366        *pcbRead = cbRead;
     367        rc = VINF_SUCCESS;
     368    }
     369    else if (GetLastError() == ERROR_IO_PENDING)
     370    {
     371        if (!CancelIo(pThis->hDev))
     372            WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
     373        if (GetOverlappedResult(pThis->hDev, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
     374        {
     375            *pcbRead = cbRead;
     376            rc = VINF_SUCCESS;
     377        }
     378        else if (GetLastError() == ERROR_OPERATION_ABORTED)
     379        {
     380            *pcbRead = 0;
     381            rc = VINF_TRY_AGAIN;
     382        }
     383        else
     384            rc = RTErrConvertFromWin32(GetLastError());
     385    }
     386    else
     387        rc = RTErrConvertFromWin32(GetLastError());
     388
     389    return rc;
    236390}
    237391
     
    253407    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
    254408
    255     return VERR_NOT_IMPLEMENTED;
     409    /* If I/O is pending, check if it has completed. */
     410    int rc = VINF_SUCCESS;
     411    if (pThis->fWritePending)
     412        rc = rtSerialPortWriteCheckCompletion(pThis);
     413    if (rc == VINF_SUCCESS)
     414    {
     415        Assert(!pThis->fWritePending);
     416
     417        /* Do the bounce buffering. */
     418        if (    pThis->cbBounceBufAlloc < cbToWrite
     419            &&  pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE)
     420        {
     421            if (cbToWrite > RTSERIALPORT_NT_SIZE)
     422                cbToWrite = RTSERIALPORT_NT_SIZE;
     423            void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
     424            if (pv)
     425            {
     426                pThis->pbBounceBuf = (uint8_t *)pv;
     427                pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
     428            }
     429            else
     430                rc = VERR_NO_MEMORY;
     431        }
     432        else if (cbToWrite > RTSERIALPORT_NT_SIZE)
     433            cbToWrite = RTSERIALPORT_NT_SIZE;
     434        if (RT_SUCCESS(rc) && cbToWrite)
     435        {
     436            memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
     437            pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
     438
     439            /* Submit the write. */
     440            rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
     441            DWORD cbWritten = 0;
     442            if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
     443                          &cbWritten, &pThis->Overlapped))
     444            {
     445                *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
     446                rc = VINF_SUCCESS;
     447            }
     448            else if (GetLastError() == ERROR_IO_PENDING)
     449            {
     450                *pcbWritten = cbToWrite;
     451                pThis->fWritePending = true;
     452                rc = VINF_SUCCESS;
     453            }
     454            else
     455                rc = RTErrConvertFromWin32(GetLastError());
     456        }
     457        else if (RT_SUCCESS(rc))
     458            *pcbWritten = 0;
     459    }
     460    else if (RT_SUCCESS(rc))
     461        *pcbWritten = 0;
     462
     463    return rc;
    256464}
    257465
     
    263471    AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
    264472
    265     RT_NOREF(pCfg);
    266 
    267     return VERR_NOT_IMPLEMENTED;
     473    pCfg->uBaudRate = pThis->PortCfg.BaudRate;
     474    switch (pThis->PortCfg.Parity)
     475    {
     476        case NOPARITY:
     477            pCfg->enmParity = RTSERIALPORTPARITY_NONE;
     478            break;
     479        case EVENPARITY:
     480            pCfg->enmParity = RTSERIALPORTPARITY_EVEN;
     481            break;
     482        case ODDPARITY:
     483            pCfg->enmParity = RTSERIALPORTPARITY_ODD;
     484            break;
     485        case MARKPARITY:
     486            pCfg->enmParity = RTSERIALPORTPARITY_MARK;
     487            break;
     488        case SPACEPARITY:
     489            pCfg->enmParity = RTSERIALPORTPARITY_SPACE;
     490            break;
     491        default:
     492            AssertFailed();
     493            return VERR_INTERNAL_ERROR;
     494    }
     495
     496    switch (pThis->PortCfg.ByteSize)
     497    {
     498        case 5:
     499            pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
     500            break;
     501        case 6:
     502            pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
     503            break;
     504        case 7:
     505            pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
     506            break;
     507        case 8:
     508            pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
     509            break;
     510        default:
     511            AssertFailed();
     512            return VERR_INTERNAL_ERROR;
     513    }
     514
     515    switch (pThis->PortCfg.StopBits)
     516    {
     517        case ONESTOPBIT:
     518            pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
     519            break;
     520        case ONE5STOPBITS:
     521            pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
     522            break;
     523        case TWOSTOPBITS:
     524            pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
     525            break;
     526        default:
     527            AssertFailed();
     528            return VERR_INTERNAL_ERROR;
     529    }
     530
     531    return VINF_SUCCESS;
    268532}
    269533
     
    275539    AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
    276540
    277     RT_NOREF(pCfg, pErrInfo);
    278 
    279     return VERR_NOT_IMPLEMENTED;
     541    RT_NOREF(pErrInfo);
     542
     543    DCB DcbNew;
     544    memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew));
     545    DcbNew.BaudRate = pCfg->uBaudRate;
     546
     547    switch (pCfg->enmParity)
     548    {
     549        case RTSERIALPORTPARITY_NONE:
     550            DcbNew.Parity = NOPARITY;
     551            break;
     552        case RTSERIALPORTPARITY_EVEN:
     553            DcbNew.Parity = EVENPARITY;
     554            break;
     555        case RTSERIALPORTPARITY_ODD:
     556            DcbNew.Parity = ODDPARITY;
     557            break;
     558        case RTSERIALPORTPARITY_MARK:
     559            DcbNew.Parity = MARKPARITY;
     560            break;
     561        case RTSERIALPORTPARITY_SPACE:
     562            DcbNew.Parity = SPACEPARITY;
     563            break;
     564        default:
     565            AssertFailedReturn(VERR_INVALID_PARAMETER);
     566    }
     567
     568    switch (pCfg->enmDataBitCount)
     569    {
     570        case RTSERIALPORTDATABITS_5BITS:
     571            DcbNew.ByteSize = 5;
     572            break;
     573        case RTSERIALPORTDATABITS_6BITS:
     574            DcbNew.ByteSize = 6;
     575            break;
     576        case RTSERIALPORTDATABITS_7BITS:
     577            DcbNew.ByteSize = 7;
     578            break;
     579        case RTSERIALPORTDATABITS_8BITS:
     580            DcbNew.ByteSize = 8;
     581            break;
     582        default:
     583            AssertFailedReturn(VERR_INVALID_PARAMETER);
     584    }
     585
     586    switch (pCfg->enmStopBitCount)
     587    {
     588        case RTSERIALPORTSTOPBITS_ONE:
     589            DcbNew.StopBits = ONESTOPBIT;
     590            break;
     591        case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
     592            AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
     593            DcbNew.StopBits = ONE5STOPBITS;
     594            break;
     595        case RTSERIALPORTSTOPBITS_TWO:
     596            AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
     597            DcbNew.StopBits = TWOSTOPBITS;
     598            break;
     599        default:
     600            AssertFailedReturn(VERR_INVALID_PARAMETER);
     601    }
     602
     603    int rc = VINF_SUCCESS;
     604    if (!SetCommState(pThis->hDev, &DcbNew))
     605        rc = RTErrConvertFromWin32(GetLastError());
     606    else
     607        memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew));
     608
     609    return rc;
    280610}
    281611
     
    290620    AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
    291621
    292     RT_NOREF(msTimeout);
    293 
    294     return VERR_NOT_IMPLEMENTED;
     622    *pfEvtsRecv = 0;
     623
     624    int rc = VINF_SUCCESS;
     625    if (fEvtMask != pThis->fEvtMask)
     626        rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask);
     627
     628    if (RT_SUCCESS(rc))
     629    {
     630        DWORD dwEventMask = 0;
     631        HANDLE ahWait[2];
     632        ahWait[0] = pThis->hEvtDev;
     633        ahWait[1] = pThis->hEvtIntr;
     634
     635        RT_ZERO(pThis->Overlapped);
     636        pThis->Overlapped.hEvent = pThis->hEvtDev;
     637
     638        if (!WaitCommEvent(pThis->hDev, &dwEventMask, &pThis->Overlapped))
     639        {
     640            DWORD dwRet = GetLastError();
     641            if (dwRet == ERROR_IO_PENDING)
     642            {
     643                dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout);
     644                if (dwRet == WAIT_TIMEOUT)
     645                    rc = VERR_TIMEOUT;
     646                else if (dwRet == WAIT_FAILED)
     647                    rc = RTErrConvertFromWin32(GetLastError());
     648                else if (dwRet != WAIT_OBJECT_0)
     649                    rc = VERR_INTERRUPTED;
     650            }
     651            else
     652                rc = RTErrConvertFromWin32(dwRet);
     653        }
     654
     655        if (RT_SUCCESS(rc))
     656        {
     657            /* Check the event */
     658            if (dwEventMask & EV_RXCHAR)
     659                *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
     660            if (dwEventMask & EV_TXEMPTY)
     661            {
     662                if (pThis->fWritePending)
     663                {
     664                    rc = rtSerialPortWriteCheckCompletion(pThis);
     665                    if (rc == VINF_SUCCESS)
     666                        *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
     667                    else
     668                        rc = VINF_SUCCESS;
     669                }
     670                else
     671                    *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
     672            }
     673            if (dwEventMask & EV_BREAK)
     674                *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
     675            if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD))
     676                *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
     677        }
     678    }
     679
     680    return rc;
    295681}
    296682
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette