2004-02-25 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / drivers / net / tcpip / datalink / loopback.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: datalink/loopback.c
5 * PURPOSE: Loopback adapter
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10 #include <tcpip.h>
11 #include <loopback.h>
12 #include <ip.h>
13 #include <address.h>
14 #include <receive.h>
15 #include <transmit.h>
16 #include <routines.h>
17
18
19 WORK_QUEUE_ITEM LoopWorkItem;
20 PIP_INTERFACE Loopback = NULL;
21 /* Indicates wether the loopback interface is currently transmitting */
22 BOOLEAN LoopBusy = FALSE;
23 /* Loopback transmit queue */
24 PNDIS_PACKET LoopQueueHead = (PNDIS_PACKET)NULL;
25 PNDIS_PACKET LoopQueueTail = (PNDIS_PACKET)NULL;
26 /* Spin lock for protecting loopback transmit queue */
27 KSPIN_LOCK LoopQueueLock;
28
29
30 VOID RealTransmit(
31 PVOID Context)
32 /*
33 * FUNCTION: Transmits one or more packet(s) in loopback queue to ourselves
34 * ARGUMENTS:
35 * Context = Pointer to context information (loopback interface)
36 */
37 {
38 PNDIS_PACKET NdisPacket;
39 PNDIS_BUFFER NdisBuffer;
40 IP_PACKET IPPacket;
41 KIRQL OldIrql;
42
43 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
44
45 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
46 KeAcquireSpinLockAtDpcLevel(&LoopQueueLock);
47
48 while (TRUE)
49 {
50 /* Get the next packet from the queue (if any) */
51 NdisPacket = LoopQueueHead;
52 if (!NdisPacket)
53 break;
54
55 TI_DbgPrint(MAX_TRACE, ("NdisPacket (0x%X)\n", NdisPacket));
56
57 LoopQueueHead = *(PNDIS_PACKET*)NdisPacket->u.s3.MacReserved;
58 KeReleaseSpinLockFromDpcLevel(&LoopQueueLock);
59 IPPacket.NdisPacket = NdisPacket;
60
61 NdisGetFirstBufferFromPacket(NdisPacket,
62 &NdisBuffer,
63 &IPPacket.Header,
64 &IPPacket.ContigSize,
65 &IPPacket.TotalSize);
66 IPReceive(Context, &IPPacket);
67 AdjustPacket(NdisPacket, 0, PC(NdisPacket)->DLOffset);
68 PC(NdisPacket)->DLComplete(Context, NdisPacket, NDIS_STATUS_SUCCESS);
69 /* Lower IRQL for a moment to prevent starvation */
70 KeLowerIrql(OldIrql);
71 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
72 KeAcquireSpinLockAtDpcLevel(&LoopQueueLock);
73 }
74
75 LoopBusy = FALSE;
76 KeReleaseSpinLockFromDpcLevel(&LoopQueueLock);
77 KeLowerIrql(OldIrql);
78 }
79
80 VOID LoopTransmit(
81 PVOID Context,
82 PNDIS_PACKET NdisPacket,
83 UINT Offset,
84 PVOID LinkAddress,
85 USHORT Type)
86 /*
87 * FUNCTION: Transmits a packet
88 * ARGUMENTS:
89 * Context = Pointer to context information (NULL)
90 * NdisPacket = Pointer to NDIS packet to send
91 * Offset = Offset in packet where packet data starts
92 * LinkAddress = Pointer to link address
93 * Type = LAN protocol type (unused)
94 */
95 {
96 PNDIS_PACKET *pNdisPacket;
97 KIRQL OldIrql;
98
99 TI_DbgPrint(MAX_TRACE, ("Called.\n"));
100
101 /* NDIS send routines don't have an offset argument so we
102 must offset the data in upper layers and adjust the
103 packet here. We save the offset in the packet context
104 area so it can be undone before we release the packet */
105 AdjustPacket(NdisPacket, Offset, 0);
106 PC(NdisPacket)->DLOffset = Offset;
107
108 pNdisPacket = (PNDIS_PACKET*)NdisPacket->u.s3.MacReserved;
109 *pNdisPacket = NULL;
110
111 KeAcquireSpinLock(&LoopQueueLock, &OldIrql);
112
113 /* Add packet to transmit queue */
114 if (LoopQueueHead != NULL)
115 {
116 /* Transmit queue is not empty */
117 pNdisPacket = (PNDIS_PACKET*)LoopQueueTail->u.s3.MacReserved;
118 *pNdisPacket = NdisPacket;
119 }
120 else
121 {
122 /* Transmit queue is empty */
123 LoopQueueHead = NdisPacket;
124 }
125
126 LoopQueueTail = NdisPacket;
127
128 /* If RealTransmit is not running (or scheduled to run) then schedule it to run now */
129 if (!LoopBusy)
130 {
131 LoopBusy = TRUE; /* The loopback interface is now busy */
132 ExQueueWorkItem(&LoopWorkItem, CriticalWorkQueue);
133 }
134
135 KeReleaseSpinLock(&LoopQueueLock, OldIrql);
136 }
137
138 NDIS_STATUS LoopRegisterAdapter(
139 PNDIS_STRING AdapterName,
140 PLAN_ADAPTER *Adapter)
141 /*
142 * FUNCTION: Registers loopback adapter with the network layer
143 * ARGUMENTS:
144 * AdapterName = Unused
145 * Adapter = Unused
146 * RETURNS:
147 * Status of operation
148 */
149 {
150 PIP_ADDRESS Address;
151 NDIS_STATUS Status;
152
153 Status = NDIS_STATUS_SUCCESS;
154
155 TI_DbgPrint(MID_TRACE, ("Called.\n"));
156
157 Address = AddrBuildIPv4(LOOPBACK_ADDRESS_IPv4);
158 if (Address != NULL)
159 {
160 LLIP_BIND_INFO BindInfo;
161
162 /* Bind the adapter to network (IP) layer */
163 BindInfo.Context = NULL;
164 BindInfo.HeaderSize = 0;
165 BindInfo.MinFrameSize = 0;
166 BindInfo.MTU = 16384;
167 BindInfo.Address = NULL;
168 BindInfo.AddressLength = 0;
169 BindInfo.Transmit = LoopTransmit;
170
171 Loopback = IPCreateInterface(&BindInfo);
172 if ((Loopback != NULL) && (IPCreateNTE(Loopback, Address, 8)))
173 {
174 /* Reference the interface for the NTE. The reference for
175 the address is just passed on to the NTE */
176 ReferenceObject(Loopback);
177
178 IPRegisterInterface(Loopback);
179
180 ExInitializeWorkItem(&LoopWorkItem, RealTransmit, Loopback);
181
182 KeInitializeSpinLock(&LoopQueueLock);
183 LoopBusy = FALSE;
184 }
185 else
186 {
187 Status = NDIS_STATUS_RESOURCES;
188 }
189 }
190 else
191 {
192 Status = NDIS_STATUS_RESOURCES;
193 }
194
195 if (!NT_SUCCESS(Status))
196 {
197 LoopUnregisterAdapter(NULL);
198 }
199
200 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
201
202 return Status;
203 }
204
205
206 NDIS_STATUS LoopUnregisterAdapter(
207 PLAN_ADAPTER Adapter)
208 /*
209 * FUNCTION: Unregisters loopback adapter with the network layer
210 * ARGUMENTS:
211 * Adapter = Unused
212 * RETURNS:
213 * Status of operation
214 * NOTES:
215 * Does not care wether we have registered loopback adapter
216 */
217 {
218 TI_DbgPrint(MID_TRACE, ("Called.\n"));
219
220 if (Loopback != NULL)
221 {
222 IPUnregisterInterface(Loopback);
223 IPDestroyInterface(Loopback);
224 Loopback = NULL;
225 }
226
227 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
228
229 return NDIS_STATUS_SUCCESS;
230 }