02b740a549a7a524fa1c6a5a35ddc0c4e6c314ca
[reactos.git] / reactos / drivers / net / ndis / ndis / miniport.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NDIS library
4 * FILE: ndis/miniport.c
5 * PURPOSE: Routines used by NDIS miniport drivers
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Vizzini (vizzini@plasmic.com)
8 * REVISIONS:
9 * CSH 01/08-2000 Created
10 * 8/20/2003 vizzini - DMA support
11 */
12 #include <miniport.h>
13 #include <protocol.h>
14 #ifdef DBG
15 #include <buffer.h>
16 #endif /* DBG */
17
18 /* Root of the scm database */
19 #define SERVICES_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
20
21 /* prefix for device object registration */
22 #define DEVICE_ROOT L"\\Device\\"
23
24 /*
25 * Define to 1 to get a debugger breakpoint at the end of NdisInitializeWrapper
26 * for each new miniport starting up
27 */
28 #define BREAK_ON_MINIPORT_INIT 0
29
30 /*
31 * This has to be big enough to hold the results of querying the Route value
32 * from the Linkage key. Please re-code me to determine this dynamically.
33 */
34 #define ROUTE_DATA_SIZE 256
35
36 /* Number of media we know */
37 #define MEDIA_ARRAY_SIZE 15
38
39 static NDIS_MEDIUM MediaArray[MEDIA_ARRAY_SIZE] = {
40 NdisMedium802_3,
41 NdisMedium802_5,
42 NdisMediumFddi,
43 NdisMediumWan,
44 NdisMediumLocalTalk,
45 NdisMediumDix,
46 NdisMediumArcnetRaw,
47 NdisMediumArcnet878_2,
48 NdisMediumAtm,
49 NdisMediumWirelessWan,
50 NdisMediumIrda,
51 NdisMediumBpc,
52 NdisMediumCoWan,
53 NdisMedium1394,
54 NdisMediumMax
55 };
56
57 /* global list and lock of Miniports NDIS has registered */
58 LIST_ENTRY MiniportListHead;
59 KSPIN_LOCK MiniportListLock;
60
61 /* global list and lock of adapters NDIS has registered */
62 LIST_ENTRY AdapterListHead;
63 KSPIN_LOCK AdapterListLock;
64
65 /* global list and lock of orphan adapters waiting to be claimed by a miniport */
66 LIST_ENTRY OrphanAdapterListHead;
67 KSPIN_LOCK OrphanAdapterListLock;
68
69
70 #ifdef DBG
71 VOID
72 MiniDisplayPacket(
73 PNDIS_PACKET Packet)
74 {
75 #if 0
76 ULONG i, Length;
77 UCHAR Buffer[64];
78 if ((DebugTraceLevel | DEBUG_PACKET) > 0) {
79 Length = CopyPacketToBuffer(
80 (PUCHAR)&Buffer,
81 Packet,
82 0,
83 64);
84
85 DbgPrint("*** PACKET START ***");
86
87 for (i = 0; i < Length; i++) {
88 if (i % 12 == 0)
89 DbgPrint("\n%04X ", i);
90 DbgPrint("%02X ", Buffer[i]);
91 }
92
93 DbgPrint("*** PACKET STOP ***\n");
94 }
95 #endif
96 }
97 #endif /* DBG */
98
99
100 VOID
101 MiniIndicateData(
102 PLOGICAL_ADAPTER Adapter,
103 NDIS_HANDLE MacReceiveContext,
104 PVOID HeaderBuffer,
105 UINT HeaderBufferSize,
106 PVOID LookaheadBuffer,
107 UINT LookaheadBufferSize,
108 UINT PacketSize)
109 /*
110 * FUNCTION: Indicate received data to bound protocols
111 * ARGUMENTS:
112 * Adapter = Pointer to logical adapter
113 * MacReceiveContext = MAC receive context handle
114 * HeaderBuffer = Pointer to header buffer
115 * HeaderBufferSize = Size of header buffer
116 * LookaheadBuffer = Pointer to lookahead buffer
117 * LookaheadBufferSize = Size of lookahead buffer
118 * PacketSize = Total size of received packet
119 */
120 {
121 KIRQL OldIrql;
122 PLIST_ENTRY CurrentEntry;
123 PADAPTER_BINDING AdapterBinding;
124
125 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called. Adapter (0x%X) HeaderBuffer (0x%X) "
126 "HeaderBufferSize (0x%X) LookaheadBuffer (0x%X) LookaheadBufferSize (0x%X).\n",
127 Adapter, HeaderBuffer, HeaderBufferSize, LookaheadBuffer, LookaheadBufferSize));
128
129 #ifdef DBG
130 #if 0
131 if ((DebugTraceLevel | DEBUG_PACKET) > 0) {
132 ULONG i, Length;
133 PUCHAR p;
134
135 DbgPrint("*** RECEIVE PACKET START ***\n");
136 DbgPrint("HEADER:");
137 p = HeaderBuffer;
138 for (i = 0; i < HeaderBufferSize; i++) {
139 if (i % 16 == 0)
140 DbgPrint("\n%04X ", i);
141 DbgPrint("%02X ", *p);
142 (ULONG_PTR)p += 1;
143 }
144
145 DbgPrint("\nFRAME:");
146
147 p = LookaheadBuffer;
148 Length = (LookaheadBufferSize < 64)? LookaheadBufferSize : 64;
149 for (i = 0; i < Length; i++) {
150 if (i % 16 == 0)
151 DbgPrint("\n%04X ", i);
152 DbgPrint("%02X ", *p);
153 (ULONG_PTR)p += 1;
154 }
155
156 DbgPrint("\n*** RECEIVE PACKET STOP ***\n");
157 }
158 #endif
159 #endif /* DBG */
160
161 KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
162 CurrentEntry = Adapter->ProtocolListHead.Flink;
163
164 if (CurrentEntry == &Adapter->ProtocolListHead) {
165 NDIS_DbgPrint(DEBUG_MINIPORT, ("WARNING: No upper protocol layer.\n"));
166 }
167
168 while (CurrentEntry != &Adapter->ProtocolListHead) {
169 AdapterBinding = CONTAINING_RECORD(CurrentEntry,
170 ADAPTER_BINDING,
171 AdapterListEntry);
172
173 KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
174
175 (*AdapterBinding->ProtocolBinding->Chars.u4.ReceiveHandler)(
176 AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
177 MacReceiveContext,
178 HeaderBuffer,
179 HeaderBufferSize,
180 LookaheadBuffer,
181 LookaheadBufferSize,
182 PacketSize);
183
184 KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
185
186 CurrentEntry = CurrentEntry->Flink;
187 }
188 KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
189 }
190
191
192 VOID STDCALL
193 MiniIndicateReceivePacket(
194 IN NDIS_HANDLE Miniport,
195 IN PPNDIS_PACKET PacketArray,
196 IN UINT NumberOfPackets)
197 /*
198 * FUNCTION: receives miniport packet array indications
199 * ARGUMENTS:
200 * Miniport: Miniport handle for the adapter
201 * PacketArray: pointer to a list of packet pointers to indicate
202 * NumberOfPackets: number of packets to indicate
203 */
204 {
205 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
206 }
207
208
209 VOID
210 MiniEthReceiveComplete(
211 IN PETH_FILTER Filter)
212 /*
213 * FUNCTION: Receive indication complete function for Ethernet devices
214 * ARGUMENTS:
215 * Filter = Pointer to Ethernet filter
216 */
217 {
218 KIRQL OldIrql;
219 PLIST_ENTRY CurrentEntry;
220 PLOGICAL_ADAPTER Adapter;
221 PADAPTER_BINDING AdapterBinding;
222
223 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
224
225 Adapter = (PLOGICAL_ADAPTER)Filter->Miniport;
226
227 KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
228 CurrentEntry = Adapter->ProtocolListHead.Flink;
229 while (CurrentEntry != &Adapter->ProtocolListHead) {
230 AdapterBinding = CONTAINING_RECORD(CurrentEntry,
231 ADAPTER_BINDING,
232 AdapterListEntry);
233
234 KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
235
236 (*AdapterBinding->ProtocolBinding->Chars.ReceiveCompleteHandler)(
237 AdapterBinding->NdisOpenBlock.ProtocolBindingContext);
238
239 KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
240
241 CurrentEntry = CurrentEntry->Flink;
242 }
243 KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
244 }
245
246
247 VOID
248 MiniEthReceiveIndication(
249 IN PETH_FILTER Filter,
250 IN NDIS_HANDLE MacReceiveContext,
251 IN PCHAR Address,
252 IN PVOID HeaderBuffer,
253 IN UINT HeaderBufferSize,
254 IN PVOID LookaheadBuffer,
255 IN UINT LookaheadBufferSize,
256 IN UINT PacketSize)
257 /*
258 * FUNCTION: Receive indication function for Ethernet devices
259 * ARGUMENTS:
260 * Filter = Pointer to Ethernet filter
261 * MacReceiveContext = MAC receive context handle
262 * Address = Pointer to destination Ethernet address
263 * HeaderBuffer = Pointer to Ethernet header buffer
264 * HeaderBufferSize = Size of Ethernet header buffer
265 * LookaheadBuffer = Pointer to lookahead buffer
266 * LookaheadBufferSize = Size of lookahead buffer
267 * PacketSize = Total size of received packet
268 */
269 {
270 MiniIndicateData((PLOGICAL_ADAPTER)Filter->Miniport,
271 MacReceiveContext,
272 HeaderBuffer,
273 HeaderBufferSize,
274 LookaheadBuffer,
275 LookaheadBufferSize,
276 PacketSize);
277 }
278
279
280 VOID
281 MiniResetComplete(
282 IN NDIS_HANDLE MiniportAdapterHandle,
283 IN NDIS_STATUS Status,
284 IN BOOLEAN AddressingReset)
285 {
286 UNIMPLEMENTED
287 }
288
289
290 VOID
291 MiniSendComplete(
292 IN NDIS_HANDLE MiniportAdapterHandle,
293 IN PNDIS_PACKET Packet,
294 IN NDIS_STATUS Status)
295 /*
296 * FUNCTION: Forwards a message to the initiating protocol saying
297 * that a packet was handled
298 * ARGUMENTS:
299 * NdisAdapterHandle = Handle input to MiniportInitialize
300 * Packet = Pointer to NDIS packet that was sent
301 * Status = Status of send operation
302 */
303 {
304 PADAPTER_BINDING AdapterBinding;
305
306 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
307
308 AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[0];
309
310 (*AdapterBinding->ProtocolBinding->Chars.u2.SendCompleteHandler)(
311 AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
312 Packet,
313 Status);
314 }
315
316
317 VOID
318 MiniSendResourcesAvailable(
319 IN NDIS_HANDLE MiniportAdapterHandle)
320 {
321 UNIMPLEMENTED
322 }
323
324
325 VOID
326 MiniTransferDataComplete(
327 IN NDIS_HANDLE MiniportAdapterHandle,
328 IN PNDIS_PACKET Packet,
329 IN NDIS_STATUS Status,
330 IN UINT BytesTransferred)
331 {
332 PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportAdapterHandle;
333 PADAPTER_BINDING AdapterBinding = Adapter->MiniportAdapterBinding;
334
335 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
336
337 (*AdapterBinding->ProtocolBinding->Chars.u3.TransferDataCompleteHandler)(
338 AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
339 Packet,
340 Status,
341 BytesTransferred);
342 }
343
344
345 BOOLEAN
346 MiniAdapterHasAddress(
347 PLOGICAL_ADAPTER Adapter,
348 PNDIS_PACKET Packet)
349 /*
350 * FUNCTION: Determines wether a packet has the same destination address as an adapter
351 * ARGUMENTS:
352 * Adapter = Pointer to logical adapter object
353 * Packet = Pointer to NDIS packet
354 * RETURNS:
355 * TRUE if the destination address is that of the adapter, FALSE if not
356 */
357 {
358 UINT Length;
359 PUCHAR Start1;
360 PUCHAR Start2;
361 PNDIS_BUFFER NdisBuffer;
362 UINT BufferLength;
363
364 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
365
366 Start1 = (PUCHAR)&Adapter->Address;
367 NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
368 if (!NdisBuffer) {
369 NDIS_DbgPrint(MID_TRACE, ("Packet contains no buffers.\n"));
370 return FALSE;
371 }
372
373 NdisQueryBuffer(NdisBuffer, (PVOID)&Start2, &BufferLength);
374
375 /* FIXME: Should handle fragmented packets */
376
377 switch (Adapter->NdisMiniportBlock.MediaType) {
378 case NdisMedium802_3:
379 Length = ETH_LENGTH_OF_ADDRESS;
380 /* Destination address is the first field */
381 break;
382
383 default:
384 NDIS_DbgPrint(MIN_TRACE, ("Adapter has unsupported media type (0x%X).\n",
385 Adapter->NdisMiniportBlock.MediaType));
386 return FALSE;
387 }
388
389 if (BufferLength < Length) {
390 NDIS_DbgPrint(MID_TRACE, ("Buffer is too small.\n"));
391 return FALSE;
392 }
393
394 return (RtlCompareMemory((PVOID)Start1, (PVOID)Start2, Length) == Length);
395 }
396
397
398 PLOGICAL_ADAPTER
399 MiniLocateDevice(
400 PNDIS_STRING AdapterName)
401 /*
402 * FUNCTION: Returns the logical adapter object for a specific adapter
403 * ARGUMENTS:
404 * AdapterName = Pointer to name of adapter
405 * RETURNS:
406 * Pointer to logical adapter object, or NULL if none was found.
407 * If found, the adapter is referenced for the caller. The caller
408 * is responsible for dereferencing after use
409 */
410 {
411 KIRQL OldIrql;
412 PLIST_ENTRY CurrentEntry;
413 PLOGICAL_ADAPTER Adapter;
414
415 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
416
417 KeAcquireSpinLock(&AdapterListLock, &OldIrql);
418 CurrentEntry = AdapterListHead.Flink;
419 while (CurrentEntry != &AdapterListHead) {
420 Adapter = CONTAINING_RECORD(CurrentEntry, LOGICAL_ADAPTER, ListEntry);
421
422 if (RtlCompareUnicodeString(AdapterName, &Adapter->DeviceName, TRUE) == 0) {
423 ReferenceObject(Adapter);
424 KeReleaseSpinLock(&AdapterListLock, OldIrql);
425
426 NDIS_DbgPrint(DEBUG_MINIPORT, ("Leaving. Adapter found at (0x%X).\n", Adapter));
427
428 return Adapter;
429 }
430
431 CurrentEntry = CurrentEntry->Flink;
432 }
433 KeReleaseSpinLock(&AdapterListLock, OldIrql);
434
435 NDIS_DbgPrint(DEBUG_MINIPORT, ("Leaving (adapter not found).\n"));
436
437 return NULL;
438 }
439
440
441 NDIS_STATUS
442 MiniQueryInformation(
443 PLOGICAL_ADAPTER Adapter,
444 NDIS_OID Oid,
445 ULONG Size,
446 PULONG BytesWritten)
447 /*
448 * FUNCTION: Queries a logical adapter for properties
449 * ARGUMENTS:
450 * Adapter = Pointer to the logical adapter object to query
451 * Oid = Specifies the Object ID to query for
452 * Size = If non-zero overrides the length in the adapter object
453 * BytesWritten = Address of buffer to place number of bytes written
454 * NOTES:
455 * If the specified buffer is too small, a new buffer is allocated,
456 * and the query is attempted again
457 * RETURNS:
458 * Status of operation
459 */
460 {
461 NDIS_STATUS NdisStatus;
462 ULONG BytesNeeded;
463
464 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
465
466 if (Adapter->QueryBufferLength == 0) {
467 Adapter->QueryBuffer = ExAllocatePool(NonPagedPool, (Size == 0)? 32 : Size);
468
469 if (!Adapter->QueryBuffer) {
470 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
471 return NDIS_STATUS_RESOURCES;
472 }
473
474 Adapter->QueryBufferLength = (Size == 0)? 32 : Size;
475 }
476
477 BytesNeeded = (Size == 0)? Adapter->QueryBufferLength : Size;
478
479 NdisStatus = (*Adapter->Miniport->Chars.QueryInformationHandler)(
480 Adapter->NdisMiniportBlock.MiniportAdapterContext,
481 Oid,
482 Adapter->QueryBuffer,
483 BytesNeeded,
484 BytesWritten,
485 &BytesNeeded);
486
487 if ((NT_SUCCESS(NdisStatus)) || (NdisStatus == NDIS_STATUS_PENDING)) {
488 NDIS_DbgPrint(DEBUG_MINIPORT, ("Miniport returned status (0x%X).\n", NdisStatus));
489 return NdisStatus;
490 }
491
492 if (NdisStatus == NDIS_STATUS_INVALID_LENGTH) {
493 ExFreePool(Adapter->QueryBuffer);
494
495 Adapter->QueryBufferLength += BytesNeeded;
496 Adapter->QueryBuffer = ExAllocatePool(NonPagedPool,
497 Adapter->QueryBufferLength);
498
499 if (!Adapter->QueryBuffer) {
500 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
501 return NDIS_STATUS_RESOURCES;
502 }
503
504 NdisStatus = (*Adapter->Miniport->Chars.QueryInformationHandler)(
505 Adapter->NdisMiniportBlock.MiniportAdapterContext,
506 Oid,
507 Adapter->QueryBuffer,
508 Size,
509 BytesWritten,
510 &BytesNeeded);
511 }
512
513 return NdisStatus;
514 }
515
516
517 NDIS_STATUS
518 FASTCALL
519 MiniQueueWorkItem(
520 PLOGICAL_ADAPTER Adapter,
521 NDIS_WORK_ITEM_TYPE WorkItemType,
522 PVOID WorkItemContext,
523 NDIS_HANDLE Initiator)
524 /*
525 * FUNCTION: Queues a work item for execution at a later time
526 * ARGUMENTS:
527 * Adapter = Pointer to the logical adapter object to queue work item on
528 * WorkItemType = Type of work item to queue
529 * WorkItemContext = Pointer to context information for work item
530 * Initiator = Pointer to ADAPTER_BINDING structure of initiating protocol
531 * NOTES:
532 * Adapter lock must be held when called
533 * RETURNS:
534 * Status of operation
535 */
536 {
537 PNDIS_MINIPORT_WORK_ITEM Item;
538
539 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
540
541 if (Adapter->WorkQueueLevel < NDIS_MINIPORT_WORK_QUEUE_SIZE - 1) {
542 Item = &Adapter->WorkQueue[Adapter->WorkQueueLevel];
543 Adapter->WorkQueueLevel++;
544 } else {
545 Item = ExAllocatePool(NonPagedPool, sizeof(NDIS_MINIPORT_WORK_ITEM));
546 if (Item) {
547 /* Set flag so we know that the buffer should be freed
548 when work item is dequeued */
549 Item->Allocated = TRUE;
550 } else {
551 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
552 return NDIS_STATUS_RESOURCES;
553 }
554 }
555
556 Item->WorkItemType = WorkItemType;
557 Item->WorkItemContext = WorkItemContext;
558 Item->Initiator = Initiator;
559
560 Item->Link.Next = NULL;
561 if (!Adapter->WorkQueueHead) {
562 Adapter->WorkQueueHead = Item;
563 } else {
564 Adapter->WorkQueueTail->Link.Next = (PSINGLE_LIST_ENTRY)Item;
565 }
566
567 KeInsertQueueDpc(&Adapter->MiniportDpc, NULL, NULL);
568
569 return NDIS_STATUS_SUCCESS;
570 }
571
572
573 NDIS_STATUS
574 FASTCALL
575 MiniDequeueWorkItem(
576 PLOGICAL_ADAPTER Adapter,
577 NDIS_WORK_ITEM_TYPE *WorkItemType,
578 PVOID *WorkItemContext,
579 NDIS_HANDLE *Initiator)
580 /*
581 * FUNCTION: Dequeues a work item from the work queue of a logical adapter
582 * ARGUMENTS:
583 * Adapter = Pointer to the logical adapter object to dequeue work item from
584 * WorkItemType = Address of buffer for work item type
585 * WorkItemContext = Address of buffer for pointer to context information
586 * Initiator = Address of buffer for initiator of the work (ADAPTER_BINDING)
587 * NOTES:
588 * Adapter lock must be held when called
589 * RETURNS:
590 * Status of operation
591 */
592 {
593 PNDIS_MINIPORT_WORK_ITEM Item;
594
595 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
596
597 Item = Adapter->WorkQueueHead;
598 if (Item) {
599 Adapter->WorkQueueHead = (PNDIS_MINIPORT_WORK_ITEM)Item->Link.Next;
600 if (Item == Adapter->WorkQueueTail)
601 Adapter->WorkQueueTail = NULL;
602
603 *WorkItemType = Item->WorkItemType;
604 *WorkItemContext = Item->WorkItemContext;
605 *Initiator = Item->Initiator;
606
607 if (Item->Allocated) {
608 ExFreePool(Item);
609 } else {
610 Adapter->WorkQueueLevel--;
611 #ifdef DBG
612 if (Adapter->WorkQueueLevel < 0) {
613 NDIS_DbgPrint(MIN_TRACE, ("Adapter->WorkQueueLevel is < 0 (should be >= 0).\n"));
614 }
615 #endif
616 }
617
618 return NDIS_STATUS_SUCCESS;
619 }
620
621 return NDIS_STATUS_FAILURE;
622 }
623
624
625 NDIS_STATUS
626 MiniDoRequest(
627 PLOGICAL_ADAPTER Adapter,
628 PNDIS_REQUEST NdisRequest)
629 /*
630 * FUNCTION: Sends a request to a miniport
631 * ARGUMENTS:
632 * Adapter = Pointer to logical adapter object
633 * NdisRequest = Pointer to NDIS request structure describing request
634 * RETURNS:
635 * Status of operation
636 */
637 {
638 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
639
640 Adapter->NdisMiniportBlock.MediaRequest = NdisRequest;
641
642 switch (NdisRequest->RequestType) {
643 case NdisRequestQueryInformation:
644 return (*Adapter->Miniport->Chars.QueryInformationHandler)(
645 Adapter->NdisMiniportBlock.MiniportAdapterContext,
646 NdisRequest->DATA.QUERY_INFORMATION.Oid,
647 NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
648 NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
649 (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
650 (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded);
651 break;
652
653 case NdisRequestSetInformation:
654 return (*Adapter->Miniport->Chars.SetInformationHandler)(
655 Adapter->NdisMiniportBlock.MiniportAdapterContext,
656 NdisRequest->DATA.SET_INFORMATION.Oid,
657 NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
658 NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
659 (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
660 (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded);
661 break;
662
663 default:
664 return NDIS_STATUS_FAILURE;
665 }
666 }
667
668
669 VOID STDCALL MiniportDpc(
670 IN PKDPC Dpc,
671 IN PVOID DeferredContext,
672 IN PVOID SystemArgument1,
673 IN PVOID SystemArgument2)
674 /*
675 * FUNCTION: Deferred routine to handle serialization
676 * ARGUMENTS:
677 * Dpc = Pointer to DPC object
678 * DeferredContext = Pointer to context information (LOGICAL_ADAPTER)
679 * SystemArgument1 = Unused
680 * SystemArgument2 = Unused
681 */
682 {
683 NDIS_STATUS NdisStatus;
684 PVOID WorkItemContext;
685 NDIS_WORK_ITEM_TYPE WorkItemType;
686 PADAPTER_BINDING AdapterBinding;
687 PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(DeferredContext);
688
689 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
690
691 NdisStatus = MiniDequeueWorkItem(Adapter,
692 &WorkItemType,
693 &WorkItemContext,
694 (PNDIS_HANDLE)&AdapterBinding);
695 if (NdisStatus == NDIS_STATUS_SUCCESS) {
696 Adapter->MiniportAdapterBinding = AdapterBinding;
697 switch (WorkItemType) {
698 case NdisWorkItemSend:
699 #ifdef DBG
700 MiniDisplayPacket((PNDIS_PACKET)WorkItemContext);
701 #endif
702 NdisStatus = (*Adapter->Miniport->Chars.u1.SendHandler)(
703 Adapter->NdisMiniportBlock.MiniportAdapterContext,
704 (PNDIS_PACKET)WorkItemContext,
705 0);
706 if (NdisStatus != NDIS_STATUS_PENDING) {
707 MiniSendComplete((NDIS_HANDLE)Adapter,
708 (PNDIS_PACKET)WorkItemContext,
709 NdisStatus);
710 }
711 break;
712
713 case NdisWorkItemSendLoopback:
714 NdisStatus = ProIndicatePacket(Adapter,
715 (PNDIS_PACKET)WorkItemContext);
716 MiniSendComplete((NDIS_HANDLE)Adapter,
717 (PNDIS_PACKET)WorkItemContext,
718 NdisStatus);
719 break;
720
721 case NdisWorkItemReturnPackets:
722 break;
723
724 case NdisWorkItemResetRequested:
725 break;
726
727 case NdisWorkItemResetInProgress:
728 break;
729
730 case NdisWorkItemHalt:
731 break;
732
733 case NdisWorkItemMiniportCallback:
734 break;
735
736 case NdisWorkItemRequest:
737 NdisStatus = MiniDoRequest(Adapter, (PNDIS_REQUEST)WorkItemContext);
738
739 if (NdisStatus == NDIS_STATUS_PENDING)
740 break;
741
742 switch (((PNDIS_REQUEST)WorkItemContext)->RequestType) {
743 case NdisRequestQueryInformation:
744 NdisMQueryInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
745 break;
746
747 case NdisRequestSetInformation:
748 NdisMSetInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
749 break;
750
751 default:
752 NDIS_DbgPrint(MIN_TRACE, ("Unknown NDIS request type.\n"));
753 break;
754 }
755 break;
756
757 default:
758 NDIS_DbgPrint(MIN_TRACE, ("Unknown NDIS work item type (%d).\n", WorkItemType));
759 break;
760 }
761 }
762 }
763
764
765 /*
766 * @unimplemented
767 */
768 VOID
769 EXPORT
770 NdisMCloseLog(
771 IN NDIS_HANDLE LogHandle)
772 {
773 UNIMPLEMENTED
774 }
775
776
777 /*
778 * @unimplemented
779 */
780 NDIS_STATUS
781 EXPORT
782 NdisMCreateLog(
783 IN NDIS_HANDLE MiniportAdapterHandle,
784 IN UINT Size,
785 OUT PNDIS_HANDLE LogHandle)
786 {
787 UNIMPLEMENTED
788
789 return NDIS_STATUS_FAILURE;
790 }
791
792
793 /*
794 * @implemented
795 */
796 VOID
797 EXPORT
798 NdisMDeregisterAdapterShutdownHandler(
799 IN NDIS_HANDLE MiniportHandle)
800 /*
801 * FUNCTION: de-registers a shutdown handler
802 * ARGUMENTS: MiniportHandle: Handle passed into MiniportInitialize
803 */
804 {
805 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
806 PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportHandle;
807
808 if(Adapter->BugcheckContext->ShutdownHandler)
809 KeDeregisterBugCheckCallback(Adapter->BugcheckContext->CallbackRecord);
810 }
811
812
813 /*
814 * @unimplemented
815 */
816 VOID
817 EXPORT
818 NdisMFlushLog(
819 IN NDIS_HANDLE LogHandle)
820 {
821 UNIMPLEMENTED
822 }
823
824
825 /*
826 * @unimplemented
827 */
828 VOID
829 EXPORT
830 NdisMIndicateStatus(
831 IN NDIS_HANDLE MiniportAdapterHandle,
832 IN NDIS_STATUS GeneralStatus,
833 IN PVOID StatusBuffer,
834 IN UINT StatusBufferSize)
835 {
836 UNIMPLEMENTED
837 }
838
839
840 /*
841 * @unimplemented
842 */
843 VOID
844 EXPORT
845 NdisMIndicateStatusComplete(
846 IN NDIS_HANDLE MiniportAdapterHandle)
847 {
848 UNIMPLEMENTED
849 }
850
851
852 /*
853 * @implemented
854 */
855 VOID
856 EXPORT
857 NdisInitializeWrapper(
858 OUT PNDIS_HANDLE NdisWrapperHandle,
859 IN PVOID SystemSpecific1,
860 IN PVOID SystemSpecific2,
861 IN PVOID SystemSpecific3)
862 /*
863 * FUNCTION: Notifies the NDIS library that a new miniport is initializing
864 * ARGUMENTS:
865 * NdisWrapperHandle = Address of buffer to place NDIS wrapper handle
866 * SystemSpecific1 = Pointer to the driver's driver object
867 * SystemSpecific2 = Pointer to the driver's registry path
868 * SystemSpecific3 = Always NULL
869 * NOTES:
870 * - SystemSpecific2 goes invalid so we copy it
871 */
872 {
873 PMINIPORT_DRIVER Miniport;
874 PUNICODE_STRING RegistryPath;
875 WCHAR *RegistryBuffer;
876
877 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
878
879 #if BREAK_ON_MINIPORT_INIT
880 __asm__ ("int $3\n");
881 #endif
882
883 Miniport = ExAllocatePool(NonPagedPool, sizeof(MINIPORT_DRIVER));
884 if (!Miniport) {
885 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
886 *NdisWrapperHandle = NULL;
887 return;
888 }
889
890 RtlZeroMemory(Miniport, sizeof(MINIPORT_DRIVER));
891
892 KeInitializeSpinLock(&Miniport->Lock);
893
894 Miniport->RefCount = 1;
895
896 Miniport->DriverObject = (PDRIVER_OBJECT)SystemSpecific1;
897
898 /* set the miniport's driver registry path */
899 RegistryPath = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING));
900 if(!RegistryPath)
901 {
902 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
903 return;
904 }
905
906 RegistryPath->Length = ((PUNICODE_STRING)SystemSpecific2)->Length;
907 RegistryPath->MaximumLength = RegistryPath->Length + sizeof(WCHAR); /* room for 0-term */
908
909 RegistryBuffer = ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(WCHAR));
910 if(!RegistryBuffer)
911 {
912 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
913 return;
914 }
915
916 RtlCopyMemory(RegistryBuffer, ((PUNICODE_STRING)SystemSpecific2)->Buffer, RegistryPath->Length);
917 RegistryBuffer[RegistryPath->Length/sizeof(WCHAR)] = 0;
918
919 RegistryPath->Buffer = RegistryBuffer;
920 Miniport->RegistryPath = RegistryPath;
921
922 InitializeListHead(&Miniport->AdapterListHead);
923
924 /* Put miniport in global miniport list */
925 ExInterlockedInsertTailList(&MiniportListHead,
926 &Miniport->ListEntry,
927 &MiniportListLock);
928
929 *NdisWrapperHandle = Miniport;
930
931 }
932
933
934 /*
935 * @implemented
936 */
937 VOID
938 EXPORT
939 NdisMQueryInformationComplete(
940 IN NDIS_HANDLE MiniportAdapterHandle,
941 IN NDIS_STATUS Status)
942 {
943 PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
944 PADAPTER_BINDING AdapterBinding = (PADAPTER_BINDING)Adapter->MiniportAdapterBinding;
945
946 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
947
948 (*AdapterBinding->ProtocolBinding->Chars.RequestCompleteHandler)(
949 AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
950 Adapter->NdisMiniportBlock.MediaRequest,
951 Status);
952 }
953
954
955 VOID NdisIBugcheckCallback(
956 IN PVOID Buffer,
957 IN ULONG Length)
958 /*
959 * FUNCTION: Internal callback for handling bugchecks - calls adapter's shutdown handler
960 * ARGUMENTS:
961 * Buffer: Pointer to a bugcheck callback context
962 * Length: Unused
963 */
964 {
965 PMINIPORT_BUGCHECK_CONTEXT Context = (PMINIPORT_BUGCHECK_CONTEXT)Buffer;
966 ADAPTER_SHUTDOWN_HANDLER sh = (ADAPTER_SHUTDOWN_HANDLER)Context->ShutdownHandler;
967
968 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
969
970 if(sh)
971 sh(Context->DriverContext);
972 }
973
974
975 /*
976 * @implemented
977 */
978 VOID
979 EXPORT
980 NdisMRegisterAdapterShutdownHandler(
981 IN NDIS_HANDLE MiniportHandle,
982 IN PVOID ShutdownContext,
983 IN ADAPTER_SHUTDOWN_HANDLER ShutdownHandler)
984 /*
985 * FUNCTION: Register a shutdown handler for an adapter
986 * ARGUMENTS:
987 * MiniportHandle: Handle originally passed into MiniportInitialize
988 * ShutdownContext: Pre-initialized bugcheck context
989 * ShutdownHandler: Function to call to handle the bugcheck
990 * NOTES:
991 * - I'm not sure about ShutdownContext
992 * - FIXME - memory leak below
993 */
994 {
995 PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportHandle;
996 PMINIPORT_BUGCHECK_CONTEXT BugcheckContext = Adapter->BugcheckContext;
997
998 NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
999
1000 if(BugcheckContext)
1001 return;
1002
1003 BugcheckContext = ExAllocatePool(NonPagedPool, sizeof(MINIPORT_BUGCHECK_CONTEXT));
1004 if(!BugcheckContext)
1005 {
1006 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
1007 return;
1008 }
1009
1010 BugcheckContext->ShutdownHandler = ShutdownHandler;
1011 BugcheckContext->DriverContext = ShutdownContext;
1012
1013 /* not sure if this needs to be initialized or not... oh well, it's a leak. */
1014 BugcheckContext->CallbackRecord = ExAllocatePool(NonPagedPool, sizeof(KBUGCHECK_CALLBACK_RECORD));
1015
1016 KeRegisterBugCheckCallback(BugcheckContext->CallbackRecord, NdisIBugcheckCallback,
1017 BugcheckContext, sizeof(BugcheckContext), "Ndis Miniport");
1018 }
1019
1020
1021 NDIS_STATUS
1022 DoQueries(
1023 PLOGICAL_ADAPTER Adapter,
1024 NDIS_OID AddressOID)
1025 /*
1026 * FUNCTION: Queries miniport for information
1027 * ARGUMENTS:
1028 * Adapter = Pointer to logical adapter
1029 * AddressOID = OID to use to query for current address
1030 * RETURNS:
1031 * Status of operation
1032 */
1033 {
1034 ULONG BytesWritten;
1035 NDIS_STATUS NdisStatus;
1036
1037 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1038
1039 /* Get MAC options for adapter */
1040 NdisStatus = MiniQueryInformation(Adapter,
1041 OID_GEN_MAC_OPTIONS,
1042 0,
1043 &BytesWritten);
1044 if (NdisStatus != NDIS_STATUS_SUCCESS) {
1045 NDIS_DbgPrint(MIN_TRACE, ("OID_GEN_MAC_OPTIONS failed. NdisStatus (0x%X).\n", NdisStatus));
1046 return NdisStatus;
1047 }
1048
1049 RtlCopyMemory(&Adapter->NdisMiniportBlock.MacOptions, Adapter->QueryBuffer, sizeof(UINT));
1050
1051 NDIS_DbgPrint(DEBUG_MINIPORT, ("MacOptions (0x%X).\n", Adapter->NdisMiniportBlock.MacOptions));
1052
1053 /* Get current hardware address of adapter */
1054 NdisStatus = MiniQueryInformation(Adapter,
1055 AddressOID,
1056 0,
1057 &BytesWritten);
1058 if (NdisStatus != NDIS_STATUS_SUCCESS) {
1059 NDIS_DbgPrint(MIN_TRACE, ("Address OID (0x%X) failed. NdisStatus (0x%X).\n",
1060 AddressOID, NdisStatus));
1061 return NdisStatus;
1062 }
1063
1064 RtlCopyMemory(&Adapter->Address, Adapter->QueryBuffer, Adapter->AddressLength);
1065 #ifdef DBG
1066 {
1067 /* 802.3 only */
1068
1069 PUCHAR A = (PUCHAR)&Adapter->Address.Type.Medium802_3;
1070
1071 NDIS_DbgPrint(MAX_TRACE, ("Adapter address is (%02X %02X %02X %02X %02X %02X).\n",
1072 A[0], A[1], A[2], A[3], A[4], A[5]));
1073 }
1074 #endif /* DBG */
1075
1076 /* Get maximum lookahead buffer size of adapter */
1077 NdisStatus = MiniQueryInformation(Adapter,
1078 OID_GEN_MAXIMUM_LOOKAHEAD,
1079 0,
1080 &BytesWritten);
1081 if (NdisStatus != NDIS_STATUS_SUCCESS) {
1082 NDIS_DbgPrint(MIN_TRACE, ("OID_GEN_MAXIMUM_LOOKAHEAD failed. NdisStatus (0x%X).\n", NdisStatus));
1083 return NdisStatus;
1084 }
1085
1086 Adapter->MaxLookaheadLength = *((PULONG)Adapter->QueryBuffer);
1087
1088 NDIS_DbgPrint(DEBUG_MINIPORT, ("MaxLookaheadLength (0x%X).\n", Adapter->MaxLookaheadLength));
1089
1090 /* Get current lookahead buffer size of adapter */
1091 NdisStatus = MiniQueryInformation(Adapter,
1092 OID_GEN_CURRENT_LOOKAHEAD,
1093 0,
1094 &BytesWritten);
1095 if (NdisStatus != NDIS_STATUS_SUCCESS) {
1096 NDIS_DbgPrint(MIN_TRACE, ("OID_GEN_CURRENT_LOOKAHEAD failed. NdisStatus (0x%X).\n", NdisStatus));
1097 return NdisStatus;
1098 }
1099
1100 Adapter->CurLookaheadLength = *((PULONG)Adapter->QueryBuffer);
1101
1102 NDIS_DbgPrint(DEBUG_MINIPORT, ("CurLookaheadLength (0x%X).\n", Adapter->CurLookaheadLength));
1103
1104 if (Adapter->MaxLookaheadLength != 0) {
1105 Adapter->LookaheadLength = Adapter->MaxLookaheadLength +
1106 Adapter->MediumHeaderSize;
1107 Adapter->LookaheadBuffer = ExAllocatePool(NonPagedPool,
1108 Adapter->LookaheadLength);
1109 if (!Adapter->LookaheadBuffer)
1110 return NDIS_STATUS_RESOURCES;
1111 }
1112
1113 return STATUS_SUCCESS;
1114 }
1115
1116
1117 VOID
1118 NdisIStartAdapter(
1119 WCHAR *DeviceNameStr,
1120 UINT DeviceNameStrLength,
1121 PMINIPORT_DRIVER Miniport
1122 )
1123 /*
1124 * FUNCTION: Start an adapter
1125 * ARGUMENTS:
1126 * DeviceNameStr: 0-terminated wide char string of name of device to start
1127 * DeviceNameStrLength: length of DeviceNameStr *IN WCHARs*
1128 * NOTES:
1129 * TODO:
1130 * - verify that all resources are properly freed on success & failure
1131 */
1132 {
1133 WCHAR *DeviceName;
1134 HANDLE RegKeyHandle;
1135 WCHAR *RegKeyPath;
1136 UNICODE_STRING RegKeyPathU;
1137 OBJECT_ATTRIBUTES RegKeyAttributes;
1138 NDIS_STATUS NdisStatus;
1139 NDIS_STATUS OpenErrorStatus;
1140 NTSTATUS Status;
1141 UINT SelectedMediumIndex = 0;
1142 PLOGICAL_ADAPTER Adapter = 0;
1143 NDIS_OID AddressOID;
1144 BOOLEAN MemError = FALSE;
1145 KIRQL OldIrql;
1146 PORPHAN_ADAPTER OrphanAdapter = 0;
1147
1148 NDIS_DbgPrint(MAX_TRACE, ("Called with %ws\n", DeviceNameStr));
1149 Adapter = ExAllocatePool(NonPagedPool, sizeof(LOGICAL_ADAPTER));
1150 if (!Adapter)
1151 {
1152 NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
1153 return;
1154 }
1155
1156 /* This is very important */
1157 RtlZeroMemory(Adapter, sizeof(LOGICAL_ADAPTER));
1158
1159 DeviceName = ExAllocatePool(NonPagedPool, sizeof(DEVICE_ROOT) + DeviceNameStrLength * sizeof(WCHAR));
1160 if(!DeviceName)
1161 {
1162 NDIS_DbgPrint(MIN_TRACE,("Insufficient memory\n"));
1163 ExFreePool(Adapter);
1164 return;
1165 }
1166
1167 /* DEVICE_ROOT is a constant string defined above, incl. 0-term */
1168 wcscpy(DeviceName, DEVICE_ROOT);
1169
1170 /* reg_sz is 0-term by def */
1171 wcsncat(DeviceName, DeviceNameStr, DeviceNameStrLength);
1172 RtlInitUnicodeString(&Adapter->DeviceName, DeviceName);
1173
1174 NDIS_DbgPrint(MAX_TRACE, ("creating device %ws\n", DeviceName));
1175
1176 Status = IoCreateDevice(Miniport->DriverObject, 0, &Adapter->DeviceName, FILE_DEVICE_PHYSICAL_NETCARD,
1177 0, FALSE, &Adapter->NdisMiniportBlock.DeviceObject);
1178 if (!NT_SUCCESS(Status))
1179 {
1180 NDIS_DbgPrint(MIN_TRACE, ("Could not create device object.\n"));
1181 ExFreePool(Adapter);
1182 return;
1183 }
1184
1185 /* find out if there are any adapters in the orphans list and reserve resources */
1186 KeAcquireSpinLock(&OrphanAdapterListLock, &OldIrql);
1187 OrphanAdapter = (PORPHAN_ADAPTER)OrphanAdapterListHead.Flink;
1188 while(&OrphanAdapter->ListEntry != &OrphanAdapterListHead)
1189 {
1190 PORPHAN_ADAPTER TempAdapter;
1191 PCM_RESOURCE_LIST ResourceList;
1192 UINT i;
1193
1194 if(!RtlCompareUnicodeString(&OrphanAdapter->RegistryPath, Miniport->RegistryPath, TRUE))
1195 {
1196 OrphanAdapter = (PORPHAN_ADAPTER)OrphanAdapter->ListEntry.Flink;
1197 continue;
1198 }
1199
1200 NDIS_DbgPrint(MAX_TRACE, ("Found an orphan adapter for RegistryPath %wZ\n", Miniport->RegistryPath));
1201
1202 /* there is an orphan adapter for us */
1203 Adapter->SlotNumber = OrphanAdapter->SlotNumber;
1204 Adapter->BusNumber = OrphanAdapter->BusNumber;
1205 Adapter->BusType = OrphanAdapter->BusType;
1206
1207 Status = HalAssignSlotResources(Miniport->RegistryPath, 0, Miniport->DriverObject,
1208 Adapter->NdisMiniportBlock.DeviceObject, Adapter->BusType, Adapter->BusNumber,
1209 Adapter->SlotNumber, &ResourceList);
1210
1211 if(!NT_SUCCESS(Status))
1212 {
1213 NDIS_DbgPrint(MIN_TRACE, ("HalAssignSlotResources broke: 0x%x\n", Status));
1214 #ifdef DBG
1215 __asm__ ("int $3\n");
1216 #endif
1217 /* i guess we should just give up on this adapter */
1218 break;
1219 }
1220
1221 /* go through the returned resource list and populate the Adapter */
1222 for(i = 0; i<ResourceList->Count; i++)
1223 {
1224 int j;
1225
1226 PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor = &ResourceList->List[i];
1227
1228 for(j=0; j<ResourceDescriptor->PartialResourceList.Count; j++)
1229 {
1230 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor =
1231 &ResourceDescriptor->PartialResourceList.PartialDescriptors[i];
1232
1233 switch(PartialResourceDescriptor->Type)
1234 {
1235 case CmResourceTypeInterrupt:
1236 Adapter->Irql = PartialResourceDescriptor->u.Interrupt.Level;
1237 Adapter->Vector = PartialResourceDescriptor->u.Interrupt.Vector;
1238 Adapter->Affinity = PartialResourceDescriptor->u.Interrupt.Affinity;
1239 break;
1240
1241 case CmResourceTypePort:
1242 Adapter->BaseIoAddress = PartialResourceDescriptor->u.Port.Start;
1243 break;
1244
1245 case CmResourceTypeMemory:
1246 Adapter->BaseMemoryAddress = PartialResourceDescriptor->u.Memory.Start;
1247 break;
1248
1249 case CmResourceTypeDma:
1250 Adapter->DmaPort = PartialResourceDescriptor->u.Dma.Port;
1251 Adapter->DmaChannel = PartialResourceDescriptor->u.Dma.Channel;
1252 break;
1253
1254 case CmResourceTypeDeviceSpecific:
1255 default:
1256 break;
1257 }
1258 }
1259 }
1260
1261 /* remove the adapter from the list */
1262 TempAdapter = (PORPHAN_ADAPTER)OrphanAdapter->ListEntry.Flink;
1263 RemoveEntryList(&OrphanAdapter->ListEntry);
1264 OrphanAdapter = TempAdapter;
1265 }
1266 KeReleaseSpinLock(&OrphanAdapterListLock, OldIrql);
1267
1268 /* includes room for a 0-term */
1269 RegKeyPath = ExAllocatePool(PagedPool, (wcslen(SERVICES_ROOT) + wcslen(DeviceNameStr) + 1) * sizeof(WCHAR));
1270 if(!RegKeyPath)
1271 {
1272 NDIS_DbgPrint(MIN_TRACE,("Insufficient resources\n"));
1273 ExFreePool(Adapter);
1274 return;
1275 }
1276
1277 wcscpy(RegKeyPath, SERVICES_ROOT);
1278 wcscat(RegKeyPath, DeviceNameStr);
1279 RegKeyPath[wcslen(SERVICES_ROOT) + wcslen(DeviceNameStr)] = 0;
1280
1281 RtlInitUnicodeString(&RegKeyPathU, RegKeyPath);
1282 InitializeObjectAttributes(&RegKeyAttributes, &RegKeyPathU, OBJ_CASE_INSENSITIVE, NULL, NULL);
1283
1284 Status = ZwOpenKey(&RegKeyHandle, KEY_ALL_ACCESS, &RegKeyAttributes);
1285 if(Status != STATUS_SUCCESS)
1286 {
1287 NDIS_DbgPrint(MIN_TRACE,("failed to open adapter-specific reg key %ws\n", RegKeyPath));
1288 ExFreePool(Adapter);
1289 return;
1290 }
1291
1292 NDIS_DbgPrint(MAX_TRACE, ("opened device reg key: %wZ\n", &RegKeyPathU));
1293
1294 KeInitializeSpinLock(&Adapter->NdisMiniportBlock.Lock);
1295 InitializeListHead(&Adapter->ProtocolListHead);
1296 Adapter->RefCount = 1;
1297 Adapter->Miniport = Miniport;
1298
1299 /* Set handlers (some NDIS macros require these) */
1300
1301 Adapter->NdisMiniportBlock.EthRxCompleteHandler = MiniEthReceiveComplete;
1302 Adapter->NdisMiniportBlock.EthRxIndicateHandler = MiniEthReceiveIndication;
1303 Adapter->NdisMiniportBlock.SendCompleteHandler = MiniSendComplete;
1304 Adapter->NdisMiniportBlock.SendResourcesHandler = MiniSendResourcesAvailable;
1305 Adapter->NdisMiniportBlock.ResetCompleteHandler = MiniResetComplete;
1306 Adapter->NdisMiniportBlock.TDCompleteHandler = MiniTransferDataComplete;
1307 Adapter->NdisMiniportBlock.PacketIndicateHandler= MiniIndicateReceivePacket;
1308
1309 KeInitializeDpc(&Adapter->MiniportDpc, MiniportDpc, (PVOID)Adapter);
1310
1311 /* Put adapter in adapter list for this miniport */
1312 ExInterlockedInsertTailList(&Miniport->AdapterListHead, &Adapter->MiniportListEntry, &Miniport->Lock);
1313
1314 /* Put adapter in global adapter list */
1315 ExInterlockedInsertTailList(&AdapterListHead, &Adapter->ListEntry, &AdapterListLock);
1316
1317 /* Call MiniportInitialize */
1318 NDIS_DbgPrint(MID_TRACE, ("calling MiniportInitialize\n"));
1319 NdisStatus = (*Miniport->Chars.InitializeHandler)( &OpenErrorStatus, &SelectedMediumIndex, &MediaArray[0],
1320 MEDIA_ARRAY_SIZE, Adapter, RegKeyHandle);
1321
1322 ZwClose(RegKeyHandle);
1323
1324 if ((NdisStatus == NDIS_STATUS_SUCCESS) && (SelectedMediumIndex < MEDIA_ARRAY_SIZE))
1325 {
1326 NDIS_DbgPrint(MID_TRACE,("successful return from MiniportInitialize\n"));
1327
1328 Adapter->NdisMiniportBlock.MediaType = MediaArray[SelectedMediumIndex];
1329
1330 switch (Adapter->NdisMiniportBlock.MediaType)
1331 {
1332 case NdisMedium802_3:
1333 Adapter->MediumHeaderSize = 14; /* XXX figure out what to do about LLC */
1334 AddressOID = OID_802_3_CURRENT_ADDRESS;
1335 Adapter->AddressLength = ETH_LENGTH_OF_ADDRESS;
1336
1337 Adapter->NdisMiniportBlock.FilterDbs.u.EthDB = ExAllocatePool(NonPagedPool, sizeof(ETH_FILTER));
1338 if (Adapter->NdisMiniportBlock.FilterDbs.u.EthDB)
1339 {
1340 RtlZeroMemory(Adapter->NdisMiniportBlock.FilterDbs.u.EthDB, sizeof(ETH_FILTER));
1341 Adapter->NdisMiniportBlock.FilterDbs.u.EthDB->Miniport = (PNDIS_MINIPORT_BLOCK)Adapter;
1342 }
1343 else
1344 MemError = TRUE;
1345
1346 break;
1347
1348 default:
1349 /* FIXME: Support other types of media */
1350 ExFreePool(Adapter);
1351 ASSERT(FALSE);
1352 return;
1353 }
1354
1355 NdisStatus = DoQueries(Adapter, AddressOID);
1356 }
1357
1358 if ((MemError) || (NdisStatus != NDIS_STATUS_SUCCESS) || (SelectedMediumIndex >= MEDIA_ARRAY_SIZE))
1359 {
1360 NDIS_DbgPrint(MAX_TRACE, ("return from MiniportInitialize: NdisStatus 0x%x, SelectedMediumIndex 0x%x\n",
1361 NdisStatus, SelectedMediumIndex));
1362
1363 /* Remove adapter from adapter list for this miniport */
1364 KeAcquireSpinLock(&Miniport->Lock, &OldIrql);
1365 RemoveEntryList(&Adapter->MiniportListEntry);
1366 KeReleaseSpinLock(&Miniport->Lock, OldIrql);
1367
1368 /* Remove adapter from global adapter list */
1369 KeAcquireSpinLock(&AdapterListLock, &OldIrql);
1370 RemoveEntryList(&Adapter->ListEntry);
1371 KeReleaseSpinLock(&AdapterListLock, OldIrql);
1372
1373 if (Adapter->LookaheadBuffer)
1374 ExFreePool(Adapter->LookaheadBuffer);
1375
1376 IoDeleteDevice(Adapter->NdisMiniportBlock.DeviceObject);
1377 ExFreePool(Adapter);
1378 NDIS_DbgPrint(MIN_TRACE, ("MiniportInitialize() failed for an adapter.\n"));
1379 }
1380 }
1381
1382 /*
1383 * @implemented
1384 */
1385 NDIS_STATUS
1386 EXPORT
1387 NdisMRegisterMiniport(
1388 IN NDIS_HANDLE NdisWrapperHandle,
1389 IN PNDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics,
1390 IN UINT CharacteristicsLength)
1391 /*
1392 * FUNCTION: Registers a miniport's MiniportXxx entry points with the NDIS library
1393 * ARGUMENTS:
1394 * NdisWrapperHandle = Pointer to handle returned by NdisMInitializeWrapper
1395 * MiniportCharacteristics = Pointer to a buffer with miniport characteristics
1396 * CharacteristicsLength = Number of bytes in characteristics buffer
1397 * RETURNS:
1398 * Status of operation
1399 * NOTES:
1400 * - To create device objects for the miniport, the Route value under Linkage is
1401 * parsed. I don't know if this is the way Microsoft does it or not.
1402 * TODO:
1403 * verify this algorithm by playing with nt
1404 */
1405 {
1406 UINT MinSize;
1407 NTSTATUS Status;
1408 PMINIPORT_DRIVER Miniport = GET_MINIPORT_DRIVER(NdisWrapperHandle);
1409 OBJECT_ATTRIBUTES DeviceKeyAttributes;
1410 OBJECT_ATTRIBUTES LinkageKeyAttributes;
1411 HANDLE DeviceKeyHandle;
1412 HANDLE LinkageKeyHandle;
1413 UNICODE_STRING RouteVal;
1414 UNICODE_STRING LinkageKeyName;
1415 KEY_VALUE_PARTIAL_INFORMATION *RouteData;
1416 ULONG RouteDataLength;
1417 UINT NextRouteOffset = 0;
1418
1419 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1420
1421 switch (MiniportCharacteristics->MajorNdisVersion) {
1422 case 0x03:
1423 MinSize = sizeof(NDIS30_MINIPORT_CHARACTERISTICS_S);
1424 break;
1425
1426 case 0x04:
1427 MinSize = sizeof(NDIS40_MINIPORT_CHARACTERISTICS_S);
1428 break;
1429
1430 case 0x05:
1431 MinSize = sizeof(NDIS50_MINIPORT_CHARACTERISTICS_S);
1432 break;
1433
1434 default:
1435 NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics version.\n"));
1436 return NDIS_STATUS_BAD_VERSION;
1437 }
1438
1439 if (CharacteristicsLength < MinSize) {
1440 NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
1441 return NDIS_STATUS_BAD_CHARACTERISTICS;
1442 }
1443
1444 /* Check if mandatory MiniportXxx functions are specified */
1445 if ((!MiniportCharacteristics->HaltHandler) ||
1446 (!MiniportCharacteristics->InitializeHandler)||
1447 (!MiniportCharacteristics->QueryInformationHandler) ||
1448 (!MiniportCharacteristics->ResetHandler) ||
1449 (!MiniportCharacteristics->SetInformationHandler)) {
1450 NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
1451 return NDIS_STATUS_BAD_CHARACTERISTICS;
1452 }
1453
1454 if (MiniportCharacteristics->MajorNdisVersion == 0x03) {
1455 if (!MiniportCharacteristics->u1.SendHandler) {
1456 NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
1457 return NDIS_STATUS_BAD_CHARACTERISTICS;
1458 }
1459 } else if (MiniportCharacteristics->MajorNdisVersion >= 0x04) {
1460 /* NDIS 4.0+ */
1461 if ((!MiniportCharacteristics->u1.SendHandler) &&
1462 (!MiniportCharacteristics->SendPacketsHandler)) {
1463 NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
1464 return NDIS_STATUS_BAD_CHARACTERISTICS;
1465 }
1466 }
1467
1468 /* TODO: verify NDIS5 and NDIS5.1 */
1469
1470 RtlCopyMemory(&Miniport->Chars, MiniportCharacteristics, MinSize);
1471
1472 /*
1473 * extract the list of bound adapters from the registry's Route value
1474 * for this adapter. It seems under WinNT that the Route value in the
1475 * Linkage subkey holds an entry for each miniport instance we know about.
1476 * This surely isn't how Windows does it, but it's better than nothing.
1477 */
1478
1479 /* Read the miniport config from the registry */
1480 InitializeObjectAttributes(&DeviceKeyAttributes, Miniport->RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
1481
1482 Status = ZwOpenKey(&DeviceKeyHandle, KEY_READ, &DeviceKeyAttributes);
1483 if(!NT_SUCCESS(Status))
1484 {
1485 NDIS_DbgPrint(MIN_TRACE,("Failed to open driver key: 0x%x\n", Status));
1486 return NDIS_STATUS_FAILURE;
1487 }
1488
1489 RtlInitUnicodeString(&LinkageKeyName, L"Linkage");
1490 InitializeObjectAttributes(&LinkageKeyAttributes, &LinkageKeyName, OBJ_CASE_INSENSITIVE, DeviceKeyHandle, NULL);
1491
1492 Status = ZwOpenKey(&LinkageKeyHandle, KEY_READ, &LinkageKeyAttributes);
1493 if(!NT_SUCCESS(Status))
1494 {
1495 NDIS_DbgPrint(MIN_TRACE,("Failed to open Linkage key: 0x%x\n", Status));
1496 ZwClose(DeviceKeyHandle);
1497 return NDIS_STATUS_FAILURE;
1498 }
1499
1500 RouteData = ExAllocatePool(PagedPool, ROUTE_DATA_SIZE);
1501 if(!RouteData)
1502 {
1503 NDIS_DbgPrint(MIN_TRACE,("Insufficient resources\n"));
1504 ZwClose(LinkageKeyHandle);
1505 ZwClose(DeviceKeyHandle);
1506 return NDIS_STATUS_RESOURCES;
1507 }
1508
1509 RtlInitUnicodeString(&RouteVal, L"Route");
1510
1511 Status = ZwQueryValueKey(LinkageKeyHandle, &RouteVal, KeyValuePartialInformation, RouteData, ROUTE_DATA_SIZE, &RouteDataLength);
1512 if(!NT_SUCCESS(Status))
1513 {
1514 NDIS_DbgPrint(MIN_TRACE,("Failed to query Route value\n"));
1515 ZwClose(LinkageKeyHandle);
1516 ZwClose(DeviceKeyHandle);
1517 ExFreePool(RouteData);
1518 return NDIS_STATUS_FAILURE;
1519 }
1520
1521 ZwClose(LinkageKeyHandle);
1522 ZwClose(DeviceKeyHandle);
1523
1524 /* route is a REG_MULTI_SZ with each nic object created by NDI - create an adapter for each */
1525 while(*(RouteData->Data + NextRouteOffset))
1526 {
1527 NDIS_DbgPrint(MID_TRACE, ("Starting adapter %ws\n", (WCHAR *)(RouteData->Data + NextRouteOffset)));
1528
1529 NdisIStartAdapter((WCHAR *)(RouteData->Data + NextRouteOffset),
1530 wcslen((WCHAR *)(RouteData->Data + NextRouteOffset)), Miniport);
1531
1532 NextRouteOffset += wcslen((WCHAR *)(RouteData->Data + NextRouteOffset));
1533 }
1534
1535 ExFreePool(RouteData);
1536 return NDIS_STATUS_SUCCESS;
1537 }
1538
1539
1540 /*
1541 * @implemented
1542 */
1543 VOID
1544 EXPORT
1545 NdisMResetComplete(
1546 IN NDIS_HANDLE MiniportAdapterHandle,
1547 IN NDIS_STATUS Status,
1548 IN BOOLEAN AddressingReset)
1549 {
1550 MiniResetComplete(MiniportAdapterHandle,
1551 Status,
1552 AddressingReset);
1553 }
1554
1555
1556 /*
1557 * @implemented
1558 */
1559 VOID
1560 EXPORT
1561 NdisMSendComplete(
1562 IN NDIS_HANDLE MiniportAdapterHandle,
1563 IN PNDIS_PACKET Packet,
1564 IN NDIS_STATUS Status)
1565 /*
1566 * FUNCTION: Forwards a message to the initiating protocol saying
1567 * that a packet was handled
1568 * ARGUMENTS:
1569 * NdisAdapterHandle = Handle input to MiniportInitialize
1570 * Packet = Pointer to NDIS packet that was sent
1571 * Status = Status of send operation
1572 */
1573 {
1574 MiniSendComplete(MiniportAdapterHandle,
1575 Packet,
1576 Status);
1577 }
1578
1579
1580 /*
1581 * @implemented
1582 */
1583 VOID
1584 EXPORT
1585 NdisMSendResourcesAvailable(
1586 IN NDIS_HANDLE MiniportAdapterHandle)
1587 {
1588 MiniSendResourcesAvailable(MiniportAdapterHandle);
1589 }
1590
1591
1592 /*
1593 * @implemented
1594 */
1595 VOID
1596 EXPORT
1597 NdisMTransferDataComplete(
1598 IN NDIS_HANDLE MiniportAdapterHandle,
1599 IN PNDIS_PACKET Packet,
1600 IN NDIS_STATUS Status,
1601 IN UINT BytesTransferred)
1602 {
1603 MiniTransferDataComplete(MiniportAdapterHandle,
1604 Packet,
1605 Status,
1606 BytesTransferred);
1607 }
1608
1609
1610 /*
1611 * @implemented
1612 */
1613 VOID
1614 EXPORT
1615 NdisMSetInformationComplete(
1616 IN NDIS_HANDLE MiniportAdapterHandle,
1617 IN NDIS_STATUS Status)
1618 {
1619 PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
1620 PADAPTER_BINDING AdapterBinding = (PADAPTER_BINDING)Adapter->MiniportAdapterBinding;
1621
1622 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1623
1624 (*AdapterBinding->ProtocolBinding->Chars.RequestCompleteHandler)(
1625 AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
1626 Adapter->NdisMiniportBlock.MediaRequest,
1627 Status);
1628 }
1629
1630
1631 /*
1632 * @implemented
1633 */
1634 VOID
1635 EXPORT
1636 NdisMSetAttributes(
1637 IN NDIS_HANDLE MiniportAdapterHandle,
1638 IN NDIS_HANDLE MiniportAdapterContext,
1639 IN BOOLEAN BusMaster,
1640 IN NDIS_INTERFACE_TYPE AdapterType)
1641 /*
1642 * FUNCTION: Informs the NDIS library of significant features of the caller's NIC
1643 * ARGUMENTS:
1644 * MiniportAdapterHandle = Handle input to MiniportInitialize
1645 * MiniportAdapterContext = Pointer to context information
1646 * BusMaster = Specifies TRUE if the caller's NIC is a busmaster DMA device
1647 * AdapterType = Specifies the I/O bus interface of the caller's NIC
1648 */
1649 {
1650 PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
1651
1652 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1653
1654 Adapter->NdisMiniportBlock.MiniportAdapterContext = MiniportAdapterContext;
1655 Adapter->Attributes = BusMaster? NDIS_ATTRIBUTE_BUS_MASTER : 0;
1656 Adapter->NdisMiniportBlock.AdapterType = AdapterType;
1657 Adapter->AttributesSet = TRUE;
1658 }
1659
1660
1661 /*
1662 * @implemented
1663 */
1664 VOID
1665 EXPORT
1666 NdisMSetAttributesEx(
1667 IN NDIS_HANDLE MiniportAdapterHandle,
1668 IN NDIS_HANDLE MiniportAdapterContext,
1669 IN UINT CheckForHangTimeInSeconds OPTIONAL,
1670 IN ULONG AttributeFlags,
1671 IN NDIS_INTERFACE_TYPE AdapterType)
1672 /*
1673 * FUNCTION: Informs the NDIS library of significant features of the caller's NIC
1674 * ARGUMENTS:
1675 * MiniportAdapterHandle = Handle input to MiniportInitialize
1676 * MiniportAdapterContext = Pointer to context information
1677 * CheckForHangTimeInSeconds = Specifies interval in seconds at which
1678 * MiniportCheckForHang should be called
1679 * AttributeFlags = Bitmask that indicates specific attributes
1680 * AdapterType = Specifies the I/O bus interface of the caller's NIC
1681 */
1682 {
1683 // Currently just like NdisMSetAttributesEx
1684 // TODO: Take CheckForHandTimeInSeconds into account!
1685 PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
1686
1687 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1688 NDIS_DbgPrint(MAX_TRACE, ("NdisMSetAttributesEx() is partly-implemented.\n"));
1689
1690 Adapter->NdisMiniportBlock.MiniportAdapterContext = MiniportAdapterContext;
1691
1692 /* don't know why this is here - anybody? */
1693 Adapter->Attributes = AttributeFlags & NDIS_ATTRIBUTE_BUS_MASTER;
1694 Adapter->NdisMiniportBlock.AdapterType = AdapterType;
1695 Adapter->AttributesSet = TRUE;
1696
1697 if(AttributeFlags & NDIS_ATTRIBUTE_DESERIALIZE)
1698 {
1699 NDIS_DbgPrint(MIN_TRACE, ("Deserialized miniport - UNIMPLEMENTED\n"));
1700 #ifdef DBG
1701 __asm__("int $3\n");
1702 #endif
1703 }
1704 }
1705
1706
1707 /*
1708 * @unimplemented
1709 */
1710 VOID
1711 EXPORT
1712 NdisMSleep(
1713 IN ULONG MicrosecondsToSleep)
1714 {
1715 UNIMPLEMENTED
1716 }
1717
1718
1719 /*
1720 * @unimplemented
1721 */
1722 BOOLEAN
1723 EXPORT
1724 NdisMSynchronizeWithInterrupt(
1725 IN PNDIS_MINIPORT_INTERRUPT Interrupt,
1726 IN PVOID SynchronizeFunction,
1727 IN PVOID SynchronizeContext)
1728 {
1729 UNIMPLEMENTED
1730
1731 return FALSE;
1732 }
1733
1734
1735 /*
1736 * @unimplemented
1737 */
1738 NDIS_STATUS
1739 EXPORT
1740 NdisMWriteLogData(
1741 IN NDIS_HANDLE LogHandle,
1742 IN PVOID LogBuffer,
1743 IN UINT LogBufferSize)
1744 {
1745 UNIMPLEMENTED
1746
1747 return NDIS_STATUS_FAILURE;
1748 }
1749
1750
1751 /*
1752 * @implemented
1753 */
1754 VOID
1755 EXPORT
1756 NdisTerminateWrapper(
1757 IN NDIS_HANDLE NdisWrapperHandle,
1758 IN PVOID SystemSpecific)
1759 /*
1760 * FUNCTION: Releases resources allocated by a call to NdisInitializeWrapper
1761 * ARGUMENTS:
1762 * NdisWrapperHandle = Handle returned by NdisInitializeWrapper (MINIPORT_DRIVER)
1763 * SystemSpecific = Always NULL
1764 */
1765 {
1766 PMINIPORT_DRIVER Miniport = GET_MINIPORT_DRIVER(NdisWrapperHandle);
1767
1768 NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
1769
1770 ExFreePool(Miniport->RegistryPath->Buffer);
1771 ExFreePool(Miniport->RegistryPath);
1772 ExFreePool(Miniport);
1773 }
1774
1775 /* EOF */
1776 /* enum test */
1777 /*
1778 {
1779 ULONG KeyInformationSize;
1780 KEY_BASIC_INFORMATION *KeyInformation;
1781 int i;
1782
1783 KeyInformation = ExAllocatePool(PagedPool, 1024);
1784 ASSERT(KeyInformation);
1785
1786 RtlInitUnicodeString(&LinkageKeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NE2000");
1787 InitializeObjectAttributes(&LinkageKeyAttributes, &LinkageKeyName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
1788
1789 Status = ZwOpenKey(&LinkageKeyHandle, KEY_READ, &LinkageKeyAttributes);
1790 if(!NT_SUCCESS(Status))
1791 {
1792 DbgPrint("ndis!NdisMRegisterMiniport: Failed to open Linkage key: 0x%x\n", Status);
1793 ASSERT(0);
1794 KeBugCheck(0);
1795 }
1796
1797 for(i=0;i<5;i++)
1798 {
1799 Status = ZwEnumerateKey(LinkageKeyHandle, i, KeyBasicInformation, KeyInformation, 1024, &KeyInformationSize);
1800 if(!NT_SUCCESS(Status))
1801 {
1802 DbgPrint("ndis!NdisMRegisterMiniport: Failed to enumerate: 0x%x\n", Status);
1803 break;
1804 }
1805
1806 KeyInformation->Name[KeyInformation->NameLength/sizeof(WCHAR)] = 0;
1807 DbgPrint("ndis!NdisMRegisterMiniport: enumerated key %ws\n", KeyInformation->Name);
1808 }
1809
1810 ExFreePool(KeyInformation);
1811 KeBugCheck(0);
1812 }
1813 */