Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
14 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: serial.c
  6. * Content: Routines for serial I/O
  7. * History:
  8. * Date By Reason
  9. * ==== == ======
  10. * 6/10/96 kipo created it
  11. * 6/22/96 kipo added support for EnumConnectionData()
  12. * 6/25/96 kipo updated for DPADDRESS
  13. * 7/13/96 kipo added GetSerialAddress()
  14. * 7/16/96 kipo changed address types to be GUIDs instead of 4CC
  15. * 8/21/96 kipo move comport address into dplobby.h
  16. * 1/06/97 kipo updated for objects
  17. * 2/11/97 kipo pass player flags to GetAddress()
  18. * 2/18/97 kipo allow multiple instances of service provider
  19. * 3/17/97 kipo deal with errors returned by DialogBoxParam()
  20. * 5/07/97 kipo added support for modem choice list
  21. ***************************************************************************/
  22. #include <windows.h>
  23. #include <windowsx.h>
  24. #include "dplaysp.h"
  25. #include "comport.h"
  26. #include "resource.h"
  27. // constants
  28. typedef enum {
  29. ASCII_XON = 0x11,
  30. ASCII_XOFF = 0x13
  31. };
  32. // serial object
  33. typedef struct {
  34. DPCOMPORT comPort; // base object globals
  35. BOOL bHaveSettings; // set to TRUE after settings dialog has been displayed
  36. DPCOMPORTADDRESS settings; // settings to use
  37. } DPSERIAL, *LPDPSERIAL;
  38. // dialog choices for serial port settings
  39. static DWORD gComPorts[] = { 1, 2, 3, 4 };
  40. static DWORD gBaudRates[] = { CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
  41. CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400,
  42. CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 };
  43. static DWORD gStopBits[] = { ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS };
  44. static DWORD gParities[] = { NOPARITY, EVENPARITY, ODDPARITY, MARKPARITY };
  45. static DWORD gFlowControls[] = { DPCPA_NOFLOW, DPCPA_XONXOFFFLOW, DPCPA_RTSFLOW, DPCPA_DTRFLOW, DPCPA_RTSDTRFLOW };
  46. // globals
  47. // this is defined in dllmain.c
  48. extern HINSTANCE ghInstance;
  49. // this is defined in dpserial.c
  50. extern GUID DPSERIAL_GUID;
  51. // prototypes
  52. static HRESULT DisposeSerial(LPDPCOMPORT baseObject);
  53. static HRESULT ConnectSerial(LPDPCOMPORT baseObject, BOOL bWaitForConnection, BOOL bReturnStatus);
  54. static HRESULT DisconnectSerial(LPDPCOMPORT baseObject);
  55. static HRESULT GetSerialAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags,
  56. LPVOID lpAddress, LPDWORD lpdwAddressSize);
  57. static HRESULT GetSerialAddressChoices(LPDPCOMPORT baseObject,
  58. LPVOID lpAddress, LPDWORD lpdwAddressSize);
  59. static BOOL SetupConnection(HANDLE hCom, LPDPCOMPORTADDRESS portSettings);
  60. static BOOL FAR PASCAL EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize,
  61. LPCVOID lpData, LPVOID lpContext);
  62. static BOOL GetSerialSettings(HINSTANCE hInstance, HWND hWndParent, LPDPSERIAL globals);
  63. static UINT_PTR CALLBACK SettingsDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  64. static void InitDialog(HWND hDlg, LPDPCOMPORTADDRESS settings);
  65. static void GetSettingsFromDialog(HWND hDlg, LPDPCOMPORTADDRESS settings);
  66. static int ValueToIndex(LPDWORD buf, int bufLen, DWORD value);
  67. static void FillComboBox(HWND hDlg, int dlgItem, int startStr, int stopStr);
  68. /*
  69. * NewSerial
  70. *
  71. * Create new serial port object.
  72. */
  73. HRESULT NewSerial(LPVOID lpConnectionData, DWORD dwConnectionDataSize,
  74. LPDIRECTPLAYSP lpDPlay, LPREADROUTINE lpReadRoutine,
  75. LPDPCOMPORT *storage)
  76. {
  77. LPDPCOMPORT baseObject;
  78. LPDPSERIAL globals;
  79. HRESULT hr;
  80. // create base object with enough space for our globals
  81. hr = NewComPort(sizeof(DPSERIAL), lpDPlay, lpReadRoutine, &baseObject);
  82. if FAILED(hr)
  83. return (hr);
  84. // fill in methods we implement
  85. baseObject->Dispose = DisposeSerial;
  86. baseObject->Connect = ConnectSerial;
  87. baseObject->Disconnect = DisconnectSerial;
  88. baseObject->GetAddress = GetSerialAddress;
  89. baseObject->GetAddressChoices = GetSerialAddressChoices;
  90. // setup default settings
  91. globals = (LPDPSERIAL) baseObject;
  92. globals->settings.dwComPort = 1; // COM port to use (1-4)
  93. globals->settings.dwBaudRate = CBR_57600; // baud rate (100-256k)
  94. globals->settings.dwStopBits = ONESTOPBIT; // no. stop bits (1-2)
  95. globals->settings.dwParity = NOPARITY; // parity (none, odd, even, mark)
  96. globals->settings.dwFlowControl = DPCPA_RTSDTRFLOW; // flow control (none, xon/xoff, rts, dtr)
  97. // check for valid connection data
  98. if (lpConnectionData)
  99. {
  100. baseObject->lpDPlay->lpVtbl->EnumAddress(baseObject->lpDPlay, EnumAddressData,
  101. lpConnectionData, dwConnectionDataSize,
  102. globals);
  103. }
  104. // return object pointer
  105. *storage = baseObject;
  106. return (DP_OK);
  107. }
  108. /*
  109. * EnumConnectionData
  110. *
  111. * Search for valid connection data
  112. */
  113. static BOOL FAR PASCAL EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize,
  114. LPCVOID lpData, LPVOID lpContext)
  115. {
  116. LPDPSERIAL globals = (LPDPSERIAL) lpContext;
  117. LPDPCOMPORTADDRESS settings = (LPDPCOMPORTADDRESS) lpData;
  118. // this is a com port chunk
  119. if ( IsEqualGUID(lpguidDataType, &DPAID_ComPort) &&
  120. (dwDataSize == sizeof(DPCOMPORTADDRESS)) )
  121. {
  122. // make sure it's valid!
  123. if ((ValueToIndex(gComPorts, sizeof(gComPorts), settings->dwComPort) >= 0) &&
  124. (ValueToIndex(gBaudRates, sizeof(gBaudRates), settings->dwBaudRate) >= 0) &&
  125. (ValueToIndex(gStopBits, sizeof(gStopBits), settings->dwStopBits) >= 0) &&
  126. (ValueToIndex(gParities, sizeof(gParities), settings->dwParity) >= 0) &&
  127. (ValueToIndex(gFlowControls, sizeof(gFlowControls), settings->dwFlowControl) >= 0))
  128. {
  129. globals->settings = *settings; // copy the data
  130. globals->bHaveSettings = TRUE; // we have valid settings
  131. }
  132. }
  133. return (TRUE);
  134. }
  135. /*
  136. * DisposeSerial
  137. *
  138. * Dispose serial port object.
  139. */
  140. static HRESULT DisposeSerial(LPDPCOMPORT baseObject)
  141. {
  142. LPDPSERIAL globals = (LPDPSERIAL) baseObject;
  143. // make sure we are disconnected
  144. DisconnectSerial(baseObject);
  145. // free object
  146. GlobalFreePtr((HGLOBAL) baseObject);
  147. return (DP_OK);
  148. }
  149. /*
  150. * ConnectSerial
  151. *
  152. * Open serial port and configure based on user settings.
  153. */
  154. static HRESULT ConnectSerial(LPDPCOMPORT baseObject,
  155. BOOL bWaitForConnection, BOOL bReturnStatus)
  156. {
  157. LPDPSERIAL globals = (LPDPSERIAL) baseObject;
  158. HANDLE hCom;
  159. TCHAR portName[10];
  160. HRESULT hr;
  161. // see if com port is already connected
  162. hCom = baseObject->GetHandle(baseObject);
  163. if (hCom)
  164. return (DP_OK);
  165. // ask user for settings if we have not already
  166. if (!globals->bHaveSettings)
  167. {
  168. if (!GetSerialSettings(ghInstance, GetForegroundWindow(), globals))
  169. {
  170. hr = DPERR_USERCANCEL;
  171. goto Failure;
  172. }
  173. globals->bHaveSettings = TRUE;
  174. }
  175. // open specified com port
  176. CopyMemory(portName, "COM0", 5);
  177. portName[3] += (BYTE) globals->settings.dwComPort;
  178. hCom = CreateFile( portName,
  179. GENERIC_READ | GENERIC_WRITE,
  180. 0, /* comm devices must be opened w/exclusive-access */
  181. NULL, /* no security attrs */
  182. OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
  183. FILE_ATTRIBUTE_NORMAL |
  184. FILE_FLAG_OVERLAPPED, // overlapped I/O
  185. NULL /* hTemplate must be NULL for comm devices */
  186. );
  187. if (hCom == INVALID_HANDLE_VALUE)
  188. {
  189. hCom = NULL;
  190. hr = HRESULT_FROM_WIN32(GetLastError());
  191. goto Failure;
  192. }
  193. // configure com port to proper settings
  194. if (!SetupConnection(hCom, &globals->settings))
  195. {
  196. hr = HRESULT_FROM_WIN32(GetLastError());
  197. goto Failure;
  198. }
  199. // setup com port
  200. hr = baseObject->Setup(baseObject, hCom);
  201. if FAILED(hr)
  202. goto Failure;
  203. return (DP_OK);
  204. Failure:
  205. if (hCom)
  206. CloseHandle(hCom);
  207. return (hr);
  208. }
  209. /*
  210. * DisconnectSerial
  211. *
  212. * Close serial port.
  213. */
  214. static HRESULT DisconnectSerial(LPDPCOMPORT baseObject)
  215. {
  216. HANDLE hCom;
  217. HRESULT hr;
  218. hCom = baseObject->GetHandle(baseObject);
  219. // com port is already disconnected
  220. if (hCom == NULL)
  221. return (DP_OK);
  222. // shut down com port
  223. hr = baseObject->Shutdown(baseObject);
  224. // close com port
  225. CloseHandle(hCom);
  226. return (hr);
  227. }
  228. /*
  229. * SetupConnection
  230. *
  231. * Configure serial port with specified settings.
  232. */
  233. static BOOL SetupConnection(HANDLE hCom, LPDPCOMPORTADDRESS portSettings)
  234. {
  235. DCB dcb;
  236. dcb.DCBlength = sizeof(DCB);
  237. if (!GetCommState(hCom, &dcb))
  238. return (FALSE);
  239. // setup various port settings
  240. dcb.fBinary = TRUE;
  241. dcb.BaudRate = portSettings->dwBaudRate;
  242. dcb.ByteSize = 8;
  243. dcb.StopBits = (BYTE) portSettings->dwStopBits;
  244. dcb.Parity = (BYTE) portSettings->dwParity;
  245. if (portSettings->dwParity == NOPARITY)
  246. dcb.fParity = FALSE;
  247. else
  248. dcb.fParity = TRUE;
  249. // setup hardware flow control
  250. if ((portSettings->dwFlowControl == DPCPA_DTRFLOW) ||
  251. (portSettings->dwFlowControl == DPCPA_RTSDTRFLOW))
  252. {
  253. dcb.fOutxDsrFlow = TRUE;
  254. dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
  255. }
  256. else
  257. {
  258. dcb.fOutxDsrFlow = FALSE;
  259. dcb.fDtrControl = DTR_CONTROL_ENABLE;
  260. }
  261. if ((portSettings->dwFlowControl == DPCPA_RTSFLOW) ||
  262. (portSettings->dwFlowControl == DPCPA_RTSDTRFLOW))
  263. {
  264. dcb.fOutxCtsFlow = TRUE;
  265. dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
  266. }
  267. else
  268. {
  269. dcb.fOutxCtsFlow = FALSE;
  270. dcb.fRtsControl = RTS_CONTROL_ENABLE;
  271. }
  272. // setup software flow control
  273. if (portSettings->dwFlowControl == DPCPA_XONXOFFFLOW)
  274. {
  275. dcb.fInX = TRUE;
  276. dcb.fOutX = TRUE;
  277. }
  278. else
  279. {
  280. dcb.fInX = FALSE;
  281. dcb.fOutX = FALSE;
  282. }
  283. dcb.XonChar = ASCII_XON;
  284. dcb.XoffChar = ASCII_XOFF;
  285. dcb.XonLim = 100;
  286. dcb.XoffLim = 100;
  287. if (!SetCommState( hCom, &dcb ))
  288. return (FALSE);
  289. return (TRUE);
  290. }
  291. /*
  292. * GetSerialAddress
  293. *
  294. * Return current serial port info if available.
  295. */
  296. static HRESULT GetSerialAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags,
  297. LPVOID lpAddress, LPDWORD lpdwAddressSize)
  298. {
  299. LPDPSERIAL globals = (LPDPSERIAL) baseObject;
  300. HRESULT hResult;
  301. // no settings yet
  302. if (!globals->bHaveSettings)
  303. return (DPERR_UNAVAILABLE);
  304. hResult = baseObject->lpDPlay->lpVtbl->CreateAddress(baseObject->lpDPlay,
  305. &DPSERIAL_GUID, &DPAID_ComPort,
  306. &globals->settings, sizeof(DPCOMPORTADDRESS),
  307. lpAddress, lpdwAddressSize);
  308. return (hResult);
  309. }
  310. /*
  311. * GetSerialAddressChoices
  312. *
  313. * Return current serial address choices
  314. */
  315. static HRESULT GetSerialAddressChoices(LPDPCOMPORT baseObject,
  316. LPVOID lpAddress, LPDWORD lpdwAddressSize)
  317. {
  318. LPDPSERIAL globals = (LPDPSERIAL) baseObject;
  319. // currently the serial provider does not support any choices
  320. return (E_NOTIMPL);
  321. }
  322. /*
  323. * GetComPortSettings
  324. *
  325. * Displays a dialog to gather and return the COM port settings.
  326. */
  327. static BOOL GetSerialSettings(HINSTANCE hInstance, HWND hWndParent, LPDPSERIAL globals)
  328. {
  329. INT_PTR iResult;
  330. iResult = (INT_PTR)DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_SETTINGSDIALOG), hWndParent, SettingsDialog, (LPARAM) globals);
  331. return (iResult > 0);
  332. }
  333. /*
  334. * SettingsDialog
  335. *
  336. * The dialog callback routine to display and edit the COM port settings.
  337. */
  338. static UINT_PTR CALLBACK SettingsDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  339. {
  340. LPDPSERIAL globals = (LPDPSERIAL) GetWindowLongPtr(hDlg, DWLP_USER);
  341. HWND hWndCtl;
  342. BOOL msgHandled = FALSE;
  343. switch (msg)
  344. {
  345. case WM_INITDIALOG:
  346. // serial info pointer passed in lParam
  347. globals = (LPDPSERIAL) lParam;
  348. // save the globals with the window
  349. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) globals);
  350. hWndCtl = GetDlgItem(hDlg, IDC_COMPORT);
  351. // make sure our dialog item is there
  352. if (hWndCtl == NULL)
  353. {
  354. EndDialog(hDlg, FALSE);
  355. msgHandled = TRUE;
  356. }
  357. else
  358. {
  359. InitDialog(hDlg, &globals->settings); // setup our dialog
  360. SetFocus(hWndCtl); // focus on com port combo box
  361. msgHandled = FALSE; // keep windows from setting input focus for us
  362. }
  363. break;
  364. case WM_COMMAND:
  365. if (HIWORD(wParam) == 0)
  366. {
  367. switch (LOWORD(wParam))
  368. {
  369. case IDOK: // return settings
  370. GetSettingsFromDialog(hDlg, &globals->settings);
  371. EndDialog(hDlg, TRUE);
  372. msgHandled = TRUE;
  373. break;
  374. case IDCANCEL: // cancel
  375. EndDialog(hDlg, FALSE);
  376. msgHandled = TRUE;
  377. break;
  378. }
  379. }
  380. break;
  381. }
  382. return (msgHandled);
  383. }
  384. /*
  385. * InitDialog
  386. *
  387. * Initialize the dialog controls to display the given COM port settings.
  388. */
  389. static void InitDialog(HWND hDlg, LPDPCOMPORTADDRESS settings)
  390. {
  391. // fill dialog combo boxes with items from string table
  392. FillComboBox(hDlg, IDC_COMPORT, IDS_COM1, IDS_COM4);
  393. FillComboBox(hDlg, IDC_BAUDRATE, IDS_BAUD1, IDS_BAUD15);
  394. FillComboBox(hDlg, IDC_STOPBITS, IDS_STOPBIT1, IDS_STOPBIT3);
  395. FillComboBox(hDlg, IDC_PARITY, IDS_PARITY1, IDS_PARITY4);
  396. FillComboBox(hDlg, IDC_FLOW, IDS_FLOW1, IDS_FLOW5);
  397. // select default values in combo boxes
  398. SendDlgItemMessage(hDlg, IDC_COMPORT, CB_SETCURSEL,
  399. ValueToIndex(gComPorts, sizeof(gComPorts), settings->dwComPort), 0);
  400. SendDlgItemMessage(hDlg, IDC_BAUDRATE, CB_SETCURSEL,
  401. ValueToIndex(gBaudRates, sizeof(gBaudRates), settings->dwBaudRate), 0);
  402. SendDlgItemMessage(hDlg, IDC_STOPBITS, CB_SETCURSEL,
  403. ValueToIndex(gStopBits, sizeof(gStopBits), settings->dwStopBits), 0);
  404. SendDlgItemMessage(hDlg, IDC_PARITY, CB_SETCURSEL,
  405. ValueToIndex(gParities, sizeof(gParities), settings->dwParity), 0);
  406. SendDlgItemMessage(hDlg, IDC_FLOW, CB_SETCURSEL,
  407. ValueToIndex(gFlowControls, sizeof(gFlowControls), settings->dwFlowControl), 0);
  408. }
  409. /*
  410. * GetSettingsFromDialog
  411. *
  412. * Get the COM port settings from the dialog controls.
  413. */
  414. static void GetSettingsFromDialog(HWND hDlg, LPDPCOMPORTADDRESS settings)
  415. {
  416. INT_PTR index;
  417. index = SendDlgItemMessage(hDlg, IDC_COMPORT, CB_GETCURSEL, 0, 0);
  418. if (index == CB_ERR)
  419. return;
  420. settings->dwComPort = gComPorts[index];
  421. index = SendDlgItemMessage(hDlg, IDC_BAUDRATE, CB_GETCURSEL, 0, 0);
  422. if (index == CB_ERR)
  423. return;
  424. settings->dwBaudRate = gBaudRates[index];
  425. index = SendDlgItemMessage(hDlg, IDC_STOPBITS, CB_GETCURSEL, 0, 0);
  426. if (index == CB_ERR)
  427. return;
  428. settings->dwStopBits = gStopBits[index];
  429. index = SendDlgItemMessage(hDlg, IDC_PARITY, CB_GETCURSEL, 0, 0);
  430. if (index == CB_ERR)
  431. return;
  432. settings->dwParity = gParities[index];
  433. index = SendDlgItemMessage(hDlg, IDC_FLOW, CB_GETCURSEL, 0, 0);
  434. if (index == CB_ERR)
  435. return;
  436. settings->dwFlowControl = gFlowControls[index];
  437. }
  438. /*
  439. * FillComboBox
  440. *
  441. * Add the specified strings to the combo box.
  442. */
  443. #define MAXSTRINGSIZE 200
  444. static void FillComboBox(HWND hDlg, int dlgItem, int startStr, int stopStr)
  445. {
  446. int i;
  447. TCHAR str[MAXSTRINGSIZE];
  448. for (i = startStr; i <= stopStr; i++)
  449. {
  450. if (LoadString(ghInstance, i, str, MAXSTRINGSIZE))
  451. SendDlgItemMessage(hDlg, dlgItem, CB_ADDSTRING, (WPARAM) 0, (LPARAM) str);
  452. }
  453. }
  454. /*
  455. * ValueToIndex
  456. *
  457. * Convert a settings value to a combo box selection index.
  458. */
  459. static int ValueToIndex(LPDWORD buf, int bufLen, DWORD value)
  460. {
  461. int i;
  462. bufLen /= sizeof(DWORD);
  463. for (i = 0; i < bufLen; i++)
  464. if (buf[i] == value)
  465. return (i);
  466. return (-1);
  467. }