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.

905 lines
22 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dpserial.c
  6. * Content: Implementation of serial port service provider
  7. * History:
  8. * Date By Reason
  9. * ==== == ======
  10. * 4/10/96 kipo created it
  11. * 4/12/96 kipo updated for new interfaces
  12. * 4/15/96 kipo added msinternal
  13. * 5/22/96 kipo updated for new interfaces
  14. * 6/10/96 kipo updated for new interfaces
  15. * 6/10/96 kipo added modem support
  16. * 6/18/96 kipo use guid to choose serial/modem connection
  17. * 6/20/96 kipo updated for new interfaces
  18. * 6/21/96 kipo Bug #2078. Changed modem service provider GUID so it's not the
  19. * same as the DPlay 1.0 GUID, so games that are checking won't
  20. * put up their loopy modem-specific UI.
  21. * 6/21/96 kipo updated for latest interfaces; return error if message size is too big.
  22. * 6/22/96 kipo updated for latest interfaces; use connection data; return version
  23. * 6/23/96 kipo updated for latest service provider interfaces.
  24. * 6/24/96 kipo divide baud rate by 100 to conform to DPlay 1.0 usage.
  25. * 6/25/96 kipo added WINAPI prototypes and updated for DPADDRESS
  26. * 7/13/96 kipo added support for GetAddress() method.
  27. * 7/13/96 kipo don't print as many errors for invalid messages.
  28. * 8/10/96 kipo return DPERR_SESSIONLOST on write failures
  29. * 8/13/96 kipo added CRC
  30. * 8/21/96 kipo return a value for dwHeaderLength in caps
  31. * 9/07/96 kip changed latency and timeout values
  32. * 1/06/97 kipo updated for objects
  33. * 2/11/97 kipo pass player flags to GetAddress()
  34. * 2/11/97 kipo SPInit was needlessly clearing the dwFlags field of the
  35. * callback table.
  36. * 2/18/97 kipo allow multiple instances of service provider
  37. * 3/04/97 kipo updated debug output; make sure we linke with dplayx.dll
  38. * 4/08/97 kipo added support for separate modem and serial baud rates
  39. * 5/07/97 kipo added support for modem choice list
  40. * 5/23/97 kipo added support return status codes
  41. * 5/15/98 a-peterz When Write fails, return DPERR_NOCONNECTION (#23745)
  42. ***************************************************************************/
  43. #define INITGUID
  44. #include <windows.h>
  45. #include <windowsx.h>
  46. #include <objbase.h>
  47. #include <initguid.h>
  48. #include "dpf.h"
  49. #include "dplaysp.h"
  50. #include "comport.h"
  51. // macros
  52. #ifdef DEBUG
  53. #define DPF_ERRVAL(a, b) DPF( 0, DPF_MODNAME ": " a, b );
  54. #else
  55. #define DPF_ERRVAL(a, b)
  56. #endif
  57. // constants
  58. #define SPMINORVERSION 0x0000 // service provider-specific version number
  59. #define VERSIONNUMBER (DPSP_MAJORVERSION | SPMINORVERSION) // version number for service provider
  60. #define MESSAGETOKEN 0x2BAD // token to signify start of message
  61. #define MESSAGEHEADERLEN sizeof(MESSAGEHEADER) // size of message header
  62. #define MESSAGEMAXSIZEINT 0x0000FFFF // maximum size of an internal message
  63. #define MESSAGEMAXSIZEEXT (MESSAGEMAXSIZEINT - MESSAGEHEADERLEN) // maximum size of an external message
  64. typedef enum {
  65. NEWMESSAGESTATE = 0, // start reading a new message
  66. READHEADERSTATE, // read the message header
  67. READDATASTATE, // read the message data
  68. SKIPDATASTATE // skip the message data
  69. } MESSAGESTATE;
  70. // structures
  71. // message header
  72. typedef struct {
  73. WORD wToken; // message token
  74. WORD wMessageSize; // length of message
  75. WORD wMessageCRC; // CRC checksum value for message body
  76. WORD wHeaderCRC; // CRC checksum value for header
  77. } MESSAGEHEADER, *LPMESSAGEHEADER;
  78. // service provider context
  79. typedef struct {
  80. LPDPCOMPORT lpComPort; // pointer to com port data structure
  81. MESSAGESTATE msReadState; // current read state
  82. BYTE lpReadHeader[MESSAGEHEADERLEN]; // buffer for message header
  83. LPBYTE lpReadBuffer; // buffer for message data
  84. DWORD dwReadBufferSize; // size of message buffer in bytes
  85. DWORD dwReadCount; // no. bytes read into message buffer
  86. DWORD dwReadTotal; // no. total bytes to read into message buffer
  87. DWORD dwSkipCount; // no. bytes skipped to find message header
  88. LPDIRECTPLAYSP lpDPlay; // pointer to IDirectPlaySP needed to call back into DPlay
  89. } SPCONTEXT, *LPSPCONTEXT;
  90. // {0F1D6860-88D9-11cf-9C4E-00A0C905425E}
  91. DEFINE_GUID(DPSERIAL_GUID, // GUID for serial service provider
  92. 0xf1d6860, 0x88d9, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e);
  93. // {44EAA760-CB68-11cf-9C4E-00A0C905425E}
  94. DEFINE_GUID(DPMODEM_GUID, // GUID for modem service provider
  95. 0x44eaa760, 0xcb68, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e);
  96. /*
  97. * GetSPContext
  98. *
  99. * Get service provider context from DirectPlay.
  100. */
  101. #undef DPF_MODNAME
  102. #define DPF_MODNAME "GetSPContext"
  103. LPSPCONTEXT GetSPContext(LPDIRECTPLAYSP lpDPlay)
  104. {
  105. LPSPCONTEXT lpContext = NULL;
  106. DWORD dwContextSize = 0;
  107. HRESULT hr;
  108. // no dplay interface?
  109. if (lpDPlay == NULL)
  110. {
  111. DPF_ERR("DPlaySP interface is NULL!");
  112. goto FAILURE;
  113. }
  114. // get pointer to context from DPlay
  115. hr = lpDPlay->lpVtbl->GetSPData(lpDPlay, &lpContext, &dwContextSize, DPGET_LOCAL);
  116. if FAILED(hr)
  117. {
  118. DPF_ERRVAL("could not get context: 0x%08X", hr);
  119. goto FAILURE;
  120. }
  121. // make sure size is correct
  122. if (dwContextSize != sizeof(SPCONTEXT))
  123. {
  124. DPF_ERR("invalid context size!");
  125. goto FAILURE;
  126. }
  127. return (lpContext);
  128. FAILURE:
  129. return (NULL);
  130. }
  131. /*
  132. * SetupMessageHeader
  133. *
  134. * Initialize the service provider-specific header put
  135. * in front of every message.
  136. */
  137. #undef DPF_MODNAME
  138. #define DPF_MODNAME "SetupMessageHeader"
  139. HRESULT SetupMessageHeader(LPVOID pvMessage, DWORD dwMessageSize)
  140. {
  141. LPMESSAGEHEADER pMessageHeader = (LPMESSAGEHEADER) pvMessage;
  142. // make sure message will fit in header
  143. if (dwMessageSize > MESSAGEMAXSIZEINT)
  144. return (DPERR_SENDTOOBIG);
  145. // set message header
  146. pMessageHeader->wToken = (WORD) MESSAGETOKEN;
  147. // set message size
  148. pMessageHeader->wMessageSize = (WORD) dwMessageSize;
  149. // generate CRC for message body
  150. pMessageHeader->wMessageCRC = (WORD) GenerateCRC(((LPBYTE) pvMessage) + MESSAGEHEADERLEN,
  151. dwMessageSize - MESSAGEHEADERLEN);
  152. // generate CRC for message header
  153. pMessageHeader->wHeaderCRC = (WORD) GenerateCRC(pvMessage, MESSAGEHEADERLEN - sizeof(pMessageHeader->wHeaderCRC));
  154. return (DP_OK);
  155. }
  156. /*
  157. * GetMessageLength
  158. *
  159. * Check for valid message header and return length of message.
  160. */
  161. #undef DPF_MODNAME
  162. #define DPF_MODNAME "GetMessageLength"
  163. DWORD GetMessageLength(LPBYTE header)
  164. {
  165. LPMESSAGEHEADER pMessageHeader = (LPMESSAGEHEADER) header;
  166. DWORD byteCount;
  167. // check for token we put in front of every message
  168. if (pMessageHeader->wToken != MESSAGETOKEN)
  169. goto FAILURE;
  170. // check CRC for message header
  171. if (pMessageHeader->wHeaderCRC != (WORD) GenerateCRC(header, MESSAGEHEADERLEN - sizeof(pMessageHeader->wHeaderCRC)))
  172. goto FAILURE;
  173. // get length of message
  174. byteCount = pMessageHeader->wMessageSize;
  175. if (byteCount <= MESSAGEHEADERLEN)
  176. {
  177. DPF_ERRVAL("bad message size: %d", byteCount);
  178. goto FAILURE;
  179. }
  180. return (byteCount);
  181. FAILURE:
  182. return (0);
  183. }
  184. /*
  185. * SetupToReadMessage
  186. *
  187. * Create/resize buffer to fit length of message and initialize header.
  188. */
  189. #undef DPF_MODNAME
  190. #define DPF_MODNAME "SetupToReadMessage"
  191. BOOL SetupToReadMessage(LPSPCONTEXT lpContext)
  192. {
  193. // no buffer, so create one
  194. if (lpContext->lpReadBuffer == NULL)
  195. {
  196. lpContext->lpReadBuffer = GlobalAllocPtr(GHND, lpContext->dwReadTotal);
  197. if (lpContext->lpReadBuffer == NULL)
  198. {
  199. DPF_ERRVAL("could not create message buffer: %d", GetLastError());
  200. goto FAILURE;
  201. }
  202. lpContext->dwReadBufferSize = lpContext->dwReadTotal;
  203. }
  204. // existing buffer not big enough, so resize
  205. else if (lpContext->dwReadBufferSize < lpContext->dwReadTotal)
  206. {
  207. HANDLE h;
  208. h = GlobalReAllocPtr(lpContext->lpReadBuffer, lpContext->dwReadTotal, 0);
  209. if (h == NULL)
  210. {
  211. DPF_ERRVAL("could not reallocate message buffer: %d", GetLastError());
  212. goto FAILURE;
  213. }
  214. lpContext->lpReadBuffer = h;
  215. lpContext->dwReadBufferSize = lpContext->dwReadTotal;
  216. }
  217. // copy message header to buffer
  218. CopyMemory(lpContext->lpReadBuffer, lpContext->lpReadHeader, lpContext->dwReadCount);
  219. return (TRUE);
  220. FAILURE:
  221. return (FALSE);
  222. }
  223. /*
  224. * ReadRoutine
  225. *
  226. * Read bytes from COM port using a state machine to assemble a message.
  227. * When message is assembled, call back to DirectPlay to deliver it.
  228. */
  229. #undef DPF_MODNAME
  230. #define DPF_MODNAME "ReadRoutine"
  231. void ReadRoutine(LPDIRECTPLAYSP lpDPlay)
  232. {
  233. LPSPCONTEXT lpContext;
  234. DWORD byteCount;
  235. // get service provider context
  236. lpContext = GetSPContext(lpDPlay);
  237. if (lpContext == NULL)
  238. {
  239. DPF_ERR("invalid context!");
  240. return;
  241. }
  242. while (1)
  243. {
  244. switch (lpContext->msReadState)
  245. {
  246. // start reading a new message
  247. case NEWMESSAGESTATE:
  248. lpContext->dwReadCount = 0;
  249. lpContext->dwReadTotal = MESSAGEHEADERLEN;
  250. lpContext->msReadState = READHEADERSTATE;
  251. lpContext->dwSkipCount = 0;
  252. break;
  253. // read message header
  254. case READHEADERSTATE:
  255. byteCount = lpContext->lpComPort->Read(lpContext->lpComPort,
  256. &lpContext->lpReadHeader[lpContext->dwReadCount],
  257. lpContext->dwReadTotal - lpContext->dwReadCount);
  258. if (byteCount == 0)
  259. return;
  260. lpContext->dwReadCount += byteCount;
  261. if (lpContext->dwReadCount == lpContext->dwReadTotal) // got enough for a header
  262. {
  263. lpContext->dwReadTotal = GetMessageLength(lpContext->lpReadHeader); // see if it's real
  264. if (lpContext->dwReadTotal)
  265. {
  266. if (lpContext->dwSkipCount)
  267. DPF_ERRVAL("%d bytes skipped", lpContext->dwSkipCount);
  268. if (SetupToReadMessage(lpContext)) // prepare to read message
  269. lpContext->msReadState = READDATASTATE;
  270. else
  271. lpContext->msReadState = SKIPDATASTATE;
  272. }
  273. else // bad message header - reset
  274. {
  275. DWORD i;
  276. if (lpContext->dwSkipCount == 0)
  277. DPF_ERR("invalid message header - skipping bytes");
  278. lpContext->dwReadCount = MESSAGEHEADERLEN - 1; // throw away first byte and try again
  279. lpContext->dwReadTotal = MESSAGEHEADERLEN;
  280. lpContext->dwSkipCount += 1;
  281. for (i = 0; i < lpContext->dwReadCount; i++) // shuffle down one byte
  282. lpContext->lpReadHeader[i] = lpContext->lpReadHeader[i + 1];
  283. }
  284. }
  285. break;
  286. // read message data
  287. case READDATASTATE:
  288. byteCount = lpContext->lpComPort->Read(lpContext->lpComPort,
  289. &lpContext->lpReadBuffer[lpContext->dwReadCount],
  290. lpContext->dwReadTotal - lpContext->dwReadCount);
  291. if (byteCount == 0)
  292. return;
  293. lpContext->dwReadCount += byteCount;
  294. if (lpContext->dwReadCount == lpContext->dwReadTotal) // have read entire message
  295. {
  296. LPMESSAGEHEADER pMessageHeader;
  297. // check for CRC errors
  298. pMessageHeader = (LPMESSAGEHEADER) lpContext->lpReadBuffer;
  299. if (pMessageHeader->wMessageCRC != (WORD) GenerateCRC(lpContext->lpReadBuffer + MESSAGEHEADERLEN, lpContext->dwReadTotal - MESSAGEHEADERLEN))
  300. {
  301. DPF_ERR("Message dropped - CRC did not match!");
  302. }
  303. else
  304. {
  305. DPF(5, "%d byte message received", lpContext->dwReadTotal);
  306. // deliver message to DirectPlay
  307. lpContext->lpDPlay->lpVtbl->HandleMessage(lpContext->lpDPlay, // DirectPlay instance
  308. lpContext->lpReadBuffer + MESSAGEHEADERLEN, // pointer to message data
  309. lpContext->dwReadTotal - MESSAGEHEADERLEN, // length of message data
  310. NULL); // pointer to header (unused here)
  311. }
  312. lpContext->msReadState = NEWMESSAGESTATE; // go read next message
  313. }
  314. break;
  315. // skip message data
  316. case SKIPDATASTATE:
  317. DPF_ERR("Skipping data!");
  318. while (lpContext->lpComPort->Read(lpContext->lpComPort, &lpContext->lpReadHeader[0], 1)) // spin until entire message discarded
  319. {
  320. lpContext->dwReadCount += 1;
  321. if (lpContext->dwReadCount == lpContext->dwReadTotal)
  322. {
  323. lpContext->msReadState = NEWMESSAGESTATE;
  324. break;
  325. }
  326. }
  327. break;
  328. default:
  329. DPF_ERRVAL("bad read state: %d", lpContext->msReadState);
  330. break;
  331. }
  332. }
  333. }
  334. /*
  335. * SP_EnumSessions
  336. *
  337. * Broadcast a message to the network.
  338. */
  339. #undef DPF_MODNAME
  340. #define DPF_MODNAME "SP_EnumSessions"
  341. HRESULT WINAPI SP_EnumSessions(LPDPSP_ENUMSESSIONSDATA ped)
  342. {
  343. LPSPCONTEXT lpContext;
  344. DWORD byteCount;
  345. HRESULT hr;
  346. DPF(5,"entering SP_EnumSessions");
  347. // get service provider context
  348. lpContext = GetSPContext(ped->lpISP);
  349. if (lpContext == NULL)
  350. {
  351. DPF_ERR("invalid context!");
  352. hr = DPERR_NOINTERFACE;
  353. goto FAILURE;
  354. }
  355. // make connection
  356. hr = lpContext->lpComPort->Connect(lpContext->lpComPort, FALSE, ped->bReturnStatus);
  357. if FAILED(hr)
  358. {
  359. if (hr != DPERR_CONNECTING)
  360. DPF_ERRVAL("error making connection: 0x%08X", hr);
  361. goto FAILURE;
  362. }
  363. // see if connection has been lost
  364. if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
  365. {
  366. DPF_ERR("connection lost!");
  367. hr = DPERR_SESSIONLOST;
  368. goto FAILURE;
  369. }
  370. // setup the message
  371. hr = SetupMessageHeader(ped->lpMessage, ped->dwMessageSize);
  372. if FAILED(hr)
  373. {
  374. DPF_ERR("message too large!");
  375. goto FAILURE;
  376. }
  377. // send message
  378. byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, ped->lpMessage, ped->dwMessageSize, TRUE);
  379. if (byteCount != ped->dwMessageSize)
  380. {
  381. DPF(0, "error writing message: %d requested, %d actual", ped->dwMessageSize, byteCount);
  382. hr = DPERR_CONNECTIONLOST;
  383. goto FAILURE;
  384. }
  385. DPF(5, "%d byte enum sessions message sent", byteCount);
  386. return (DP_OK);
  387. FAILURE:
  388. return (hr);
  389. } // EnumSessions
  390. /*
  391. * SP_Send
  392. *
  393. * Send a message to a particular player or group.
  394. */
  395. #undef DPF_MODNAME
  396. #define DPF_MODNAME "SP_Send"
  397. HRESULT WINAPI SP_Send(LPDPSP_SENDDATA psd)
  398. {
  399. LPSPCONTEXT lpContext;
  400. DWORD byteCount;
  401. HRESULT hr;
  402. DPF(5,"entering SP_Send");
  403. // get service provider context
  404. lpContext = GetSPContext(psd->lpISP);
  405. if (lpContext == NULL)
  406. {
  407. DPF_ERR("invalid context!");
  408. hr = DPERR_NOINTERFACE;
  409. goto FAILURE;
  410. }
  411. // see if connection has been lost
  412. if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
  413. {
  414. DPF_ERR("connection lost!");
  415. hr = DPERR_SESSIONLOST;
  416. goto FAILURE;
  417. }
  418. // setup the message
  419. hr = SetupMessageHeader(psd->lpMessage, psd->dwMessageSize);
  420. if FAILED(hr)
  421. {
  422. DPF_ERR("message too large!");
  423. goto FAILURE;
  424. }
  425. // send message
  426. byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, psd->lpMessage, psd->dwMessageSize, TRUE);
  427. if (byteCount != psd->dwMessageSize)
  428. {
  429. DPF(0, "error writing message: %d requested, %d actual", psd->dwMessageSize, byteCount);
  430. hr = DPERR_CONNECTIONLOST;
  431. goto FAILURE;
  432. }
  433. DPF(5, "%d byte message sent", byteCount);
  434. return (DP_OK);
  435. FAILURE:
  436. return (hr);
  437. } // Send
  438. /*
  439. * SP_Reply
  440. *
  441. * Send a reply to a message.
  442. */
  443. #undef DPF_MODNAME
  444. #define DPF_MODNAME "SP_Reply"
  445. HRESULT WINAPI SP_Reply(LPDPSP_REPLYDATA prd)
  446. {
  447. LPSPCONTEXT lpContext;
  448. DWORD byteCount;
  449. HRESULT hr;
  450. DPF(5,"entering Reply");
  451. // get service provider context
  452. lpContext = GetSPContext(prd->lpISP);
  453. if (lpContext == NULL)
  454. {
  455. DPF_ERR("invalid context!");
  456. hr = DPERR_NOINTERFACE;
  457. goto FAILURE;
  458. }
  459. // see if connection has been lost
  460. if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
  461. {
  462. DPF_ERR("connection lost!");
  463. hr = DPERR_SESSIONLOST;
  464. goto FAILURE;
  465. }
  466. // setup the message
  467. hr = SetupMessageHeader(prd->lpMessage, prd->dwMessageSize);
  468. if FAILED(hr)
  469. {
  470. DPF_ERR("message too large!");
  471. goto FAILURE;
  472. }
  473. // send message
  474. byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, prd->lpMessage, prd->dwMessageSize, TRUE);
  475. if (byteCount != prd->dwMessageSize)
  476. {
  477. DPF(0, "error writing message: %d requested, %d actual", prd->dwMessageSize, byteCount);
  478. hr = DPERR_CONNECTIONLOST;
  479. goto FAILURE;
  480. }
  481. DPF(5, "%d byte reply message sent", byteCount);
  482. return (DP_OK);
  483. FAILURE:
  484. return (hr);
  485. } // Reply
  486. /*
  487. * SP_Open
  488. *
  489. * Open the service provider.
  490. */
  491. #undef DPF_MODNAME
  492. #define DPF_MODNAME "SP_Open"
  493. HRESULT WINAPI SP_Open(LPDPSP_OPENDATA pod)
  494. {
  495. LPSPCONTEXT lpContext;
  496. HRESULT hr;
  497. DPF(5,"entering Open");
  498. // get service provider context
  499. lpContext = GetSPContext(pod->lpISP);
  500. if (lpContext == NULL)
  501. {
  502. DPF_ERR("invalid context!");
  503. hr = DPERR_NOINTERFACE;
  504. goto FAILURE;
  505. }
  506. // make connection
  507. hr = lpContext->lpComPort->Connect(lpContext->lpComPort, pod->bCreate, pod->bReturnStatus);
  508. if FAILED(hr)
  509. {
  510. DPF_ERRVAL("error making connection: 0x%08X", hr);
  511. goto FAILURE;
  512. }
  513. return (DP_OK);
  514. FAILURE:
  515. return (hr);
  516. } // Open
  517. /*
  518. * SP_GetCaps
  519. *
  520. * Return capabilities of service provider.
  521. *
  522. * Only the fields that matter to this service provider have
  523. * to be set here, since all the fields are preset to
  524. * default values.
  525. */
  526. #undef DPF_MODNAME
  527. #define DPF_MODNAME "SP_GetCaps"
  528. HRESULT WINAPI SP_GetCaps(LPDPSP_GETCAPSDATA pcd)
  529. {
  530. LPSPCONTEXT lpContext;
  531. LPDPCAPS lpCaps;
  532. HRESULT hr;
  533. DPF(5,"entering GetCaps");
  534. // get service provider context
  535. lpContext = GetSPContext(pcd->lpISP);
  536. if (lpContext == NULL)
  537. {
  538. DPF_ERR("invalid context!");
  539. hr = DPERR_NOINTERFACE;
  540. goto FAILURE;
  541. }
  542. // make sure caps buffer is large enough
  543. lpCaps = pcd->lpCaps;
  544. if (lpCaps->dwSize < sizeof(DPCAPS))
  545. {
  546. DPF_ERR("caps buffer too small");
  547. hr = DPERR_BUFFERTOOSMALL;
  548. goto FAILURE;
  549. }
  550. // don't zero out caps as DPlay has pre-initialized some default caps for us
  551. lpCaps->dwSize = sizeof(DPCAPS);
  552. lpCaps->dwMaxBufferSize = MESSAGEMAXSIZEEXT; // return maximum external message size
  553. lpCaps->dwHeaderLength = MESSAGEHEADERLEN; // return size of message header
  554. lpCaps->dwFlags = 0; // have DPlay do the keep-alives
  555. lpCaps->dwLatency = 250; // todo - base these on baud rate ACK!!!
  556. lpCaps->dwTimeout = 2500;
  557. // if we have connected we can get the baud rate
  558. if (lpContext->lpComPort->GetHandle(lpContext->lpComPort))
  559. {
  560. DWORD dwBaudRate;
  561. // try to get baud rate
  562. hr = lpContext->lpComPort->GetBaudRate(lpContext->lpComPort, &dwBaudRate);
  563. if SUCCEEDED(hr)
  564. {
  565. lpCaps->dwHundredBaud = dwBaudRate / 100; // return baud rate in hundreds of baud
  566. }
  567. }
  568. return (DP_OK);
  569. FAILURE:
  570. return (hr);
  571. } // GetCaps
  572. /*
  573. * SP_GetAddress
  574. *
  575. * Return network address of a given player.
  576. *
  577. */
  578. #undef DPF_MODNAME
  579. #define DPF_MODNAME "SP_GetAddress"
  580. HRESULT WINAPI SP_GetAddress(LPDPSP_GETADDRESSDATA pga)
  581. {
  582. LPSPCONTEXT lpContext;
  583. HRESULT hr;
  584. DPF(5,"entering GetAddress");
  585. // get service provider context
  586. lpContext = GetSPContext(pga->lpISP);
  587. if (lpContext == NULL)
  588. {
  589. DPF_ERR("invalid context!");
  590. hr = DPERR_NOINTERFACE;
  591. goto FAILURE;
  592. }
  593. hr = lpContext->lpComPort->GetAddress(lpContext->lpComPort, pga->dwFlags, pga->lpAddress, pga->lpdwAddressSize);
  594. FAILURE:
  595. return (hr);
  596. } // GetAddress
  597. /*
  598. * SP_GetAddressChoices
  599. *
  600. * Return address choices for this service provider
  601. *
  602. */
  603. #undef DPF_MODNAME
  604. #define DPF_MODNAME "SP_GetAddressChoices"
  605. HRESULT WINAPI SP_GetAddressChoices(LPDPSP_GETADDRESSCHOICESDATA pga)
  606. {
  607. LPSPCONTEXT lpContext;
  608. HRESULT hr;
  609. DPF(5,"entering GetAddressChoices");
  610. // get service provider context
  611. lpContext = GetSPContext(pga->lpISP);
  612. if (lpContext == NULL)
  613. {
  614. DPF_ERR("invalid context!");
  615. hr = DPERR_NOINTERFACE;
  616. goto FAILURE;
  617. }
  618. hr = lpContext->lpComPort->GetAddressChoices(lpContext->lpComPort, pga->lpAddress, pga->lpdwAddressSize);
  619. FAILURE:
  620. return (hr);
  621. } // GetAddressChoices
  622. /*
  623. * SP_Shutdown
  624. *
  625. * Turn off all I/O on service provider and release all allocated
  626. * memory and resources.
  627. */
  628. #undef DPF_MODNAME
  629. #define DPF_MODNAME "SP_Shutdown"
  630. HRESULT WINAPI SP_ShutdownEx(LPDPSP_SHUTDOWNDATA psd)
  631. {
  632. LPSPCONTEXT lpContext;
  633. HRESULT hr;
  634. DPF(5,"entering Shutdown");
  635. // get service provider context
  636. lpContext = GetSPContext(psd->lpISP);
  637. if (lpContext == NULL)
  638. {
  639. DPF_ERR("invalid context!");
  640. hr = DPERR_NOINTERFACE;
  641. goto FAILURE;
  642. }
  643. if (lpContext->lpComPort)
  644. {
  645. lpContext->lpComPort->Dispose(lpContext->lpComPort);
  646. lpContext->lpComPort = NULL;
  647. }
  648. if (lpContext->lpReadBuffer)
  649. {
  650. GlobalFreePtr(lpContext->lpReadBuffer);
  651. lpContext->lpReadBuffer = NULL;
  652. }
  653. lpContext->lpDPlay = NULL;
  654. // OK to release DPLAYX.DLL
  655. gdwDPlaySPRefCount++;
  656. return (DP_OK);
  657. FAILURE:
  658. return (hr);
  659. } // Shutdown
  660. /*
  661. * SPInit
  662. *
  663. * This is the main entry point for the service provider. This should be
  664. * the only entry point exported from the DLL.
  665. *
  666. * Allocate any needed resources and return the supported callbacks.
  667. */
  668. #undef DPF_MODNAME
  669. #define DPF_MODNAME "SPInit"
  670. HRESULT WINAPI SPInit(LPSPINITDATA pid)
  671. {
  672. SPCONTEXT context;
  673. LPSPCONTEXT lpContext;
  674. LPDPSP_SPCALLBACKS lpcbTable;
  675. HRESULT hr;
  676. DPF(5,"entering SPInit");
  677. // check to make sure table is big enough
  678. lpcbTable = pid->lpCB;
  679. if (lpcbTable->dwSize < sizeof(DPSP_SPCALLBACKS)) // table not big enough
  680. {
  681. DPF_ERR("callback table too small");
  682. hr = DPERR_BUFFERTOOSMALL;
  683. goto FAILURE;
  684. }
  685. // initialize context
  686. ZeroMemory(&context, sizeof(SPCONTEXT));
  687. lpContext = &context;
  688. lpContext->msReadState = NEWMESSAGESTATE;
  689. lpContext->lpDPlay = pid->lpISP; // save pointer to IDPlaySP so we can pass it back later
  690. // check for correct GUID
  691. if (IsEqualGUID(pid->lpGuid, &DPSERIAL_GUID))
  692. {
  693. hr = NewSerial(pid->lpAddress, pid->dwAddressSize,
  694. lpContext->lpDPlay, ReadRoutine,
  695. &lpContext->lpComPort);
  696. }
  697. else if (IsEqualGUID(pid->lpGuid, &DPMODEM_GUID))
  698. {
  699. hr = NewModem(pid->lpAddress, pid->dwAddressSize,
  700. lpContext->lpDPlay, ReadRoutine,
  701. &lpContext->lpComPort);
  702. }
  703. else
  704. {
  705. DPF_ERR("unknown service provider GUID");
  706. hr = DPERR_INVALIDPARAM;
  707. }
  708. if FAILED(hr)
  709. {
  710. DPF_ERRVAL("error opening com port: 0x%08X", hr);
  711. goto FAILURE;
  712. }
  713. // return size of header we need on every message so
  714. // DirectPlay will leave room for it.
  715. pid->dwSPHeaderSize = MESSAGEHEADERLEN;
  716. // return version number so DirectPlay will treat us with respect
  717. pid->dwSPVersion = VERSIONNUMBER;
  718. // set up callbacks
  719. lpcbTable->dwSize = sizeof(DPSP_SPCALLBACKS); // MUST set the return size of the table
  720. lpcbTable->Send = SP_Send;
  721. lpcbTable->EnumSessions = SP_EnumSessions;
  722. lpcbTable->Reply = SP_Reply;
  723. lpcbTable->GetCaps = SP_GetCaps;
  724. lpcbTable->GetAddress = SP_GetAddress;
  725. lpcbTable->GetAddressChoices = SP_GetAddressChoices;
  726. lpcbTable->Open = SP_Open;
  727. lpcbTable->ShutdownEx = SP_ShutdownEx;
  728. // save context with DPlay so we can get it later
  729. hr = lpContext->lpDPlay->lpVtbl->SetSPData(lpContext->lpDPlay, lpContext, sizeof(SPCONTEXT), DPSET_LOCAL);
  730. if FAILED(hr)
  731. {
  732. DPF_ERRVAL("could not store context: 0x%08X", hr);
  733. goto FAILURE;
  734. }
  735. // make sure DPLAYX.DLL sticks around
  736. gdwDPlaySPRefCount++;
  737. return (DP_OK);
  738. FAILURE:
  739. return (hr);
  740. } // SPInit