Watt-32 tcp/ip  2.2 dev-rel.10
swsvpkt.c
1 /****************************************************************************
2  *
3  * File name : vpkt_uio.c
4  * Function : SwsVpkt user mode I/O
5  * Project : SwsVpkt
6  * Authors : Lawrence Rust
7  * Systems : ANSI C Win32
8  *
9  ****************************************************************************
10  *
11  * Created by Lawrence Rust, Software Systems Consultants
12  * lvr@softsystem.co.uk. Tel/Fax +33 5 49 72 79 63
13  *
14  * 15-Jan-06, LVR, Created.
15  *
16  * Aug 2010, GV, changed for Watt-32.
17  *
18  * Comments:
19  * --------
20  * This is an example of how to perform user mode network I/O.
21  *
22  ****************************************************************************
23  */
24 
25 #include "wattcp.h"
26 
27 #if defined(WIN32)
28 
29 #include "misc.h"
30 #include "pcpkt.h"
31 #include "winpkt.h"
32 #include "ntddndis.h"
33 #include "swsvpkt.h"
34 
35 #if defined(__LCC__)
36  #define QueueUserAPC_4th_ARG __int64 *
37 #else
38  #define QueueUserAPC_4th_ARG ULONG_PTR
39 #endif
40 
41 static struct {
42  uint64 rx_pkts;
43  uint64 rx_bytes;
44  uint64 rx_errors;
45  uint64 tx_pkts;
46  uint64 tx_bytes;
47  uint64 tx_errors;
48  } sws_stat;
49 
50 static DWORD WINAPI WorkerThread (void *);
51 static void WINAPI StartReceivingApc (ULONG_PTR);
52 static void WINAPI ReceiveComplete (DWORD, DWORD, OVERLAPPED*);
53 static void WINAPI ResubmitRxBufferApc (ULONG_PTR);
54 static BOOL QueueBuffer (struct SwsVpktUsr*, struct SRxBuffer*);
55 
56 /*
57  * Open a handle to the packet driver
58  */
59 struct SwsVpktUsr *SwsVpktOpen (const char *szName,
60  const struct SwsVpktOpenInfo *pInfo)
61 {
62  struct SwsVpktUsr *pPacket;
63  unsigned uRxBufs;
64  size_t size;
65  DWORD err;
66 
67  winpkt_trace_func = "SwsVpktOpen";
68  WINPKT_TRACE (("name = %s\n", szName));
69 
70  if (!pInfo || !pInfo->pfnRx)
71  return (NULL);
72 
73  uRxBufs = pInfo->uRxBufs > 0 ? pInfo->uRxBufs : 16;
74 
75  /* Allocate instance data */
76  size = sizeof(*pPacket) + uRxBufs * sizeof(pPacket->rxBuffer[0]);
77  pPacket = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, size);
78  if (!pPacket)
79  {
80  err = GetLastError();
81  WINPKT_TRACE (("HeapAlloc() failed; %s\n", win_strerror(err)));
82  SetLastError (err);
83  return (NULL);
84  }
85 
86  pPacket->hDevice = INVALID_HANDLE_VALUE;
87  pPacket->uRxBufs = uRxBufs;
88  pPacket->pfnRx = pInfo->pfnRx;
89 
90  /* Create overlapped Tx done event
91  */
92  pPacket->txOverlap.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
93  if (!pPacket->txOverlap.hEvent)
94  {
95  err = GetLastError();
96  SwsVpktClose (pPacket);
97  SetLastError (err);
98  WINPKT_TRACE (("CreateEvent() failed; %s\n", win_strerror(err)));
99  return (NULL);
100  }
101 
102  /* Attach to packet driver
103  */
104  pPacket->hDevice = CreateFileA (szName, GENERIC_READ | GENERIC_WRITE,
105  FILE_SHARE_READ | FILE_SHARE_WRITE,
106  NULL, /* Security */
107  OPEN_EXISTING,
108  FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
109  NULL); /* Template file */
110  if (pPacket->hDevice == INVALID_HANDLE_VALUE)
111  {
112  err = GetLastError();
113  SwsVpktClose (pPacket);
114  SetLastError (err);
115  WINPKT_TRACE (("CreateEvent() failed; %s\n", win_strerror(err)));
116  return (NULL);
117  }
118 
119  /* Start the worker thread */
120  pPacket->hWorker = CreateThread (NULL, 0, WorkerThread, pPacket,
121  CREATE_SUSPENDED, NULL);
122  if (!pPacket->hWorker)
123  {
124  err = GetLastError();
125  SwsVpktClose (pPacket);
126  SetLastError (err);
127  WINPKT_TRACE (("CreateThread() failed; %s\n", win_strerror(err)));
128  return (NULL);
129  }
130 
131  SetThreadPriority (pPacket->hWorker, THREAD_PRIORITY_ABOVE_NORMAL);
132  ResumeThread (pPacket->hWorker);
133  QueueUserAPC (StartReceivingApc, pPacket->hWorker,
134  (QueueUserAPC_4th_ARG)pPacket);
135  return (pPacket);
136 }
137 
138 /*
139  * Release packet driver resources
140  */
141 BOOL SwsVpktClose (struct SwsVpktUsr *pPacket)
142 {
143  char res[100] = { '\0' };
144  BOOL rc = FALSE;
145 
146  winpkt_trace_func = "SwsVpktClose";
147 
148  /* Signal the worker thread to exit */
149  pPacket->iWorkerExit = 1;
150  if (pPacket->hWorker)
151  {
152  if (WaitForSingleObject(pPacket->hWorker, 300) == WAIT_TIMEOUT)
153  {
154  /* This will occur if the thread is waiting in a critical section
155  * Resolve the deadlock by killing the thread
156  */
157  TerminateThread (pPacket->hWorker, 1);
158  strcpy (res, "thread killed, ");
159  }
160  CloseHandle (pPacket->hWorker);
161  }
162 
163  if (pPacket->hDevice != INVALID_HANDLE_VALUE)
164  {
165  CloseHandle (pPacket->hDevice);
166  pPacket->hDevice = INVALID_HANDLE_VALUE;
167  strcat (res, "device closed, ");
168  rc = TRUE;
169  }
170 
171  if (pPacket->txOverlap.hEvent)
172  {
173  CloseHandle (pPacket->txOverlap.hEvent);
174  strcat (res, "overlap event closed");
175  }
176 
177  WINPKT_TRACE (("rc: %d, res: %s\n", rc, res));
178 
179  HeapFree (GetProcessHeap(), 0, pPacket);
180  return (rc);
181 }
182 
183 /*
184  * Send a frame
185  */
186 UINT SwsVpktSend (struct SwsVpktUsr *pPacket,
187  const void *pvBuf, UINT uLen)
188 {
189  DWORD sent = 0;
190  BOOL bRet, overlapped = FALSE;
191 
192  pPacket->txOverlap.Offset = 0;
193  pPacket->txOverlap.OffsetHigh = 0;
194 
195  bRet = WriteFile (pPacket->hDevice, pvBuf, uLen,
196  &sent, &pPacket->txOverlap);
197 
198  if (!bRet && GetLastError() == ERROR_IO_PENDING)
199  {
200  /* Wait for completion */
201  sent = 0;
202  GetOverlappedResult (pPacket->hDevice, &pPacket->txOverlap,
203  &sent, TRUE);
204  overlapped = TRUE;
205  }
206  if (sent)
207  {
208  sws_stat.tx_pkts++;
209  sws_stat.tx_bytes += sent;
210  }
211  else
212  sws_stat.tx_errors++;
213 
214  winpkt_trace_func = "SwsVpktSend";
215  WINPKT_TRACE (("overlapped: %d, bRet %d; %s\n",
216  overlapped, bRet, sent == 0 ? win_strerror(GetLastError()) : "okay"));
217  return (UINT)sent;
218 }
219 
220 
221 BOOL SwsVpktGetStatistics (const struct SwsVpktUsr *usr, struct PktStats *stats)
222 {
223  stats->in_packets = sws_stat.rx_pkts;
224  stats->in_bytes = sws_stat.rx_bytes;
225  stats->out_packets = sws_stat.tx_pkts;
226  stats->out_bytes = sws_stat.tx_bytes;
227  stats->in_errors = sws_stat.rx_errors;
228  stats->out_errors = sws_stat.tx_errors;
229  return (TRUE);
230 }
231 
232 BOOL SwsVpktGetAdapterState (const struct SwsVpktUsr *usr,
233  struct SwsVpktAdapterState *state)
234 {
235  IOCTL_INFO info;
236  BOOL rc;
237 
238  memset (&info, '\0', sizeof(info));
239 
240  /* Get adapter info */
241  rc = SwsVpktDeviceRequest(
242  usr->hDevice, /* Device handle */
243  (DWORD)IOCTL_GETINFO, /* IOCTL code */
244  NULL, 0, /* -> input buffer & size */
245  &info, /* -> output buffer */
246  sizeof(info), /* input buffer size */
247  NULL); /* Bytes returned */
248  if (rc)
249  {
250  state->isPowerOn = info.bPowerOn;
251  state->isMediaConnected = !info.bMediaDisconnected;
252  state->isWan = info.bWan;
253  state->isWanDown = info.bWanDown;
254  }
255  winpkt_trace_func = "SwsVpktGetAdapterState";
256  WINPKT_TRACE (("rc %d, state: %d,%d,%d,%d\n",
257  rc, state->isPowerOn, state->isMediaConnected,
258  state->isWan, state->isWanDown));
259  return (rc);
260 }
261 
262 /*
263  * Get the adapter's MAC address
264  */
265 BOOL SwsVpktGetMacAddress (const struct SwsVpktUsr *pPacket, mac_address *pMac)
266 {
267  /* Get the device type etc */
268  return SwsVpktDeviceRequest (
269  pPacket->hDevice, /* Device handle */
270  (DWORD)IOCTL_GETMACADDR, /* IOCTL code */
271  NULL, 0, /* -> input buffer & size */
272  pMac, /* -> output buffer */
273  sizeof(*pMac), /* output buffer size */
274  NULL); /* Bytes returned */
275 }
276 
277 /*
278  * Get the adapter's description.
279  * Note: This IOCTL was added in ver 1.0.0.5
280  */
281 BOOL SwsVpktGetDescription (const struct SwsVpktUsr *pPacket, char *descr, size_t max)
282 {
283  return SwsVpktDeviceRequest (
284  pPacket->hDevice, /* Device handle */
285  (DWORD)IOCTL_GETDESC, /* IOCTL code */
286  NULL, 0, /* -> input buffer & size */
287  descr, /* -> output buffer */
288  max, /* output buffer size */
289  NULL); /* Bytes returned */
290 
291 }
292 
293 /*
294  * Get the NDIS version.
295  */
296 BOOL SwsVpktGetNDISversion (const struct SwsVpktUsr *pPacket, DWORD *ver)
297 {
298  return SwsVpktDeviceRequest (
299  pPacket->hDevice, /* Device handle */
300  OID_GEN_DRIVER_VERSION, /* IOCTL code */
301  NULL, 0, /* -> input buffer & size */
302  ver, /* -> output buffer */
303  sizeof(*ver), /* output buffer size */
304  NULL); /* Bytes returned */
305 }
306 
307 /*
308  * Return swsVpkt.sys file-version.
309  */
310 static char swsVpkt_ver[64] = "?";
311 
312 const char *SwsVpktGetDriverVersion (void)
313 {
314  if (swsVpkt_ver[0] == '?')
315  GetFileVersion ("drivers\\swsVpkt.sys", swsVpkt_ver, sizeof(swsVpkt_ver));
316  return (swsVpkt_ver);
317 }
318 
319 /*
320  * Worker thread
321  */
322 static DWORD WINAPI WorkerThread (void *pv)
323 {
324  struct SwsVpktUsr *pPacket = (struct SwsVpktUsr*)pv;
325 
326  /* Execute IO completion callbacks and APC's until exit */
327  while (!pPacket->iWorkerExit)
328  SleepEx (100, TRUE);
329 
330  /* Cancel all pending reads */
331  CancelIo (pPacket->hDevice);
332  SleepEx (10, TRUE);
333  return (0);
334 }
335 
336 /*
337  * APC to start receiving
338  */
339 static void WINAPI StartReceivingApc (ULONG_PTR ulContext)
340 {
341  struct SwsVpktUsr *pPacket = (struct SwsVpktUsr*)ulContext;
342  UINT i;
343 
344  /* Submit all read buffers */
345  for (i = 0; i < pPacket->uRxBufs; i++)
346  QueueBuffer (pPacket, &pPacket->rxBuffer[i]);
347 }
348 
349 /*
350  * Post a receive buffer
351  */
352 static BOOL QueueBuffer (struct SwsVpktUsr *pPacket, struct SRxBuffer *pBuffer)
353 {
354  if (pPacket->iWorkerExit)
355  return (FALSE);
356 
357  memset (&pBuffer->overlap, 0, sizeof(pBuffer->overlap));
358  pBuffer->overlap.hEvent = (HANDLE)pPacket;
359 
360  if (!ReadFileEx(pPacket->hDevice, pBuffer->buffer, sizeof(pBuffer->buffer),
361  &pBuffer->overlap, ReceiveComplete))
362  {
363  DWORD dwErr = GetLastError();
364 
365  switch (dwErr)
366  {
367  case ERROR_OPERATION_ABORTED:
368  break;
369  default:
370  /* Retry later */
371  QueueUserAPC (ResubmitRxBufferApc, pPacket->hWorker, (QueueUserAPC_4th_ARG)pBuffer);
372  break;
373  }
374  return (FALSE);
375  }
376  return (TRUE);
377 }
378 
379 /*
380  * APC to resubmit an RxBuffer
381  */
382 static void WINAPI ResubmitRxBufferApc (ULONG_PTR ulContext)
383 {
384  struct SRxBuffer *pBuffer = (struct SRxBuffer*) ulContext;
385  struct SwsVpktUsr *pPacket = (struct SwsVpktUsr*) pBuffer->overlap.hEvent;
386 
387  /* Resubmit the buffer */
388  QueueBuffer (pPacket, pBuffer);
389 }
390 
391 /*
392  * ReadFileEx completion routine
393  */
394 static void WINAPI ReceiveComplete (
395  DWORD dwErr, /* completion code */
396  DWORD dwBytes, /* number of bytes transferred */
397  OVERLAPPED *lpOverlapped) /* I/O information buffer */
398 {
399  struct SRxBuffer *pBuffer = OVL2RXBUF (lpOverlapped);
400  struct SwsVpktUsr *pPacket = (struct SwsVpktUsr*) pBuffer->overlap.hEvent;
401 
402  winpkt_trace_func = "ReceiveComplete";
403  WINPKT_TRACE (("dwErr %lu, got %lu bytes\n", dwErr, dwBytes));
404 
405  switch (dwErr)
406  {
407  case 0:
408  (*pPacket->pfnRx) (pPacket, pBuffer->buffer, dwBytes);
409  sws_stat.rx_pkts++;
410  sws_stat.rx_bytes += dwBytes;
411  break;
412 
413  case ERROR_OPERATION_ABORTED:
414  sws_stat.rx_errors++;
415  break;
416  }
417 
418  /* Resubmit the buffer */
419  QueueBuffer (pPacket, pBuffer);
420 }
421 
422 static const struct search_list ccode_list[] = {
423  { IOCTL_GETINFO, "IOCTL_GETINFO" },
424  { IOCTL_GETMACADDR, "IOCTL_GETMACADDR" },
425  { IOCTL_GETDESC, "IOCTL_GETDESC" },
426  { OID_GEN_DRIVER_VERSION, "OID_GEN_DRIVER_VERSION" }
427  };
428 
429 /*
430  * Make a synchronous DeviceIoControl call to a handle opened
431  * using FILE_FLAG_OVERLAPPED
432  */
433 BOOL SwsVpktDeviceRequest2 (HANDLE hDevice, DWORD dwIoControlCode,
434  const void *lpInBuffer, DWORD nInBufferSize,
435  void *lpOutBuffer, DWORD nOutBufferSize,
436  DWORD *lpBytesReturned,
437  const char *file, unsigned line)
438 {
439  BOOL bResult;
440  HANDLE hEvent;
441  OVERLAPPED overlap;
442  DWORD bytesReturned, err;
443 
444 #if defined(USE_DEBUG)
445  winpkt_trace_func = "SwsVpktDeviceRequest";
446  winpkt_trace_file = file;
447  winpkt_trace_line = line;
448 #endif
449 
450  /* Create a manual reset event for overlapped wait */
451  hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
452  if (!hEvent)
453  {
454  err = GetLastError();
455 #if defined(USE_DEBUG)
456  winpkt_trace ("CreateEvent failed; %s\n", win_strerror(err));
457 #endif
458  SetLastError (err);
459  return (FALSE);
460  }
461 
462  memset (&overlap, 0, sizeof(overlap));
463  overlap.hEvent = hEvent;
464 
465  if (!lpBytesReturned)
466  lpBytesReturned = &bytesReturned;
467 
468  bResult = DeviceIoControl (hDevice, dwIoControlCode,
469  (void*)lpInBuffer,
470  nInBufferSize, lpOutBuffer,
471  nOutBufferSize, lpBytesReturned,
472  &overlap);
473 
474  if (!bResult && GetLastError() == ERROR_IO_PENDING)
475  {
476  /* Wait for completion */
477  bResult = GetOverlappedResult (hDevice, &overlap, lpBytesReturned, TRUE);
478  }
479 
480  err = GetLastError();
481 
482 #if defined(USE_DEBUG)
483  winpkt_trace ("%s, len %lu/%lu, bResult %d\n",
484  list_lookup(dwIoControlCode,ccode_list,DIM(ccode_list)),
485  nInBufferSize, *lpBytesReturned, bResult);
486 #endif
487 
488  CloseHandle (hEvent);
489  SetLastError (err);
490  return (bResult);
491 }
492 #endif /* WIN32 */
Core definitions.
const char * list_lookup(DWORD type, const struct search_list *list, int num)
Search 'list' for 'type' and return it's name.
Definition: misc.c:929
unsigned long long uint64
our unsigned "long long" type
Definition: wattcp.h:60
Definition: tcp.h:778
BOOL GetFileVersion(const char *file, char *buf, size_t buf_len)
Returns the version of a PE image (.sys, .dll or .exe).
Definition: winmisc.c:219