Leaked source code of windows server 2003
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.

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