| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617 |
- /**
- * WinPR: Windows Portable Runtime
- * Serial Communication API
- *
- * Copyright 2011 O.S. Systems Software Ltda.
- * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
- * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
- * Copyright 2014 Hewlett-Packard Development Company, L.P.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #if defined __linux__ && !defined ANDROID
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <termios.h>
- #include <unistd.h>
- #include "comm_serial_sys.h"
- #ifdef __UCLIBC__
- #include "comm.h"
- #endif
- #include <winpr/crt.h>
- #include <winpr/wlog.h>
- /* Undocumented flag, not supported everywhere.
- * Provide a sensible fallback to avoid compilation problems. */
- #ifndef CMSPAR
- #define CMSPAR 010000000000
- #endif
- /* hard-coded in N_TTY */
- #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
- #define TTY_THRESHOLD_UNTHROTTLE 128
- #define N_TTY_BUF_SIZE 4096
- #define _BAUD_TABLE_END 0010020 /* __MAX_BAUD + 1 */
- /* 0: B* (Linux termios)
- * 1: CBR_* or actual baud rate
- * 2: BAUD_* (identical to SERIAL_BAUD_*)
- */
- static const speed_t _BAUD_TABLE[][3] = {
- #ifdef B0
- { B0, 0, 0 }, /* hang up */
- #endif
- #ifdef B50
- { B50, 50, 0 },
- #endif
- #ifdef B75
- { B75, 75, BAUD_075 },
- #endif
- #ifdef B110
- { B110, CBR_110, BAUD_110 },
- #endif
- #ifdef B134
- { B134, 134, 0 /*BAUD_134_5*/ },
- #endif
- #ifdef B150
- { B150, 150, BAUD_150 },
- #endif
- #ifdef B200
- { B200, 200, 0 },
- #endif
- #ifdef B300
- { B300, CBR_300, BAUD_300 },
- #endif
- #ifdef B600
- { B600, CBR_600, BAUD_600 },
- #endif
- #ifdef B1200
- { B1200, CBR_1200, BAUD_1200 },
- #endif
- #ifdef B1800
- { B1800, 1800, BAUD_1800 },
- #endif
- #ifdef B2400
- { B2400, CBR_2400, BAUD_2400 },
- #endif
- #ifdef B4800
- { B4800, CBR_4800, BAUD_4800 },
- #endif
- /* {, ,BAUD_7200} */
- #ifdef B9600
- { B9600, CBR_9600, BAUD_9600 },
- #endif
- /* {, CBR_14400, BAUD_14400}, /\* unsupported on Linux *\/ */
- #ifdef B19200
- { B19200, CBR_19200, BAUD_19200 },
- #endif
- #ifdef B38400
- { B38400, CBR_38400, BAUD_38400 },
- #endif
- /* {, CBR_56000, BAUD_56K}, /\* unsupported on Linux *\/ */
- #ifdef B57600
- { B57600, CBR_57600, BAUD_57600 },
- #endif
- #ifdef B115200
- { B115200, CBR_115200, BAUD_115200 },
- #endif
- /* {, CBR_128000, BAUD_128K}, /\* unsupported on Linux *\/ */
- /* {, CBR_256000, BAUD_USER}, /\* unsupported on Linux *\/ */
- #ifdef B230400
- { B230400, 230400, BAUD_USER },
- #endif
- #ifdef B460800
- { B460800, 460800, BAUD_USER },
- #endif
- #ifdef B500000
- { B500000, 500000, BAUD_USER },
- #endif
- #ifdef B576000
- { B576000, 576000, BAUD_USER },
- #endif
- #ifdef B921600
- { B921600, 921600, BAUD_USER },
- #endif
- #ifdef B1000000
- { B1000000, 1000000, BAUD_USER },
- #endif
- #ifdef B1152000
- { B1152000, 1152000, BAUD_USER },
- #endif
- #ifdef B1500000
- { B1500000, 1500000, BAUD_USER },
- #endif
- #ifdef B2000000
- { B2000000, 2000000, BAUD_USER },
- #endif
- #ifdef B2500000
- { B2500000, 2500000, BAUD_USER },
- #endif
- #ifdef B3000000
- { B3000000, 3000000, BAUD_USER },
- #endif
- #ifdef B3500000
- { B3500000, 3500000, BAUD_USER },
- #endif
- #ifdef B4000000
- { B4000000, 4000000, BAUD_USER }, /* __MAX_BAUD */
- #endif
- { _BAUD_TABLE_END, 0, 0 }
- };
- static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
- {
- int i;
- /* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
- * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
- */
- /* FIXME: properties should be better probed. The current
- * implementation just relies on the Linux' implementation.
- */
- if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
- {
- ZeroMemory(pProperties, sizeof(COMMPROP));
- pProperties->wPacketLength = sizeof(COMMPROP);
- }
- pProperties->wPacketVersion = 2;
- pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
- /* pProperties->Reserved1; not used */
- /* FIXME: could be implemented on top of N_TTY */
- pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
- pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
- /* FIXME: to be probe on the device? */
- pProperties->dwMaxBaud = BAUD_USER;
- /* FIXME: what about PST_RS232? see also: serial_struct */
- pProperties->dwProvSubType = PST_UNSPECIFIED;
- /* TODO: to be finalized */
- pProperties->dwProvCapabilities =
- /*PCF_16BITMODE |*/ PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK | /*PCF_RLSD |*/
- PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS |*/ PCF_TOTALTIMEOUTS | PCF_XONXOFF;
- /* TODO: double check SP_RLSD */
- pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
- SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS;
- pProperties->dwSettableBaud = 0;
- for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
- {
- pProperties->dwSettableBaud |= _BAUD_TABLE[i][2];
- }
- pProperties->wSettableData =
- DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/;
- pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE |
- PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
- /* FIXME: additional input and output buffers could be implemented on top of N_TTY */
- pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
- pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
- /* pProperties->ProvSpec1; see above */
- /* pProperties->ProvSpec2; ignored */
- /* pProperties->ProvChar[1]; ignored */
- return TRUE;
- }
- static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
- {
- int i;
- speed_t newSpeed;
- struct termios futureState;
- ZeroMemory(&futureState, sizeof(struct termios));
- if (tcgetattr(pComm->fd, &futureState) <
- 0) /* NB: preserves current settings not directly handled by the Communication Functions */
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
- {
- if (_BAUD_TABLE[i][1] == pBaudRate->BaudRate)
- {
- newSpeed = _BAUD_TABLE[i][0];
- if (cfsetspeed(&futureState, newSpeed) < 0)
- {
- CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%" PRIu32 ")", newSpeed,
- pBaudRate->BaudRate);
- return FALSE;
- }
- assert(cfgetispeed(&futureState) == newSpeed);
- if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
- {
- CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
- GetLastError());
- return FALSE;
- }
- return TRUE;
- }
- }
- CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %" PRIu32 "",
- pBaudRate->BaudRate);
- SetLastError(ERROR_INVALID_DATA);
- return FALSE;
- }
- static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
- {
- int i;
- speed_t currentSpeed;
- struct termios currentState;
- ZeroMemory(¤tState, sizeof(struct termios));
- if (tcgetattr(pComm->fd, ¤tState) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- currentSpeed = cfgetispeed(¤tState);
- for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
- {
- if (_BAUD_TABLE[i][0] == currentSpeed)
- {
- pBaudRate->BaudRate = _BAUD_TABLE[i][1];
- return TRUE;
- }
- }
- CommLog_Print(WLOG_WARN, "could not find a matching baud rate for the speed 0x%x",
- currentSpeed);
- SetLastError(ERROR_INVALID_DATA);
- return FALSE;
- }
- /**
- * NOTE: Only XonChar and XoffChar are plenty supported with the Linux
- * N_TTY line discipline.
- *
- * ERRORS:
- * ERROR_IO_DEVICE
- * ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
- * ERROR_NOT_SUPPORTED
- */
- static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
- {
- BOOL result = TRUE;
- struct termios upcomingTermios;
- ZeroMemory(&upcomingTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- if (pSerialChars->XonChar == pSerialChars->XoffChar)
- {
- /* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- /* termios(3): (..) above symbolic subscript values are all
- * different, except that VTIME, VMIN may have the same value
- * as VEOL, VEOF, respectively. In noncanonical mode the
- * special character meaning is replaced by the timeout
- * meaning.
- *
- * EofChar and c_cc[VEOF] are not quite the same, prefer to
- * don't use c_cc[VEOF] at all.
- *
- * FIXME: might be implemented during read/write I/O
- */
- if (pSerialChars->EofChar != '\0')
- {
- CommLog_Print(WLOG_WARN, "EofChar %02" PRIX8 " cannot be set\n", pSerialChars->EofChar);
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- /* According the Linux's n_tty discipline, charaters with a
- * parity error can only be let unchanged, replaced by \0 or
- * get the prefix the prefix \377 \0
- */
- /* FIXME: see also: _set_handflow() */
- if (pSerialChars->ErrorChar != '\0')
- {
- CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
- pSerialChars->ErrorChar, (char)pSerialChars->ErrorChar);
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- /* FIXME: see also: _set_handflow() */
- if (pSerialChars->BreakChar != '\0')
- {
- CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
- pSerialChars->BreakChar, (char)pSerialChars->BreakChar);
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- if (pSerialChars->EventChar != '\0')
- {
- pComm->eventChar = pSerialChars->EventChar;
- }
- upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
- upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
- if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
- {
- CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
- GetLastError());
- return FALSE;
- }
- return result;
- }
- static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
- {
- struct termios currentTermios;
- ZeroMemory(¤tTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, ¤tTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
- /* EofChar unsupported */
- /* ErrorChar unsupported */
- /* BreakChar unsupported */
- /* FIXME: see also: _set_serial_chars() */
- /* EventChar */
- pSerialChars->XonChar = currentTermios.c_cc[VSTART];
- pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
- return TRUE;
- }
- static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
- {
- BOOL result = TRUE;
- struct termios upcomingTermios;
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
- *
- * The use of 5 data bits with 2 stop bits is an invalid
- * combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
- *
- * FIXME: prefered to let the underlying driver to deal with
- * this issue. At least produce a warning message?
- */
- ZeroMemory(&upcomingTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- /* FIXME: use of a COMMPROP to validate new settings? */
- switch (pLineControl->StopBits)
- {
- case STOP_BIT_1:
- upcomingTermios.c_cflag &= ~CSTOPB;
- break;
- case STOP_BITS_1_5:
- CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits.");
- break;
- case STOP_BITS_2:
- upcomingTermios.c_cflag |= CSTOPB;
- break;
- default:
- CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %" PRIu8 "\n",
- pLineControl->StopBits);
- result = FALSE; /* but keep on */
- break;
- }
- switch (pLineControl->Parity)
- {
- case NO_PARITY:
- upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
- break;
- case ODD_PARITY:
- upcomingTermios.c_cflag &= ~CMSPAR;
- upcomingTermios.c_cflag |= PARENB | PARODD;
- break;
- case EVEN_PARITY:
- upcomingTermios.c_cflag &= ~(PARODD | CMSPAR);
- upcomingTermios.c_cflag |= PARENB;
- break;
- case MARK_PARITY:
- upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
- break;
- case SPACE_PARITY:
- upcomingTermios.c_cflag &= ~PARODD;
- upcomingTermios.c_cflag |= PARENB | CMSPAR;
- break;
- default:
- CommLog_Print(WLOG_WARN, "unexpected type of parity: %" PRIu8 "\n",
- pLineControl->Parity);
- result = FALSE; /* but keep on */
- break;
- }
- switch (pLineControl->WordLength)
- {
- case 5:
- upcomingTermios.c_cflag &= ~CSIZE;
- upcomingTermios.c_cflag |= CS5;
- break;
- case 6:
- upcomingTermios.c_cflag &= ~CSIZE;
- upcomingTermios.c_cflag |= CS6;
- break;
- case 7:
- upcomingTermios.c_cflag &= ~CSIZE;
- upcomingTermios.c_cflag |= CS7;
- break;
- case 8:
- upcomingTermios.c_cflag &= ~CSIZE;
- upcomingTermios.c_cflag |= CS8;
- break;
- default:
- CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %" PRIu8 "\n",
- pLineControl->WordLength);
- result = FALSE; /* but keep on */
- break;
- }
- if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
- {
- CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
- GetLastError());
- return FALSE;
- }
- return result;
- }
- static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
- {
- struct termios currentTermios;
- ZeroMemory(¤tTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, ¤tTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
- if (!(currentTermios.c_cflag & PARENB))
- {
- pLineControl->Parity = NO_PARITY;
- }
- else if (currentTermios.c_cflag & CMSPAR)
- {
- pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
- }
- else
- {
- /* PARENB is set */
- pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
- }
- switch (currentTermios.c_cflag & CSIZE)
- {
- case CS5:
- pLineControl->WordLength = 5;
- break;
- case CS6:
- pLineControl->WordLength = 6;
- break;
- case CS7:
- pLineControl->WordLength = 7;
- break;
- default:
- pLineControl->WordLength = 8;
- break;
- }
- return TRUE;
- }
- static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
- {
- BOOL result = TRUE;
- struct termios upcomingTermios;
- ZeroMemory(&upcomingTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- /* HUPCL */
- /* logical XOR */
- if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
- (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
- ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
- !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
- {
- CommLog_Print(WLOG_WARN,
- "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
- "will be set since it is claimed for one of the both lines.",
- (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF",
- (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF");
- }
- if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
- (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
- {
- upcomingTermios.c_cflag |= HUPCL;
- }
- else
- {
- upcomingTermios.c_cflag &= ~HUPCL;
- /* FIXME: is the DTR line also needs to be forced to a disable state according
- * SERIAL_DTR_CONTROL? */
- /* FIXME: is the RTS line also needs to be forced to a disable state according
- * SERIAL_RTS_CONTROL? */
- }
- /* CRTSCTS */
- /* logical XOR */
- if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
- (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
- ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
- !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
- {
- CommLog_Print(WLOG_WARN,
- "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
- "CRTSCTS will be set since it is claimed for one of the both lines.",
- (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF",
- (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF");
- }
- if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
- (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
- {
- upcomingTermios.c_cflag |= CRTSCTS;
- }
- else
- {
- upcomingTermios.c_cflag &= ~CRTSCTS;
- }
- /* ControlHandShake */
- if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
- {
- /* DTR/DSR flow control not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
- {
- /* DTR/DSR flow control not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
- {
- /* DCD flow control not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
- {
- /* DSR line control not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
- {
- /* Aborting operations on error not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- /* FlowReplace */
- if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
- {
- upcomingTermios.c_iflag |= IXON;
- }
- else
- {
- upcomingTermios.c_iflag &= ~IXON;
- }
- if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
- {
- upcomingTermios.c_iflag |= IXOFF;
- }
- else
- {
- upcomingTermios.c_iflag &= ~IXOFF;
- }
- // FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0'
- if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
- {
- /* errors will be replaced by the character '\0'. */
- upcomingTermios.c_iflag &= ~IGNPAR;
- }
- else
- {
- upcomingTermios.c_iflag |= IGNPAR;
- }
- if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
- {
- upcomingTermios.c_iflag |= IGNBRK;
- }
- else
- {
- upcomingTermios.c_iflag &= ~IGNBRK;
- }
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
- {
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
- {
- /* not supported on Linux */
- CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- /* XonLimit */
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
- {
- CommLog_Print(WLOG_WARN, "Attempt to set XonLimit with an unsupported value: %" PRId32 "",
- pHandflow->XonLimit);
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- /* XoffChar */
- // FIXME: could be implemented during read/write I/O
- if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
- {
- CommLog_Print(WLOG_WARN, "Attempt to set XoffLimit with an unsupported value: %" PRId32 "",
- pHandflow->XoffLimit);
- SetLastError(ERROR_NOT_SUPPORTED);
- result = FALSE; /* but keep on */
- }
- if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
- {
- CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
- GetLastError());
- return FALSE;
- }
- return result;
- }
- static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
- {
- struct termios currentTermios;
- ZeroMemory(¤tTermios, sizeof(struct termios));
- if (tcgetattr(pComm->fd, ¤tTermios) < 0)
- {
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- /* ControlHandShake */
- pHandflow->ControlHandShake = 0;
- if (currentTermios.c_cflag & HUPCL)
- pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
- /* SERIAL_DTR_HANDSHAKE unsupported */
- if (currentTermios.c_cflag & CRTSCTS)
- pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
- /* SERIAL_DSR_HANDSHAKE unsupported */
- /* SERIAL_DCD_HANDSHAKE unsupported */
- /* SERIAL_DSR_SENSITIVITY unsupported */
- /* SERIAL_ERROR_ABORT unsupported */
- /* FlowReplace */
- pHandflow->FlowReplace = 0;
- if (currentTermios.c_iflag & IXON)
- pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
- if (currentTermios.c_iflag & IXOFF)
- pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
- if (!(currentTermios.c_iflag & IGNPAR))
- pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
- if (currentTermios.c_iflag & IGNBRK)
- pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
- /* SERIAL_BREAK_CHAR unsupported */
- if (currentTermios.c_cflag & HUPCL)
- pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
- if (currentTermios.c_cflag & CRTSCTS)
- pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
- /* SERIAL_XOFF_CONTINUE unsupported */
- /* XonLimit */
- pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
- /* XoffLimit */
- pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
- return TRUE;
- }
- static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
- {
- /* NB: timeouts are applied on system during read/write I/O */
- /* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
- if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
- (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
- {
- CommLog_Print(
- WLOG_WARN,
- "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
- pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
- pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
- pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
- pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
- CommLog_Print(WLOG_DEBUG, "ReadIntervalTimeout %" PRIu32 "",
- pComm->timeouts.ReadIntervalTimeout);
- CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutMultiplier %" PRIu32 "",
- pComm->timeouts.ReadTotalTimeoutMultiplier);
- CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutConstant %" PRIu32 "",
- pComm->timeouts.ReadTotalTimeoutConstant);
- CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutMultiplier %" PRIu32 "",
- pComm->timeouts.WriteTotalTimeoutMultiplier);
- CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutConstant %" PRIu32 "",
- pComm->timeouts.WriteTotalTimeoutConstant);
- return TRUE;
- }
- static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
- {
- pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
- pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
- pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
- pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
- pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
- return TRUE;
- }
- static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines)
- {
- if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCMBIS ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
- errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines)
- {
- if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCMBIC ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
- errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _set_dtr(WINPR_COMM* pComm)
- {
- SERIAL_HANDFLOW handflow;
- if (!_get_handflow(pComm, &handflow))
- return FALSE;
- /* SERIAL_DTR_HANDSHAKE not supported as of today */
- assert((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
- if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- return _set_lines(pComm, TIOCM_DTR);
- }
- static BOOL _clear_dtr(WINPR_COMM* pComm)
- {
- SERIAL_HANDFLOW handflow;
- if (!_get_handflow(pComm, &handflow))
- return FALSE;
- /* SERIAL_DTR_HANDSHAKE not supported as of today */
- assert((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
- if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- return _clear_lines(pComm, TIOCM_DTR);
- }
- static BOOL _set_rts(WINPR_COMM* pComm)
- {
- SERIAL_HANDFLOW handflow;
- if (!_get_handflow(pComm, &handflow))
- return FALSE;
- if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- return _set_lines(pComm, TIOCM_RTS);
- }
- static BOOL _clear_rts(WINPR_COMM* pComm)
- {
- SERIAL_HANDFLOW handflow;
- if (!_get_handflow(pComm, &handflow))
- return FALSE;
- if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- return _clear_lines(pComm, TIOCM_RTS);
- }
- static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
- {
- UINT32 lines = 0;
- if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- ZeroMemory(pRegister, sizeof(ULONG));
- /* FIXME: Is the last read of the MSR register available or
- * cached somewhere? Not quite sure we need to return the 4
- * LSBits anyway. A direct access to the register -- which
- * would reset the register -- is likely not expected from
- * this function.
- */
- /* #define SERIAL_MSR_DCTS 0x01 */
- /* #define SERIAL_MSR_DDSR 0x02 */
- /* #define SERIAL_MSR_TERI 0x04 */
- /* #define SERIAL_MSR_DDCD 0x08 */
- if (lines & TIOCM_CTS)
- *pRegister |= SERIAL_MSR_CTS;
- if (lines & TIOCM_DSR)
- *pRegister |= SERIAL_MSR_DSR;
- if (lines & TIOCM_RI)
- *pRegister |= SERIAL_MSR_RI;
- if (lines & TIOCM_CD)
- *pRegister |= SERIAL_MSR_DCD;
- return TRUE;
- }
- /* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
- static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK =
- SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
- SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
- /* SERIAL_EV_PERR | */
- SERIAL_EV_RX80FULL /*|
- SERIAL_EV_EVENT1 |
- SERIAL_EV_EVENT2*/
- ;
- static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
- {
- ULONG possibleMask;
- /* Stops pending IOCTL_SERIAL_WAIT_ON_MASK
- * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
- */
- if (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
- {
- /* FIXME: any doubt on reading PendingEvents out of a critical section? */
- EnterCriticalSection(&pComm->EventsLock);
- pComm->PendingEvents |= SERIAL_EV_FREERDP_STOP;
- LeaveCriticalSection(&pComm->EventsLock);
- /* waiting the end of the pending _wait_on_mask() */
- while (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
- Sleep(10); /* 10ms */
- }
- /* NB: ensure to leave the critical section before to return */
- EnterCriticalSection(&pComm->EventsLock);
- if (*pWaitMask == 0)
- {
- /* clearing pending events */
- if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
- strerror(errno));
- if (pComm->permissive)
- {
- /* counters could not be reset but keep on */
- ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
- }
- else
- {
- SetLastError(ERROR_IO_DEVICE);
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- }
- pComm->PendingEvents = 0;
- }
- possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK;
- if (possibleMask != *pWaitMask)
- {
- CommLog_Print(WLOG_WARN,
- "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
- ", possible events= 0x%08" PRIX32 "",
- *pWaitMask, possibleMask);
- /* FIXME: shall we really set the possibleMask and return FALSE? */
- pComm->WaitEventMask = possibleMask;
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- pComm->WaitEventMask = possibleMask;
- LeaveCriticalSection(&pComm->EventsLock);
- return TRUE;
- }
- static BOOL _get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
- {
- *pWaitMask = pComm->WaitEventMask;
- return TRUE;
- }
- static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
- {
- if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
- return TRUE; /* nothing to do */
- /* FIXME: could be implemented on top of N_TTY */
- if (pQueueSize->InSize > N_TTY_BUF_SIZE)
- CommLog_Print(WLOG_WARN,
- "Requested an incompatible input buffer size: %" PRIu32
- ", keeping on with a %" PRIu32 " bytes buffer.",
- pQueueSize->InSize, N_TTY_BUF_SIZE);
- if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
- CommLog_Print(WLOG_WARN,
- "Requested an incompatible output buffer size: %" PRIu32
- ", keeping on with a %" PRIu32 " bytes buffer.",
- pQueueSize->OutSize, N_TTY_BUF_SIZE);
- SetLastError(ERROR_CANCELLED);
- return FALSE;
- }
- static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
- {
- if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR |
- SERIAL_PURGE_RXCLEAR)) > 0)
- {
- CommLog_Print(WLOG_WARN, "Invalid purge mask: 0x%" PRIX32 "\n", *pPurgeMask);
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- /* FIXME: currently relying too much on the fact the server
- * sends a single IRP_MJ_WRITE or IRP_MJ_READ at a time
- * (taking care though that one IRP_MJ_WRITE and one
- * IRP_MJ_READ can be sent simultaneously) */
- if (*pPurgeMask & SERIAL_PURGE_TXABORT)
- {
- /* Purges all write (IRP_MJ_WRITE) requests. */
- if (eventfd_write(pComm->fd_write_event, FREERDP_PURGE_TXABORT) < 0)
- {
- if (errno != EAGAIN)
- {
- CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
- strerror(errno));
- }
- assert(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_WRITE */
- }
- }
- if (*pPurgeMask & SERIAL_PURGE_RXABORT)
- {
- /* Purges all read (IRP_MJ_READ) requests. */
- if (eventfd_write(pComm->fd_read_event, FREERDP_PURGE_RXABORT) < 0)
- {
- if (errno != EAGAIN)
- {
- CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
- strerror(errno));
- }
- assert(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_READ */
- }
- }
- if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
- {
- /* Purges the transmit buffer, if one exists. */
- if (tcflush(pComm->fd, TCOFLUSH) < 0)
- {
- CommLog_Print(WLOG_WARN, "tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
- strerror(errno));
- SetLastError(ERROR_CANCELLED);
- return FALSE;
- }
- }
- if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
- {
- /* Purges the receive buffer, if one exists. */
- if (tcflush(pComm->fd, TCIFLUSH) < 0)
- {
- CommLog_Print(WLOG_WARN, "tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
- strerror(errno));
- SetLastError(ERROR_CANCELLED);
- return FALSE;
- }
- }
- return TRUE;
- }
- /* NB: _get_commstatus also produces most of the events consumed by _wait_on_mask(). Exceptions:
- * - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
- *
- */
- static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
- {
- /* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */
- struct serial_icounter_struct currentCounters;
- /* NB: ensure to leave the critical section before to return */
- EnterCriticalSection(&pComm->EventsLock);
- ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
- ZeroMemory(¤tCounters, sizeof(struct serial_icounter_struct));
- if (ioctl(pComm->fd, TIOCGICOUNT, ¤tCounters) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
- strerror(errno));
- CommLog_Print(WLOG_WARN, " could not read counters.");
- if (pComm->permissive)
- {
- /* Errors and events based on counters could not be
- * detected but keep on.
- */
- ZeroMemory(¤tCounters, sizeof(struct serial_icounter_struct));
- }
- else
- {
- SetLastError(ERROR_IO_DEVICE);
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- }
- /* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
- * pComm->counters.*) thinking the counters can loop */
- /* Errors */
- if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
- {
- pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
- }
- if (currentCounters.overrun != pComm->counters.overrun)
- {
- pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
- pComm->PendingEvents |= SERIAL_EV_ERR;
- }
- if (currentCounters.brk != pComm->counters.brk)
- {
- pCommstatus->Errors |= SERIAL_ERROR_BREAK;
- pComm->PendingEvents |= SERIAL_EV_BREAK;
- }
- if (currentCounters.parity != pComm->counters.parity)
- {
- pCommstatus->Errors |= SERIAL_ERROR_PARITY;
- pComm->PendingEvents |= SERIAL_EV_ERR;
- }
- if (currentCounters.frame != pComm->counters.frame)
- {
- pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
- pComm->PendingEvents |= SERIAL_EV_ERR;
- }
- /* HoldReasons */
- /* TODO: SERIAL_TX_WAITING_FOR_CTS */
- /* TODO: SERIAL_TX_WAITING_FOR_DSR */
- /* TODO: SERIAL_TX_WAITING_FOR_DCD */
- /* TODO: SERIAL_TX_WAITING_FOR_XON */
- /* TODO: SERIAL_TX_WAITING_ON_BREAK, see LCR's bit 6 */
- /* TODO: SERIAL_TX_WAITING_XOFF_SENT */
- /* AmountInInQueue */
- if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCINQ ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- /* AmountInOutQueue */
- if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCOUTQ ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- /* BOOLEAN EofReceived; FIXME: once EofChar supported */
- /* BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */
- /* other events based on counters */
- if (currentCounters.rx != pComm->counters.rx)
- {
- pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
- }
- if ((currentCounters.tx != pComm->counters.tx) && /* at least a transmission occurred AND ...*/
- (pCommstatus->AmountInOutQueue == 0)) /* output bufer is now empty */
- {
- pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
- }
- else
- {
- /* FIXME: "now empty" from the specs is ambiguous, need to track previous completed
- * transmission? */
- pComm->PendingEvents &= ~SERIAL_EV_TXEMPTY;
- }
- if (currentCounters.cts != pComm->counters.cts)
- {
- pComm->PendingEvents |= SERIAL_EV_CTS;
- }
- if (currentCounters.dsr != pComm->counters.dsr)
- {
- pComm->PendingEvents |= SERIAL_EV_DSR;
- }
- if (currentCounters.dcd != pComm->counters.dcd)
- {
- pComm->PendingEvents |= SERIAL_EV_RLSD;
- }
- if (currentCounters.rng != pComm->counters.rng)
- {
- pComm->PendingEvents |= SERIAL_EV_RING;
- }
- if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
- {
- pComm->PendingEvents |= SERIAL_EV_RX80FULL;
- }
- else
- {
- /* FIXME: "is 80 percent full" from the specs is ambiguous, need to track when it previously
- * * occurred? */
- pComm->PendingEvents &= ~SERIAL_EV_RX80FULL;
- }
- pComm->counters = currentCounters;
- LeaveCriticalSection(&pComm->EventsLock);
- return TRUE;
- }
- static BOOL _refresh_PendingEvents(WINPR_COMM* pComm)
- {
- SERIAL_STATUS serialStatus;
- /* NB: also ensures PendingEvents to be up to date */
- ZeroMemory(&serialStatus, sizeof(SERIAL_STATUS));
- if (!_get_commstatus(pComm, &serialStatus))
- {
- return FALSE;
- }
- return TRUE;
- }
- static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
- {
- if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
- {
- pComm->PendingEvents &= ~event; /* consumed */
- *pOutputMask |= event;
- }
- }
- /*
- * NB: see also: _set_wait_mask()
- */
- static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
- {
- assert(*pOutputMask == 0);
- EnterCriticalSection(&pComm->EventsLock);
- pComm->PendingEvents |= SERIAL_EV_FREERDP_WAITING;
- LeaveCriticalSection(&pComm->EventsLock);
- while (TRUE)
- {
- /* NB: EventsLock also used by _refresh_PendingEvents() */
- if (!_refresh_PendingEvents(pComm))
- {
- EnterCriticalSection(&pComm->EventsLock);
- pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
- LeaveCriticalSection(&pComm->EventsLock);
- return FALSE;
- }
- /* NB: ensure to leave the critical section before to return */
- EnterCriticalSection(&pComm->EventsLock);
- if (pComm->PendingEvents & SERIAL_EV_FREERDP_STOP)
- {
- pComm->PendingEvents &= ~SERIAL_EV_FREERDP_STOP;
- /* pOutputMask must remain empty but should
- * not have been modified.
- *
- * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
- */
- assert(*pOutputMask == 0);
- pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
- LeaveCriticalSection(&pComm->EventsLock);
- return TRUE;
- }
- _consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
- _consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
- _consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
- _consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
- _consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
- _consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
- _consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
- _consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
- _consume_event(pComm, pOutputMask, SERIAL_EV_RING);
- _consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
- LeaveCriticalSection(&pComm->EventsLock);
- /* NOTE: PendingEvents can be modified from now on but
- * not pOutputMask */
- if (*pOutputMask != 0)
- {
- /* at least an event occurred */
- EnterCriticalSection(&pComm->EventsLock);
- pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
- LeaveCriticalSection(&pComm->EventsLock);
- return TRUE;
- }
- /* waiting for a modification of PendingEvents.
- *
- * NOTE: previously used a semaphore but used
- * sem_timedwait() anyway. Finally preferred a simpler
- * solution with Sleep() without the burden of the
- * semaphore initialization and destroying.
- */
- Sleep(100); /* 100 ms */
- }
- }
- static BOOL _set_break_on(WINPR_COMM* pComm)
- {
- if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _set_break_off(WINPR_COMM* pComm)
- {
- if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _set_xoff(WINPR_COMM* pComm)
- {
- if (tcflow(pComm->fd, TCIOFF) < 0)
- {
- CommLog_Print(WLOG_WARN, "TCIOFF failure, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _set_xon(WINPR_COMM* pComm)
- {
- if (tcflow(pComm->fd, TCION) < 0)
- {
- CommLog_Print(WLOG_WARN, "TCION failure, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
- {
- UINT32 lines = 0;
- if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
- {
- CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, strerror(errno));
- SetLastError(ERROR_IO_DEVICE);
- return FALSE;
- }
- *pMask = 0;
- if (!(lines & TIOCM_DTR))
- *pMask |= SERIAL_DTR_STATE;
- if (!(lines & TIOCM_RTS))
- *pMask |= SERIAL_RTS_STATE;
- return TRUE;
- }
- static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize)
- {
- /* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */
- if (!pSize)
- return FALSE;
- *pSize = 0;
- return TRUE;
- }
- static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
- {
- BOOL result;
- DWORD nbBytesWritten = -1;
- /* FIXME: CommWriteFile uses a critical section, shall it be
- * interrupted?
- *
- * FIXME: see also _get_commstatus()'s WaitForImmediate boolean
- */
- result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
- assert(nbBytesWritten == 1);
- return result;
- }
- static BOOL _reset_device(WINPR_COMM* pComm)
- {
- /* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
- return TRUE;
- }
- static SERIAL_DRIVER _SerialSys = {
- .id = SerialDriverSerialSys,
- .name = _T("Serial.sys"),
- .set_baud_rate = _set_baud_rate,
- .get_baud_rate = _get_baud_rate,
- .get_properties = _get_properties,
- .set_serial_chars = _set_serial_chars,
- .get_serial_chars = _get_serial_chars,
- .set_line_control = _set_line_control,
- .get_line_control = _get_line_control,
- .set_handflow = _set_handflow,
- .get_handflow = _get_handflow,
- .set_timeouts = _set_timeouts,
- .get_timeouts = _get_timeouts,
- .set_dtr = _set_dtr,
- .clear_dtr = _clear_dtr,
- .set_rts = _set_rts,
- .clear_rts = _clear_rts,
- .get_modemstatus = _get_modemstatus,
- .set_wait_mask = _set_wait_mask,
- .get_wait_mask = _get_wait_mask,
- .wait_on_mask = _wait_on_mask,
- .set_queue_size = _set_queue_size,
- .purge = _purge,
- .get_commstatus = _get_commstatus,
- .set_break_on = _set_break_on,
- .set_break_off = _set_break_off,
- .set_xoff = _set_xoff,
- .set_xon = _set_xon,
- .get_dtrrts = _get_dtrrts,
- .config_size = _config_size,
- .immediate_char = _immediate_char,
- .reset_device = _reset_device,
- };
- SERIAL_DRIVER* SerialSys_s()
- {
- return &_SerialSys;
- }
- #endif /* __linux__ */
|