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.

678 lines
21 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. llcinfo.c
  5. Abstract:
  6. Includes set/get information primitives of the data link driver.
  7. Contents:
  8. LlcQueryInformation
  9. LlcSetInformation
  10. UpdateFunctionalAddress
  11. UpdateGroupAddress
  12. Author:
  13. Antti Saarenheimo (o-anttis) 17-MAY-1991
  14. Revision History:
  15. --*/
  16. #include <llc.h>
  17. //
  18. // We are using only a single state state machine defined
  19. // in IBM Token-Ring Architecture. On the other hand, DLC
  20. // API requires a state machine having primary and secondary
  21. // states. The secondary states are used only when the link is
  22. // open. These tables converts the single internal state to
  23. // the primary and secondary states.
  24. //
  25. UCHAR PrimaryStates[MAX_LLC_LINK_STATE] = {
  26. LLC_LINK_CLOSED, // LINK_CLOSED,
  27. LLC_DISCONNECTED, // DISCONNECTED,
  28. LLC_LINK_OPENING, // LINK_OPENING,
  29. LLC_DISCONNECTING, // DISCONNECTING,
  30. LLC_FRMR_SENT, // FRMR_SENT,
  31. LLC_LINK_OPENED, // LINK_OPENED,
  32. LLC_LINK_OPENED, // LOCAL_BUSY,
  33. LLC_LINK_OPENED, // REJECTION
  34. LLC_LINK_OPENED, // CHECKPOINTING
  35. LLC_LINK_OPENED, // CHKP_LOCAL_BUSY
  36. LLC_LINK_OPENED, // CHKP_REJECT
  37. LLC_RESETTING, // RESETTING
  38. LLC_LINK_OPENED, // REMOTE_BUSY
  39. LLC_LINK_OPENED, // LOCAL_REMOTE_BUSY
  40. LLC_LINK_OPENED, // REJECT_LOCAL_BUSY
  41. LLC_LINK_OPENED, // REJECT_REMOTE_BUSY
  42. LLC_LINK_OPENED, // CHKP_REJECT_LOCAL_BUSY
  43. LLC_LINK_OPENED, // CHKP_CLEARING
  44. LLC_LINK_OPENED, // CHKP_REJECT_CLEARING
  45. LLC_LINK_OPENED, // REJECT_LOCAL_REMOTE_BUSY
  46. LLC_FRMR_RECEIVED // FRMR_RECEIVED
  47. };
  48. //
  49. // Note: the local busy state must be set separately!
  50. //
  51. UCHAR SecondaryStates[MAX_LLC_LINK_STATE] = {
  52. LLC_NO_SECONDARY_STATE, // LINK_CLOSED,
  53. LLC_NO_SECONDARY_STATE, // DISCONNECTED,
  54. LLC_NO_SECONDARY_STATE, // LINK_OPENING,
  55. LLC_NO_SECONDARY_STATE, // DISCONNECTING,
  56. LLC_NO_SECONDARY_STATE, // FRMR_SENT,
  57. LLC_NO_SECONDARY_STATE, // LINK_OPENED,
  58. LLC_NO_SECONDARY_STATE, // LOCAL_BUSY,
  59. LLC_REJECTING, // REJECTION
  60. LLC_CHECKPOINTING, // CHECKPOINTING
  61. LLC_CHECKPOINTING, // CHKP_LOCAL_BUSY
  62. LLC_CHECKPOINTING|LLC_REJECTING, // CHKP_REJECT
  63. LLC_NO_SECONDARY_STATE, // RESETTING
  64. LLC_REMOTE_BUSY, // REMOTE_BUSY
  65. LLC_REMOTE_BUSY, // LOCAL_REMOTE_BUSY
  66. LLC_REJECTING, // REJECT_LOCAL_BUSY
  67. LLC_REJECTING|LLC_REMOTE_BUSY, // REJECT_REMOTE_BUSY
  68. LLC_CHECKPOINTING|LLC_REJECTING, // CHKP_REJECT_LOCAL_BUSY
  69. LLC_CHECKPOINTING|LLC_CLEARING, // CHKP_CLEARING
  70. LLC_CHECKPOINTING|LLC_CLEARING|LLC_REJECTING, // CHKP_REJECT_CLEARING
  71. LLC_REJECTING|LLC_REMOTE_BUSY, // REJECT_LOCAL_REMOTE_BUSY
  72. LLC_NO_SECONDARY_STATE // FRMR_RECEIVED
  73. };
  74. DLC_STATUS
  75. LlcQueryInformation(
  76. IN PVOID hObject,
  77. IN UINT InformationType,
  78. IN PLLC_QUERY_INFO_BUFFER pQuery,
  79. IN UINT QueryBufferSize
  80. )
  81. /*++
  82. Routine Description:
  83. Procedure returns the statistics or parameter information of
  84. the given LLC object.
  85. Arguments:
  86. hObject - LLC object
  87. InformationType - the requested information (NDIS, Statistics, params)
  88. pQuery - buffer for the queried parameters
  89. QueryBufferSize - size of the buffer
  90. Return Value:
  91. DLC_STATUS
  92. --*/
  93. {
  94. PVOID CopyBuffer = NULL; // no warnings when -W4
  95. UINT CopyLength = 0; // no warnings when -W4
  96. DLC_STATUS Status = STATUS_SUCCESS;
  97. PADAPTER_CONTEXT pAdapterContext;
  98. switch (InformationType) {
  99. case DLC_INFO_CLASS_STATISTICS:
  100. case DLC_INFO_CLASS_STATISTICS_RESET:
  101. switch (((PDATA_LINK)hObject)->Gen.ObjectType) {
  102. case LLC_DIRECT_OBJECT:
  103. //
  104. // We don't gather any staticstics for direct objects
  105. //
  106. CopyBuffer = &(((PLLC_STATION_OBJECT)hObject)->Statistics);
  107. CopyLength = sizeof(SAP_STATISTICS);
  108. break;
  109. case LLC_SAP_OBJECT:
  110. //
  111. // copy the SAP statistics, reset the old data if necessary.
  112. // We don't check the buffer, the upper part if responsible
  113. // of that.
  114. //
  115. CopyBuffer = &(((PLLC_SAP)hObject)->Statistics);
  116. CopyLength = sizeof(SAP_STATISTICS);
  117. break;
  118. case LLC_LINK_OBJECT:
  119. CopyBuffer = &(((PDATA_LINK)hObject)->Statistics);
  120. CopyLength = sizeof(LINK_STATION_STATISTICS);
  121. break;
  122. #if LLC_DBG
  123. default:
  124. LlcInvalidObjectType();
  125. #endif
  126. }
  127. //
  128. // Check the size of the receive buffers
  129. //
  130. if (CopyLength <= QueryBufferSize) {
  131. LlcMemCpy(pQuery->auchBuffer, CopyBuffer, CopyLength);
  132. //
  133. // Copy also the specific link station information
  134. //
  135. if (((PDATA_LINK)hObject)->Gen.ObjectType == LLC_LINK_OBJECT) {
  136. pQuery->LinkLog.uchLastCmdRespReceived = ((PDATA_LINK)hObject)->LastCmdOrRespReceived;
  137. pQuery->LinkLog.uchLastCmdRespTransmitted = ((PDATA_LINK)hObject)->LastCmdOrRespSent;
  138. pQuery->LinkLog.uchPrimaryState = PrimaryStates[((PDATA_LINK)hObject)->State];
  139. pQuery->LinkLog.uchSecondaryState = SecondaryStates[((PDATA_LINK)hObject)->State];
  140. //
  141. // We keep a separate state by whom the local
  142. // busy state has been set.
  143. //
  144. if (((PDATA_LINK)hObject)->Flags & DLC_LOCAL_BUSY_USER) {
  145. pQuery->LinkLog.uchSecondaryState |= LLC_LOCAL_BUSY_USER_SET;
  146. }
  147. if (((PDATA_LINK)hObject)->Flags & DLC_LOCAL_BUSY_BUFFER) {
  148. pQuery->LinkLog.uchSecondaryState |= LLC_LOCAL_BUSY_BUFFER_SET;
  149. }
  150. pQuery->LinkLog.uchSendStateVariable = ((PDATA_LINK)hObject)->Vs / (UCHAR)2;
  151. pQuery->LinkLog.uchReceiveStateVariable = ((PDATA_LINK)hObject)->Vr / (UCHAR)2;
  152. pQuery->LinkLog.uchLastNr = (UCHAR)(((PDATA_LINK)hObject)->Nr / 2);
  153. //
  154. // The lan header used by the link is in the same
  155. // format as the received lan headers
  156. //
  157. pQuery->LinkLog.cbLanHeader = (UCHAR)LlcCopyReceivedLanHeader(
  158. ((PLLC_STATION_OBJECT)hObject)->Gen.pLlcBinding,
  159. pQuery->LinkLog.auchLanHeader,
  160. ((PDATA_LINK)hObject)->auchLanHeader
  161. );
  162. }
  163. } else {
  164. //
  165. // The data will be lost, when it is reset
  166. //
  167. Status = DLC_STATUS_LOST_LOG_DATA;
  168. CopyLength = QueryBufferSize;
  169. }
  170. if (InformationType == DLC_INFO_CLASS_STATISTICS_RESET) {
  171. LlcZeroMem(CopyBuffer, CopyLength);
  172. }
  173. break;
  174. case DLC_INFO_CLASS_DLC_TIMERS:
  175. if (QueryBufferSize < sizeof( LLC_TICKS)) {
  176. //
  177. // This is a wrong error message, but there is no better one
  178. //
  179. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  180. }
  181. LlcMemCpy(&pQuery->Timer,
  182. &((PBINDING_CONTEXT)hObject)->pAdapterContext->ConfigInfo.TimerTicks,
  183. sizeof(LLC_TICKS)
  184. );
  185. break;
  186. case DLC_INFO_CLASS_DIR_ADAPTER:
  187. if (QueryBufferSize < sizeof(LLC_ADAPTER_INFO)) {
  188. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  189. }
  190. pAdapterContext = ((PBINDING_CONTEXT)hObject)->pAdapterContext;
  191. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  192. pQuery->Adapter.auchFunctionalAddress,
  193. ((PBINDING_CONTEXT)hObject)->Functional.auchAddress,
  194. 4
  195. );
  196. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  197. pQuery->Adapter.auchGroupAddress,
  198. (PUCHAR)&((PBINDING_CONTEXT)hObject)->ulBroadcastAddress,
  199. 4
  200. );
  201. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  202. pQuery->Adapter.auchNodeAddress,
  203. pAdapterContext->Adapter.Node.auchAddress,
  204. 6
  205. );
  206. pQuery->Adapter.usMaxFrameSize = (USHORT)pAdapterContext->MaxFrameSize;
  207. pQuery->Adapter.ulLinkSpeed = pAdapterContext->LinkSpeed;
  208. if (pAdapterContext->NdisMedium == NdisMedium802_3) {
  209. pQuery->Adapter.usAdapterType = 0x0100; // Ethernet type in OS/2
  210. } else if (pAdapterContext->NdisMedium == NdisMediumFddi) {
  211. //
  212. // NOTE: Using a currently free value to indicate FDDI
  213. //
  214. pQuery->Adapter.usAdapterType = 0x0200;
  215. } else {
  216. //
  217. // All token-ring adapters use type "IBM tr 16/4 Adapter A",
  218. //
  219. pQuery->Adapter.usAdapterType = 0x0040;
  220. }
  221. break;
  222. case DLC_INFO_CLASS_PERMANENT_ADDRESS:
  223. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  224. pQuery->PermanentAddress,
  225. ((PBINDING_CONTEXT)hObject)->pAdapterContext->PermanentAddress,
  226. 6
  227. );
  228. break;
  229. default:
  230. return DLC_STATUS_INVALID_COMMAND;
  231. }
  232. return Status;
  233. }
  234. DLC_STATUS
  235. LlcSetInformation(
  236. IN PVOID hObject,
  237. IN UINT InformationType,
  238. IN PLLC_SET_INFO_BUFFER pSetInfo,
  239. IN UINT ParameterBufferSize
  240. )
  241. /*++
  242. Routine Description:
  243. Procedure sets different parameter sets to the link station objects.
  244. Arguments:
  245. hObject - LLC object
  246. InformationType - the requested information (NDIS, Statistics, params)
  247. ParameterBuffer - buffer for the queried parameters
  248. ParameterBufferSize - size of the buffer
  249. Special:
  250. Must be called IRQL < DPC (at least when broadcast addresses
  251. are modifiled)
  252. Return Value:
  253. --*/
  254. {
  255. DLC_STATUS Status = STATUS_SUCCESS;
  256. TR_BROADCAST_ADDRESS TempFunctional;
  257. //
  258. // There is only one high level functions, but InformationType
  259. // and the destination station type defines the actual changed
  260. // information.
  261. //
  262. ASSUME_IRQL(DISPATCH_LEVEL);
  263. switch (InformationType) {
  264. case DLC_INFO_CLASS_LINK_STATION:
  265. switch (((PDATA_LINK)hObject)->Gen.ObjectType) {
  266. case LLC_LINK_OBJECT:
  267. if (ParameterBufferSize < sizeof(DLC_LINK_PARAMETERS)) {
  268. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  269. }
  270. Status = CheckLinkParameters(&pSetInfo->LinkParms);
  271. if (Status != STATUS_SUCCESS) {
  272. return Status;
  273. }
  274. ACQUIRE_SPIN_LOCK(&(((PLLC_GENERIC_OBJECT)hObject)->pAdapterContext->SendSpinLock));
  275. SetLinkParameters((PDATA_LINK)hObject, pSetInfo->auchBuffer);
  276. RELEASE_SPIN_LOCK(&(((PLLC_GENERIC_OBJECT)hObject)->pAdapterContext->SendSpinLock));
  277. break;
  278. case LLC_SAP_OBJECT:
  279. if (ParameterBufferSize < sizeof(DLC_LINK_PARAMETERS)) {
  280. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  281. }
  282. Status = CheckLinkParameters(&pSetInfo->LinkParms);
  283. if (Status != STATUS_SUCCESS) {
  284. return Status;
  285. }
  286. CopyLinkParameters((PUCHAR)&((PLLC_SAP)hObject)->DefaultParameters,
  287. (PUCHAR)&pSetInfo->LinkParms,
  288. (PUCHAR)&DefaultParameters
  289. );
  290. break;
  291. default:
  292. return DLC_STATUS_INVALID_STATION_ID;
  293. }
  294. break;
  295. case DLC_INFO_CLASS_DLC_TIMERS:
  296. if (ParameterBufferSize < sizeof(LLC_TICKS)) {
  297. return DLC_STATUS_INVALID_BUFFER_LENGTH;
  298. }
  299. //
  300. // We will copy all non-zero timer tick values from the
  301. // the given buffer.
  302. //
  303. CopyNonZeroBytes((PUCHAR)&((PBINDING_CONTEXT)hObject)->pAdapterContext->ConfigInfo.TimerTicks,
  304. (PUCHAR)&pSetInfo->Timers,
  305. (PUCHAR)&((PBINDING_CONTEXT)hObject)->pAdapterContext->ConfigInfo.TimerTicks,
  306. sizeof(LLC_TICKS)
  307. );
  308. break;
  309. case DLC_INFO_CLASS_SET_GROUP:
  310. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  311. (PUCHAR)&((PBINDING_CONTEXT)hObject)->ulBroadcastAddress,
  312. pSetInfo->auchGroupAddress,
  313. sizeof(TR_BROADCAST_ADDRESS)
  314. );
  315. Status = UpdateGroupAddress(((PBINDING_CONTEXT)hObject)->pAdapterContext,
  316. (PBINDING_CONTEXT)hObject
  317. );
  318. break;
  319. case DLC_INFO_CLASS_RESET_FUNCTIONAL:
  320. case DLC_INFO_CLASS_SET_FUNCTIONAL:
  321. SwapMemCpy(((PBINDING_CONTEXT)hObject)->SwapCopiedLanAddresses,
  322. TempFunctional.auchAddress,
  323. pSetInfo->auchFunctionalAddress,
  324. sizeof(TR_BROADCAST_ADDRESS)
  325. );
  326. //
  327. // We have now swapped the bits to ethernet format,
  328. // Highest bit is now 0x01... and lowest ones are ..30,
  329. // Reset the bits if highest (0x01) is set and set
  330. // them if it is zero, but do not hange yje orginal
  331. // bits: 31,1,0 that are now mapped to ethernet format.
  332. //
  333. if (InformationType == DLC_INFO_CLASS_SET_FUNCTIONAL) {
  334. ((PBINDING_CONTEXT)hObject)->Functional.ulAddress |= TempFunctional.ulAddress;
  335. } else {
  336. ((PBINDING_CONTEXT)hObject)->Functional.ulAddress &= ~TempFunctional.ulAddress;
  337. }
  338. ((PBINDING_CONTEXT)hObject)->ulFunctionalZeroBits = ~((PBINDING_CONTEXT)hObject)->Functional.ulAddress;
  339. Status = UpdateFunctionalAddress(((PBINDING_CONTEXT)hObject)->pAdapterContext);
  340. break;
  341. case DLC_INFO_CLASS_SET_MULTICAST:
  342. memcpy(&((PBINDING_CONTEXT)hObject)->usBroadcastAddress,
  343. pSetInfo->auchBuffer,
  344. 6
  345. );
  346. Status = UpdateFunctionalAddress(((PBINDING_CONTEXT)hObject)->pAdapterContext);
  347. break;
  348. default:
  349. return DLC_STATUS_INVALID_COMMAND;
  350. }
  351. return Status;
  352. }
  353. DLC_STATUS
  354. UpdateFunctionalAddress(
  355. IN PADAPTER_CONTEXT pAdapterContext
  356. )
  357. /*++
  358. Routine Description:
  359. Procedure first makes inclusive or between the functionl address of
  360. this process context and the functional address defined
  361. for all bindings and then saves the new functional address to NDIS.
  362. The NT mutex makes this operation atomic.
  363. Arguments:
  364. pAdapterContext - LLC context of the current adapter
  365. Return Value:
  366. --*/
  367. {
  368. UCHAR aMulticastList[LLC_MAX_MULTICAST_ADDRESS * 6];
  369. TR_BROADCAST_ADDRESS NewFunctional;
  370. UINT MulticastListSize;
  371. ULONG CurrentMulticast;
  372. PBINDING_CONTEXT pBinding;
  373. UINT i;
  374. NDIS_STATUS Status;
  375. ASSUME_IRQL(DISPATCH_LEVEL);
  376. NewFunctional.ulAddress = 0;
  377. //
  378. // We cannot be setting several functional addresses simultanously!
  379. //
  380. RELEASE_DRIVER_LOCK();
  381. ASSUME_IRQL(PASSIVE_LEVEL);
  382. KeWaitForSingleObject(&NdisAccessMutex, UserRequest, KernelMode, FALSE, NULL);
  383. ACQUIRE_DRIVER_LOCK();
  384. //
  385. // The access to global data structures must be procted by
  386. // the spin lock.
  387. //
  388. ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
  389. for (pBinding = pAdapterContext->pBindings; pBinding; pBinding = pBinding->pNext) {
  390. NewFunctional.ulAddress |= pBinding->Functional.ulAddress;
  391. }
  392. RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase);
  393. if ((pAdapterContext->NdisMedium == NdisMedium802_3)
  394. || (pAdapterContext->NdisMedium == NdisMediumFddi)) {
  395. //
  396. // Each bit in the functional address is translated
  397. // to a ethernet multicast address.
  398. // The bit order within byte has already been swapped.
  399. //
  400. CurrentMulticast = 1;
  401. MulticastListSize = 0;
  402. for (i = 0; i < 31; i++) {
  403. if (CurrentMulticast & NewFunctional.ulAddress) {
  404. aMulticastList[MulticastListSize] = 0x03;
  405. aMulticastList[MulticastListSize + 1] = 0;
  406. LlcMemCpy(&aMulticastList[MulticastListSize + 2],
  407. &CurrentMulticast,
  408. sizeof(CurrentMulticast)
  409. );
  410. MulticastListSize += 6;
  411. }
  412. CurrentMulticast <<= 1;
  413. }
  414. //
  415. // Add the group addresses of all bindings behind
  416. // the functional addresses in the table.
  417. //
  418. for (pBinding = pAdapterContext->pBindings; pBinding; pBinding = pBinding->pNext) {
  419. //
  420. // We may drop some group addresses, but I don't expect that
  421. // it will ever happen (i don't know anybody using tr
  422. // group address, the possibility to have all functional
  423. // address bits in use and more than one group address in system
  424. // is almost impossible)
  425. //
  426. if (pBinding->ulBroadcastAddress != 0
  427. && MulticastListSize < LLC_MAX_MULTICAST_ADDRESS * 6) {
  428. //
  429. // Set the default bits in the group address,
  430. // but use ethernet bit order.
  431. //
  432. LlcMemCpy(&aMulticastList[MulticastListSize],
  433. &pBinding->usBroadcastAddress,
  434. 6
  435. );
  436. MulticastListSize += 6;
  437. }
  438. }
  439. RELEASE_DRIVER_LOCK();
  440. Status = SetNdisParameter(pAdapterContext,
  441. (pAdapterContext->NdisMedium == NdisMediumFddi)
  442. ? OID_FDDI_LONG_MULTICAST_LIST
  443. : OID_802_3_MULTICAST_LIST,
  444. aMulticastList,
  445. MulticastListSize
  446. );
  447. } else {
  448. //
  449. // The functional address bit (bit 0) must be always reset!
  450. //
  451. NewFunctional.auchAddress[0] &= ~0x80;
  452. RELEASE_DRIVER_LOCK();
  453. Status = SetNdisParameter(pAdapterContext,
  454. OID_802_5_CURRENT_FUNCTIONAL,
  455. &NewFunctional,
  456. sizeof(NewFunctional)
  457. );
  458. }
  459. ASSUME_IRQL(PASSIVE_LEVEL);
  460. KeReleaseMutex(&NdisAccessMutex, FALSE);
  461. ACQUIRE_DRIVER_LOCK();
  462. if (Status != STATUS_SUCCESS) {
  463. return DLC_STATUS_INVALID_FUNCTIONAL_ADDRESS;
  464. }
  465. return STATUS_SUCCESS;
  466. }
  467. DLC_STATUS
  468. UpdateGroupAddress(
  469. IN PADAPTER_CONTEXT pAdapterContext,
  470. IN PBINDING_CONTEXT pBindingContext
  471. )
  472. /*++
  473. Routine Description:
  474. Procedure updates token-ring' group address.
  475. It is converted automatically multicast address
  476. on the ethernet.
  477. Arguments:
  478. pAdapterContext - describes adapter to update group addresses for
  479. pBindingContext - describes binding context
  480. Return Value:
  481. DLC_STATUS
  482. --*/
  483. {
  484. NDIS_STATUS Status;
  485. ASSUME_IRQL(DISPATCH_LEVEL);
  486. if ((pAdapterContext->NdisMedium == NdisMedium802_3)
  487. || (pAdapterContext->NdisMedium == NdisMediumFddi)) {
  488. PUCHAR pMulticastAddress = (PUCHAR)&pBindingContext->usBroadcastAddress;
  489. pMulticastAddress[0] = 0x03;
  490. pMulticastAddress[1] = 0;
  491. pMulticastAddress[2] |= 1;
  492. //
  493. // Because functional and group addresses are mapped into
  494. // the multicast addresses, the updated global multicast list
  495. // must inlcude both address types of all bindings,
  496. //
  497. Status = UpdateFunctionalAddress(pAdapterContext);
  498. return Status;
  499. } else {
  500. PUCHAR pGroupAddress = (PUCHAR)&pBindingContext->usBroadcastAddress;
  501. pGroupAddress[0] = 0xC0;
  502. pGroupAddress[1] = 0;
  503. //
  504. // The group/functional address bit must be always set!
  505. //
  506. pGroupAddress[2] |= 0x80;
  507. RELEASE_DRIVER_LOCK();
  508. Status = SetNdisParameter(pAdapterContext,
  509. OID_802_5_CURRENT_GROUP,
  510. &pGroupAddress[2],
  511. 4
  512. );
  513. ACQUIRE_DRIVER_LOCK();
  514. //
  515. // The error status code is wrong, but IBM has defined no
  516. // error code for this case.
  517. //
  518. if (Status != STATUS_SUCCESS) {
  519. return DLC_STATUS_INVALID_FUNCTIONAL_ADDRESS;
  520. } else {
  521. return STATUS_SUCCESS;
  522. }
  523. }
  524. }