win-pvdrivers

annotate xennet/xennet.c @ 91:24963c2b7846

xennet: break up tx and rx locks to prevent recursive acquire
author Andy Grover <andy.grover@oracle.com>
date Wed Jan 02 16:52:52 2008 -0800 (2008-01-02)
parents 93879f914f05
children b55f4ed508b0
rev   line source
andy@11 1 /*
andy@11 2 PV Net Driver for Windows Xen HVM Domains
andy@11 3 Copyright (C) 2007 James Harper
andy@32 4 Copyright (C) 2007 Andrew Grover <andy.grover@oracle.com>
andy@11 5
andy@11 6 This program is free software; you can redistribute it and/or
andy@11 7 modify it under the terms of the GNU General Public License
andy@11 8 as published by the Free Software Foundation; either version 2
andy@11 9 of the License, or (at your option) any later version.
andy@11 10
andy@11 11 This program is distributed in the hope that it will be useful,
andy@11 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
andy@11 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
andy@11 14 GNU General Public License for more details.
andy@11 15
andy@11 16 You should have received a copy of the GNU General Public License
andy@11 17 along with this program; if not, write to the Free Software
andy@11 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
andy@11 19 */
andy@11 20
andy@22 21 #include <stdlib.h>
andy@22 22 #include <io/xenbus.h>
andy@11 23 #include "xennet.h"
andy@11 24
andy@51 25 /* Xen macros use these, so they need to be redefined to Win equivs */
andy@32 26 #define wmb() KeMemoryBarrier()
andy@32 27 #define mb() KeMemoryBarrier()
andy@32 28
andy@11 29 #if !defined (NDIS51_MINIPORT)
andy@11 30 #error requires NDIS 5.1 compilation environment
andy@11 31 #endif
andy@11 32
andy@30 33 #define GRANT_INVALID_REF 0
andy@11 34
andy@30 35 /* couldn't get regular xen ring macros to work...*/
andy@30 36 #define __NET_RING_SIZE(type, _sz) \
andy@30 37 (__RD32( \
andy@30 38 (_sz - sizeof(struct type##_sring) + sizeof(union type##_sring_entry)) \
andy@30 39 / sizeof(union type##_sring_entry)))
andy@30 40
andy@30 41 #define NET_TX_RING_SIZE __NET_RING_SIZE(netif_tx, PAGE_SIZE)
andy@30 42 #define NET_RX_RING_SIZE __NET_RING_SIZE(netif_rx, PAGE_SIZE)
andy@11 43
andy@32 44 #pragma warning(disable: 4127) // conditional expression is constant
andy@14 45
andy@11 46 struct xennet_info
andy@11 47 {
andy@11 48 PDEVICE_OBJECT pdo;
andy@11 49 PDEVICE_OBJECT fdo;
andy@11 50 PDEVICE_OBJECT lower_do;
andy@14 51 WDFDEVICE wdf_device;
james@76 52 PXENPCI_XEN_DEVICE_DATA pdoData;
andy@11 53
andy@11 54 WCHAR name[NAME_SIZE];
andy@11 55 NDIS_HANDLE adapter_handle;
andy@11 56 ULONG packet_filter;
andy@23 57 int connected;
andy@22 58 UINT8 perm_mac_addr[ETH_ALEN];
andy@22 59 UINT8 curr_mac_addr[ETH_ALEN];
andy@11 60
james@77 61 // char Path[128];
andy@11 62 char BackendPath[128];
james@76 63 XEN_IFACE XenInterface;
andy@11 64
andy@30 65 /* ring control structures */
andy@11 66 struct netif_tx_front_ring tx;
andy@11 67 struct netif_rx_front_ring rx;
andy@11 68
andy@30 69 /* ptrs to the actual rings themselvves */
andy@30 70 struct netif_tx_sring *tx_pgs;
andy@30 71 struct netif_rx_sring *rx_pgs;
andy@30 72
andy@30 73 /* MDLs for the above */
andy@14 74 PMDL tx_mdl;
andy@14 75 PMDL rx_mdl;
andy@30 76
andy@30 77 /* Outstanding packets. The first entry in tx_pkts
andy@30 78 * is an index into a chain of free entries. */
andy@30 79 PNDIS_PACKET tx_pkts[NET_TX_RING_SIZE];
andy@30 80 PNDIS_PACKET rx_pkts[NET_RX_RING_SIZE];
andy@30 81
andy@30 82 grant_ref_t gref_tx_head;
andy@30 83 grant_ref_t grant_tx_ref[NET_TX_RING_SIZE];
andy@30 84 grant_ref_t gref_rx_head;
andy@30 85 grant_ref_t grant_rx_ref[NET_TX_RING_SIZE];
andy@11 86
andy@11 87 UINT irq;
andy@14 88 evtchn_port_t event_channel;
andy@11 89
andy@11 90 grant_ref_t tx_ring_ref;
andy@11 91 grant_ref_t rx_ring_ref;
andy@43 92
james@76 93 /* Receive-ring batched refills. */
andy@43 94 #define RX_MIN_TARGET 8
andy@43 95 #define RX_DFL_MIN_TARGET 64
andy@43 96 #define RX_MAX_TARGET min(NET_RX_RING_SIZE, 256)
andy@43 97 ULONG rx_target;
andy@43 98 ULONG rx_max_target;
andy@43 99 ULONG rx_min_target;
andy@43 100
andy@43 101 NDIS_HANDLE packet_pool;
andy@43 102 NDIS_HANDLE buffer_pool;
andy@51 103
andy@51 104 /* stats */
andy@51 105 ULONG64 stat_tx_ok;
andy@51 106 ULONG64 stat_rx_ok;
andy@51 107 ULONG64 stat_tx_error;
andy@51 108 ULONG64 stat_rx_error;
andy@51 109 ULONG64 stat_rx_no_buffer;
james@77 110
james@85 111 // KEVENT backend_ready_event;
james@85 112 KEVENT backend_state_change_event;
james@85 113
james@85 114 ULONG state;
james@85 115 ULONG backend_state;
james@85 116
andy@91 117 KSPIN_LOCK RxLock;
andy@91 118 KSPIN_LOCK TxLock;
andy@11 119 };
andy@11 120
andy@22 121 /* This function copied from linux's lib/vsprintf.c, see it for attribution */
andy@22 122 static unsigned long
andy@22 123 simple_strtoul(const char *cp,char **endp,unsigned int base)
andy@22 124 {
andy@22 125 unsigned long result = 0,value;
andy@22 126
andy@22 127 if (!base) {
andy@22 128 base = 10;
andy@22 129 if (*cp == '0') {
andy@22 130 base = 8;
andy@22 131 cp++;
andy@22 132 if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
andy@22 133 cp++;
andy@22 134 base = 16;
andy@22 135 }
andy@22 136 }
andy@22 137 } else if (base == 16) {
andy@22 138 if (cp[0] == '0' && toupper(cp[1]) == 'X')
andy@22 139 cp += 2;
andy@22 140 }
andy@22 141 while (isxdigit(*cp) &&
andy@22 142 (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
andy@22 143 result = result*base + value;
andy@22 144 cp++;
andy@22 145 }
andy@22 146 if (endp)
andy@22 147 *endp = (char *)cp;
andy@22 148 return result;
andy@22 149 }
andy@22 150
andy@30 151 static void
andy@30 152 add_id_to_freelist(NDIS_PACKET **list, unsigned short id)
andy@30 153 {
andy@30 154 list[id] = list[0];
andy@30 155 list[0] = (void *)(unsigned long)id;
andy@30 156 }
andy@30 157
andy@30 158 static unsigned short
andy@30 159 get_id_from_freelist(NDIS_PACKET **list)
andy@30 160 {
andy@30 161 unsigned short id = (unsigned short)(unsigned long)list[0];
andy@30 162 list[0] = list[id];
andy@30 163 return id;
andy@30 164 }
andy@30 165
james@80 166 // Called at DISPATCH_LEVEL with spinlock held
andy@30 167 static NDIS_STATUS
andy@43 168 XenNet_TxBufferGC(struct xennet_info *xi)
andy@30 169 {
andy@42 170 RING_IDX cons, prod;
andy@30 171 unsigned short id;
andy@42 172 PNDIS_PACKET pkt;
andy@42 173 PMDL pmdl;
andy@91 174 KIRQL OldIrql;
andy@30 175
andy@42 176 ASSERT(xi->connected);
andy@30 177
james@79 178 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 179
andy@91 180 KeAcquireSpinLock(&xi->TxLock, &OldIrql);
andy@91 181
andy@30 182 do {
andy@42 183 prod = xi->tx.sring->rsp_prod;
andy@42 184 KeMemoryBarrier(); /* Ensure we see responses up to 'rp'. */
andy@30 185
andy@42 186 for (cons = xi->tx.rsp_cons; cons != prod; cons++) {
andy@30 187 struct netif_tx_response *txrsp;
andy@30 188
andy@42 189 txrsp = RING_GET_RESPONSE(&xi->tx, cons);
andy@30 190 if (txrsp->status == NETIF_RSP_NULL)
andy@30 191 continue;
andy@30 192
andy@30 193 id = txrsp->id;
andy@42 194 pkt = xi->tx_pkts[id];
james@76 195 xi->XenInterface.GntTbl_EndAccess(xi->XenInterface.InterfaceHeader.Context,
andy@42 196 xi->grant_tx_ref[id]);
andy@42 197 xi->grant_tx_ref[id] = GRANT_INVALID_REF;
andy@42 198 add_id_to_freelist(xi->tx_pkts, id);
andy@42 199
andy@43 200 /* free linearized data page */
andy@42 201 pmdl = *(PMDL *)pkt->MiniportReservedEx;
james@78 202 NdisFreeMemory(MmGetMdlVirtualAddress(pmdl), 0, 0); // <= DISPATCH_LEVEL
andy@42 203 IoFreeMdl(pmdl);
andy@42 204
andy@51 205 xi->stat_tx_ok++;
andy@42 206 NdisMSendComplete(xi->adapter_handle, pkt, NDIS_STATUS_SUCCESS);
andy@30 207 }
andy@30 208
andy@42 209 xi->tx.rsp_cons = prod;
andy@30 210
andy@30 211 /*
andy@30 212 * Set a new event, then check for race with update of tx_cons.
andy@30 213 * Note that it is essential to schedule a callback, no matter
andy@30 214 * how few buffers are pending. Even if there is space in the
andy@30 215 * transmit ring, higher layers may be blocked because too much
andy@30 216 * data is outstanding: in such cases notification from Xen is
andy@30 217 * likely to be the only kick that we'll get.
andy@30 218 */
andy@42 219 xi->tx.sring->rsp_event =
andy@42 220 prod + ((xi->tx.sring->req_prod - prod) >> 1) + 1;
andy@51 221 KeMemoryBarrier();
andy@42 222 } while ((cons == prod) && (prod != xi->tx.sring->rsp_prod));
andy@87 223
andy@42 224 /* if queued packets, send them now?
andy@42 225 network_maybe_wake_tx(dev); */
andy@30 226
andy@91 227 KeReleaseSpinLock(&xi->TxLock, OldIrql);
andy@91 228
james@79 229 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
james@78 230
andy@30 231 return NDIS_STATUS_SUCCESS;
andy@30 232 }
andy@30 233
andy@43 234 static NDIS_STATUS
andy@43 235 XenNet_AllocRXBuffers(struct xennet_info *xi)
andy@43 236 {
andy@43 237 unsigned short id;
andy@43 238 PNDIS_PACKET packet;
andy@43 239 PNDIS_BUFFER buffer;
andy@43 240 int i, batch_target, notify;
andy@43 241 RING_IDX req_prod = xi->rx.req_prod_pvt;
andy@43 242 grant_ref_t ref;
andy@43 243 netif_rx_request_t *req;
andy@43 244 NDIS_STATUS status;
andy@43 245 PVOID start;
andy@43 246
james@79 247 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 248
andy@43 249 batch_target = xi->rx_target - (req_prod - xi->rx.rsp_cons);
andy@43 250 for (i = 0; i < batch_target; i++)
andy@43 251 {
andy@43 252 /*
andy@43 253 * Allocate a packet, page, and buffer. Hook them up.
andy@43 254 */
andy@44 255 status = NdisAllocateMemoryWithTag(&start, PAGE_SIZE, XENNET_POOL_TAG);
andy@44 256 if (status != NDIS_STATUS_SUCCESS)
andy@44 257 {
andy@44 258 KdPrint(("NdisAllocateMemoryWithTag Failed! status = 0x%x\n", status));
andy@44 259 break;
andy@44 260 }
andy@44 261 NdisAllocateBuffer(&status, &buffer, xi->buffer_pool, start, PAGE_SIZE);
andy@44 262 if (status != NDIS_STATUS_SUCCESS)
andy@44 263 {
andy@44 264 KdPrint(("NdisAllocateBuffer Failed! status = 0x%x\n", status));
andy@44 265 NdisFreeMemory(start, 0, 0);
andy@44 266 break;
andy@44 267 }
andy@43 268 NdisAllocatePacket(&status, &packet, xi->packet_pool);
andy@43 269 if (status != NDIS_STATUS_SUCCESS)
andy@43 270 {
andy@43 271 KdPrint(("NdisAllocatePacket Failed! status = 0x%x\n", status));
andy@44 272 NdisFreeMemory(start, 0, 0);
andy@44 273 NdisFreeBuffer(buffer);
andy@43 274 break;
andy@43 275 }
andy@43 276 NdisChainBufferAtBack(packet, buffer);
andy@65 277 NDIS_SET_PACKET_HEADER_SIZE(packet, XN_HDR_SIZE);
andy@43 278
andy@43 279 /* Give to netback */
andy@43 280 id = (unsigned short)(req_prod + i) & (NET_RX_RING_SIZE - 1);
andy@43 281 ASSERT(!xi->rx_pkts[id]);
andy@43 282 xi->rx_pkts[id] = packet;
andy@43 283 req = RING_GET_REQUEST(&xi->rx, req_prod + i);
andy@44 284 /* an NDIS_BUFFER is just a MDL, so we can get its pfn array */
james@76 285 ref = xi->XenInterface.GntTbl_GrantAccess(
james@76 286 xi->XenInterface.InterfaceHeader.Context, 0,
andy@44 287 *MmGetMdlPfnArray(buffer), FALSE);
andy@43 288 ASSERT((signed short)ref >= 0);
andy@43 289 xi->grant_rx_ref[id] = ref;
andy@43 290
andy@43 291 req->id = id;
andy@43 292 req->gref = ref;
andy@43 293 }
andy@43 294
andy@43 295 xi->rx.req_prod_pvt = req_prod + i;
andy@43 296 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xi->rx, notify);
andy@43 297 if (notify)
andy@43 298 {
james@76 299 xi->XenInterface.EvtChn_Notify(xi->XenInterface.InterfaceHeader.Context,
andy@43 300 xi->event_channel);
andy@43 301 }
andy@43 302
james@79 303 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
james@78 304
andy@43 305 return NDIS_STATUS_SUCCESS;
andy@43 306 }
andy@43 307
james@80 308 // Called at DISPATCH_LEVEL with spinlock held
andy@43 309 static NDIS_STATUS
andy@43 310 XenNet_RxBufferCheck(struct xennet_info *xi)
andy@43 311 {
andy@43 312 RING_IDX cons, prod;
andy@43 313
andy@43 314 PNDIS_PACKET pkt;
andy@43 315 PNDIS_BUFFER buffer;
andy@43 316 PVOID buff_va;
andy@43 317 UINT buff_len;
andy@43 318 UINT tot_buff_len;
james@78 319 int moretodo;
andy@91 320 KIRQL OldIrql;
james@78 321
james@79 322 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
andy@43 323
andy@43 324 ASSERT(xi->connected);
andy@43 325
andy@91 326 KeAcquireSpinLock(&xi->RxLock, &OldIrql);
andy@91 327
andy@43 328 do {
andy@43 329 prod = xi->rx.sring->rsp_prod;
andy@43 330 KeMemoryBarrier(); /* Ensure we see responses up to 'rp'. */
andy@43 331
andy@43 332 for (cons = xi->rx.rsp_cons; cons != prod; cons++) {
andy@43 333 struct netif_rx_response *rxrsp;
andy@43 334
andy@43 335 rxrsp = RING_GET_RESPONSE(&xi->rx, cons);
andy@43 336 if (rxrsp->status == NETIF_RSP_NULL)
andy@43 337 continue;
andy@43 338
james@80 339 // KdPrint((__DRIVER_NAME " Got a packet\n"));
james@78 340
andy@45 341 pkt = xi->rx_pkts[rxrsp->id];
andy@45 342 xi->rx_pkts[rxrsp->id] = NULL;
james@76 343 xi->XenInterface.GntTbl_EndAccess(xi->XenInterface.InterfaceHeader.Context,
andy@45 344 xi->grant_rx_ref[rxrsp->id]);
andy@45 345 xi->grant_rx_ref[rxrsp->id] = GRANT_INVALID_REF;
andy@43 346
andy@43 347 NdisGetFirstBufferFromPacketSafe(pkt, &buffer, &buff_va, &buff_len,
andy@43 348 &tot_buff_len, NormalPagePriority);
andy@43 349 ASSERT(rxrsp->offset == 0);
andy@43 350 ASSERT(rxrsp->status > 0);
andy@43 351 NdisAdjustBufferLength(buffer, rxrsp->status);
andy@43 352
andy@51 353 xi->stat_rx_ok++;
andy@57 354 NDIS_SET_PACKET_STATUS(pkt, NDIS_STATUS_SUCCESS);
andy@57 355
andy@57 356 /* just indicate 1 packet for now */
james@80 357 // KdPrint((__DRIVER_NAME " Indicating Received\n"));
andy@57 358 NdisMIndicateReceivePacket(xi->adapter_handle, &pkt, 1);
james@80 359 // KdPrint((__DRIVER_NAME " Done Indicating Received\n"));
andy@43 360 }
andy@43 361
andy@43 362 xi->rx.rsp_cons = prod;
andy@43 363
james@78 364 RING_FINAL_CHECK_FOR_RESPONSES(&xi->rx, moretodo);
james@78 365 } while (moretodo);
andy@43 366
andy@43 367 /* Give netback more buffers */
andy@43 368 XenNet_AllocRXBuffers(xi);
andy@43 369
andy@91 370 KeReleaseSpinLock(&xi->RxLock, OldIrql);
andy@91 371
james@78 372 //xi->rx.sring->rsp_event = xi->rx.rsp_cons + 1;
james@78 373
james@79 374 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
james@78 375
andy@43 376 return NDIS_STATUS_SUCCESS;
andy@43 377 }
andy@43 378
james@80 379 // Called at DISPATCH_LEVEL
andy@11 380 static BOOLEAN
andy@11 381 XenNet_Interrupt(
andy@11 382 PKINTERRUPT Interrupt,
andy@11 383 PVOID ServiceContext
andy@11 384 )
andy@11 385 {
andy@30 386 struct xennet_info *xi = ServiceContext;
andy@14 387
andy@14 388 UNREFERENCED_PARAMETER(Interrupt);
andy@11 389
james@79 390 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 391
andy@30 392 if (xi->connected)
andy@30 393 {
andy@43 394 XenNet_TxBufferGC(xi);
andy@43 395 XenNet_RxBufferCheck(xi);
andy@30 396 }
james@78 397
james@79 398 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@11 399
andy@11 400 return TRUE;
andy@11 401 }
andy@11 402
andy@22 403 static VOID
andy@22 404 XenNet_BackEndStateHandler(char *Path, PVOID Data)
andy@22 405 {
andy@22 406 struct xennet_info *xi = Data;
andy@22 407 char *Value;
andy@23 408 char TmpPath[128];
andy@23 409 xenbus_transaction_t xbt = 0;
andy@23 410 int retry = 0;
andy@23 411 char *err;
andy@32 412 int i;
andy@32 413
andy@32 414 struct set_params {
andy@32 415 char *name;
andy@32 416 int value;
andy@32 417 } params[] = {
andy@32 418 {"tx-ring-ref", 0},
andy@32 419 {"rx-ring-ref", 0},
andy@32 420 {"event-channel", 0},
andy@32 421 {"request-rx-copy", 1},
james@78 422 #if 0 // these seemed to cause kernel messages about checksums
andy@32 423 {"feature-rx-notify", 1},
andy@32 424 {"feature-no-csum-offload", 1},
andy@32 425 {"feature-sg", 1},
andy@32 426 {"feature-gso-tcpv4", 0},
james@78 427 #endif
andy@32 428 {NULL, 0},
andy@32 429 };
andy@22 430
james@76 431 xi->XenInterface.XenBus_Read(xi->XenInterface.InterfaceHeader.Context,
andy@22 432 XBT_NIL, Path, &Value);
james@85 433 xi->backend_state = atoi(Value);
james@77 434 xi->XenInterface.FreeMem(Value);
andy@22 435
james@85 436 switch (xi->backend_state)
andy@22 437 {
andy@22 438 case XenbusStateUnknown:
andy@22 439 KdPrint((__DRIVER_NAME " Backend State Changed to Unknown\n"));
andy@22 440 break;
andy@22 441
andy@22 442 case XenbusStateInitialising:
andy@22 443 KdPrint((__DRIVER_NAME " Backend State Changed to Initialising\n"));
andy@22 444 break;
andy@22 445
andy@22 446 case XenbusStateInitWait:
andy@22 447 KdPrint((__DRIVER_NAME " Backend State Changed to InitWait\n"));
andy@22 448
james@76 449 xi->event_channel = xi->XenInterface.EvtChn_AllocUnbound(
james@76 450 xi->XenInterface.InterfaceHeader.Context, 0);
james@78 451 xi->XenInterface.EvtChn_BindDpc(xi->XenInterface.InterfaceHeader.Context,
andy@23 452 xi->event_channel, XenNet_Interrupt, xi);
andy@23 453
andy@23 454 /* TODO: must free pages in MDL as well as MDL using MmFreePagesFromMdl and ExFreePool */
andy@23 455 // or, allocate mem and then get mdl, then free mdl
andy@23 456 xi->tx_mdl = AllocatePage();
james@75 457 xi->tx_pgs = MmGetMdlVirtualAddress(xi->tx_mdl); //MmMapLockedPagesSpecifyCache(xi->tx_mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
andy@23 458 SHARED_RING_INIT(xi->tx_pgs);
andy@23 459 FRONT_RING_INIT(&xi->tx, xi->tx_pgs, PAGE_SIZE);
james@76 460 xi->tx_ring_ref = xi->XenInterface.GntTbl_GrantAccess(
james@76 461 xi->XenInterface.InterfaceHeader.Context, 0,
andy@23 462 *MmGetMdlPfnArray(xi->tx_mdl), FALSE);
andy@23 463
andy@23 464 xi->rx_mdl = AllocatePage();
james@75 465 xi->rx_pgs = MmGetMdlVirtualAddress(xi->rx_mdl); //MmMapLockedPagesSpecifyCache(xi->rx_mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
andy@23 466 SHARED_RING_INIT(xi->rx_pgs);
andy@23 467 FRONT_RING_INIT(&xi->rx, xi->rx_pgs, PAGE_SIZE);
james@76 468 xi->rx_ring_ref = xi->XenInterface.GntTbl_GrantAccess(
james@76 469 xi->XenInterface.InterfaceHeader.Context, 0,
andy@23 470 *MmGetMdlPfnArray(xi->rx_mdl), FALSE);
andy@23 471
andy@32 472 /* fixup array for dynamic values */
andy@32 473 params[0].value = xi->tx_ring_ref;
andy@32 474 params[1].value = xi->rx_ring_ref;
andy@32 475 params[2].value = xi->event_channel;
andy@23 476
james@76 477 xi->XenInterface.XenBus_StartTransaction(
james@76 478 xi->XenInterface.InterfaceHeader.Context, &xbt);
andy@23 479
andy@32 480 for (i = 0; params[i].name; i++)
andy@32 481 {
andy@32 482 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/%s",
james@77 483 xi->pdoData->Path, params[i].name);
james@76 484 err = xi->XenInterface.XenBus_Printf(xi->XenInterface.InterfaceHeader.Context,
andy@32 485 XBT_NIL, TmpPath, "%d", params[i].value);
andy@32 486 if (err)
andy@32 487 {
andy@32 488 KdPrint(("setting %s failed, err = %s\n", params[i].name, err));
andy@32 489 goto trouble;
andy@32 490 }
andy@32 491 }
andy@23 492
andy@23 493 /* commit transaction */
james@76 494 xi->XenInterface.XenBus_EndTransaction(xi->XenInterface.InterfaceHeader.Context,
andy@23 495 xbt, 0, &retry);
andy@23 496
andy@43 497 XenNet_AllocRXBuffers(xi);
andy@22 498
james@85 499 xi->state = XenbusStateConnected;
andy@33 500 KdPrint((__DRIVER_NAME " Set Frontend state to Connected\n"));
james@77 501 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/state", xi->pdoData->Path);
james@76 502 xi->XenInterface.XenBus_Printf(xi->XenInterface.InterfaceHeader.Context,
james@85 503 XBT_NIL, TmpPath, "%d", xi->state);
andy@23 504
andy@23 505 /* send fake arp? */
andy@23 506
andy@23 507 xi->connected = TRUE;
andy@23 508
andy@22 509 break;
andy@22 510
andy@22 511 case XenbusStateInitialised:
andy@22 512 KdPrint((__DRIVER_NAME " Backend State Changed to Initialised\n"));
andy@22 513 break;
andy@22 514
andy@22 515 case XenbusStateConnected:
andy@22 516 KdPrint((__DRIVER_NAME " Backend State Changed to Connected\n"));
andy@22 517 break;
andy@22 518
andy@22 519 case XenbusStateClosing:
andy@22 520 KdPrint((__DRIVER_NAME " Backend State Changed to Closing\n"));
andy@22 521 break;
andy@22 522
andy@22 523 case XenbusStateClosed:
andy@22 524 KdPrint((__DRIVER_NAME " Backend State Changed to Closed\n"));
andy@22 525 break;
andy@22 526
andy@22 527 default:
james@85 528 KdPrint((__DRIVER_NAME " Backend State Changed to Undefined = %d\n", xi->backend_state));
andy@22 529 break;
andy@22 530 }
andy@22 531
james@85 532 KeSetEvent(&xi->backend_state_change_event, 1, FALSE);
james@85 533
andy@32 534 return;
andy@32 535
andy@23 536 trouble:
andy@23 537 KdPrint((__DRIVER_NAME __FUNCTION__ ": Should never happen!\n"));
james@76 538 xi->XenInterface.XenBus_EndTransaction(xi->XenInterface.InterfaceHeader.Context,
andy@23 539 xbt, 1, &retry);
andy@23 540
andy@22 541 }
andy@22 542
andy@11 543 VOID
andy@11 544 XenNet_Halt(
andy@11 545 IN NDIS_HANDLE MiniportAdapterContext
andy@11 546 )
andy@11 547 {
james@85 548 struct xennet_info *xi = MiniportAdapterContext;
james@85 549 CHAR TmpPath[128];
james@85 550
james@85 551 KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@85 552 KdPrint((__DRIVER_NAME " IRQL = %d\n", KeGetCurrentIrql()));
james@85 553
james@85 554 // set frontend state to 'closing'
james@85 555 xi->state = XenbusStateClosing;
james@85 556 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/state", xi->pdoData->Path);
james@85 557 xi->XenInterface.XenBus_Printf(xi->XenInterface.InterfaceHeader.Context,
james@85 558 XBT_NIL, TmpPath, "%d", xi->state);
james@85 559
james@85 560 // wait for backend to set 'Closing' state
james@85 561 while (xi->backend_state != XenbusStateClosing)
james@85 562 KeWaitForSingleObject(&xi->backend_state_change_event, Executive, KernelMode, FALSE, NULL);
james@85 563
james@85 564 // set frontend state to 'closed'
james@85 565 xi->state = XenbusStateClosed;
james@85 566 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/state", xi->pdoData->Path);
james@85 567 xi->XenInterface.XenBus_Printf(xi->XenInterface.InterfaceHeader.Context,
james@85 568 XBT_NIL, TmpPath, "%d", xi->state);
james@85 569
james@85 570 // wait for backend to set 'Closed' state
james@85 571 while (xi->backend_state != XenbusStateClosed)
james@85 572 KeWaitForSingleObject(&xi->backend_state_change_event, Executive, KernelMode, FALSE, NULL);
james@85 573
james@85 574 // remove event channel xenbus entry
james@85 575 // unbind event channel
james@85 576 // remove tx/rx ring entries
james@85 577 // clean up all grant table entries
james@85 578 // free all memory
james@85 579
james@85 580 KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@11 581 }
andy@11 582
andy@11 583 static NDIS_STATUS
andy@11 584 XenNet_Init(
andy@11 585 OUT PNDIS_STATUS OpenErrorStatus,
andy@11 586 OUT PUINT SelectedMediumIndex,
andy@11 587 IN PNDIS_MEDIUM MediumArray,
andy@11 588 IN UINT MediumArraySize,
andy@11 589 IN NDIS_HANDLE MiniportAdapterHandle,
andy@11 590 IN NDIS_HANDLE WrapperConfigurationContext
andy@11 591 )
andy@11 592 {
andy@11 593 NDIS_STATUS status;
andy@11 594 UINT i;
andy@11 595 BOOLEAN medium_found = FALSE;
andy@11 596 struct xennet_info *xi = NULL;
andy@14 597 ULONG length;
andy@11 598 WDF_OBJECT_ATTRIBUTES wdf_attrs;
andy@90 599 char *res;
andy@21 600 char *Value;
andy@21 601 char TmpPath[128];
andy@14 602
andy@14 603 UNREFERENCED_PARAMETER(OpenErrorStatus);
andy@14 604 UNREFERENCED_PARAMETER(WrapperConfigurationContext);
andy@11 605
andy@11 606 /* deal with medium stuff */
andy@11 607 for (i = 0; i < MediumArraySize; i++)
andy@11 608 {
andy@11 609 if (MediumArray[i] == NdisMedium802_3)
andy@11 610 {
andy@11 611 medium_found = TRUE;
andy@11 612 break;
andy@11 613 }
andy@11 614 }
andy@11 615 if (!medium_found)
andy@11 616 {
andy@11 617 KdPrint(("NIC_MEDIA_TYPE not in MediumArray\n"));
andy@11 618 return NDIS_STATUS_UNSUPPORTED_MEDIA;
andy@11 619 }
andy@11 620 *SelectedMediumIndex = i;
andy@11 621
andy@11 622 /* Alloc memory for adapter private info */
andy@11 623 status = NdisAllocateMemoryWithTag(&xi, sizeof(*xi), XENNET_POOL_TAG);
andy@11 624 if (!NT_SUCCESS(status))
andy@11 625 {
andy@11 626 KdPrint(("NdisAllocateMemoryWithTag failed with 0x%x\n", status));
andy@11 627 status = NDIS_STATUS_RESOURCES;
andy@11 628 goto err;
andy@11 629 }
andy@11 630 RtlZeroMemory(xi, sizeof(*xi));
andy@11 631
andy@11 632 /* init xennet_info */
andy@11 633 xi->adapter_handle = MiniportAdapterHandle;
andy@43 634 xi->rx_target = RX_DFL_MIN_TARGET;
andy@43 635 xi->rx_min_target = RX_DFL_MIN_TARGET;
andy@43 636 xi->rx_max_target = RX_MAX_TARGET;
andy@43 637
james@85 638 xi->state = XenbusStateUnknown;
james@85 639 xi->backend_state = XenbusStateUnknown;
james@85 640
andy@91 641 KeInitializeSpinLock(&xi->TxLock);
andy@91 642 KeInitializeSpinLock(&xi->RxLock);
james@80 643
andy@43 644 NdisAllocatePacketPool(&status, &xi->packet_pool, NET_RX_RING_SIZE,
andy@43 645 PROTOCOL_RESERVED_SIZE_IN_PACKET);
andy@43 646 if (status != NDIS_STATUS_SUCCESS)
andy@43 647 {
andy@43 648 KdPrint(("NdisAllocatePacketPool failed with 0x%x\n", status));
andy@43 649 status = NDIS_STATUS_RESOURCES;
andy@43 650 goto err;
andy@43 651 }
andy@43 652
andy@43 653 NdisAllocateBufferPool(&status, &xi->buffer_pool, NET_RX_RING_SIZE);
andy@43 654 if (status != NDIS_STATUS_SUCCESS)
andy@43 655 {
andy@43 656 KdPrint(("NdisAllocateBufferPool failed with 0x%x\n", status));
andy@43 657 status = NDIS_STATUS_RESOURCES;
andy@43 658 goto err;
andy@43 659 }
andy@43 660
andy@11 661 NdisMGetDeviceProperty(MiniportAdapterHandle, &xi->pdo, &xi->fdo,
andy@11 662 &xi->lower_do, NULL, NULL);
james@76 663 xi->pdoData = (PXENPCI_XEN_DEVICE_DATA)xi->pdo->DeviceExtension;
andy@11 664
andy@51 665 /* Initialize tx_pkts as a free chain containing every entry. */
andy@30 666 for (i = 0; i <= NET_TX_RING_SIZE; i++) {
andy@30 667 xi->tx_pkts[i] = (void *)((unsigned long) i+1);
andy@30 668 xi->grant_tx_ref[i] = GRANT_INVALID_REF;
andy@30 669 }
andy@30 670
andy@30 671 for (i = 0; i < NET_RX_RING_SIZE; i++) {
andy@30 672 xi->rx_pkts[i] = NULL;
andy@30 673 xi->grant_rx_ref[i] = GRANT_INVALID_REF;
andy@30 674 }
andy@30 675
james@78 676 xi->packet_filter = NDIS_PACKET_TYPE_PROMISCUOUS;
james@78 677
andy@11 678 status = IoGetDeviceProperty(xi->pdo, DevicePropertyDeviceDescription,
andy@11 679 NAME_SIZE, xi->name, &length);
andy@11 680 if (!NT_SUCCESS(status))
andy@11 681 {
andy@11 682 KdPrint(("IoGetDeviceProperty failed with 0x%x\n", status));
andy@11 683 status = NDIS_STATUS_FAILURE;
andy@11 684 goto err;
andy@11 685 }
andy@11 686
andy@11 687 NdisMSetAttributesEx(xi->adapter_handle, (NDIS_HANDLE) xi,
andy@32 688 0, (NDIS_ATTRIBUTE_DESERIALIZE /*| NDIS_ATTRIBUTE_BUS_MASTER*/),
andy@32 689 NdisInterfaceInternal);
andy@32 690
andy@50 691 WDF_OBJECT_ATTRIBUTES_INIT(&wdf_attrs);
andy@11 692
andy@11 693 status = WdfDeviceMiniportCreate(WdfGetDriver(), &wdf_attrs, xi->fdo,
andy@11 694 xi->lower_do, xi->pdo, &xi->wdf_device);
andy@11 695 if (!NT_SUCCESS(status))
andy@11 696 {
andy@11 697 KdPrint(("WdfDeviceMiniportCreate failed with 0x%x\n", status));
andy@11 698 status = NDIS_STATUS_FAILURE;
andy@11 699 goto err;
andy@11 700 }
andy@30 701
andy@11 702 /* get lower (Xen) interfaces */
andy@11 703
james@76 704 status = WdfFdoQueryForInterface(xi->wdf_device, &GUID_XEN_IFACE,
james@76 705 (PINTERFACE) &xi->XenInterface, sizeof(XEN_IFACE), 1, NULL);
andy@11 706 if(!NT_SUCCESS(status))
andy@11 707 {
james@76 708 KdPrint(("WdfFdoQueryForInterface failed with status 0x%08x\n", status));
andy@11 709 status = NDIS_STATUS_FAILURE;
andy@11 710 goto err;
andy@11 711 }
andy@11 712
james@76 713 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath),
james@77 714 "%s/backend", xi->pdoData->Path);
james@77 715 KdPrint(("About to read %s to get backend path\n", TmpPath));
andy@90 716 res = xi->XenInterface.XenBus_Read(xi->XenInterface.InterfaceHeader.Context,
james@77 717 XBT_NIL, TmpPath, &Value);
andy@90 718 if (res)
andy@11 719 {
james@77 720 KdPrint((__DRIVER_NAME " Failed to read backend path\n"));
andy@90 721 xi->XenInterface.FreeMem(res);
andy@21 722 status = NDIS_STATUS_FAILURE;
andy@21 723 goto err;
andy@21 724 }
james@77 725 RtlStringCbCopyA(xi->BackendPath, ARRAY_SIZE(xi->BackendPath), Value);
james@77 726 KdPrint((__DRIVER_NAME "backend path = %s\n", xi->BackendPath));
andy@21 727
james@85 728 KeInitializeEvent(&xi->backend_state_change_event, SynchronizationEvent, FALSE);
andy@21 729
james@77 730 /* Add watch on backend state */
james@77 731 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/state", xi->BackendPath);
james@77 732 xi->XenInterface.XenBus_AddWatch(xi->XenInterface.InterfaceHeader.Context,
andy@22 733 XBT_NIL, TmpPath, XenNet_BackEndStateHandler, xi);
andy@22 734
james@77 735 // wait here for signal that we are all set up
james@85 736 while (xi->backend_state != XenbusStateConnected)
james@85 737 KeWaitForSingleObject(&xi->backend_state_change_event, Executive, KernelMode, FALSE, NULL);
andy@22 738
james@77 739 /* get mac address */
james@77 740 RtlStringCbPrintfA(TmpPath, ARRAY_SIZE(TmpPath), "%s/mac", xi->BackendPath);
james@77 741 xi->XenInterface.XenBus_Read(xi->XenInterface.InterfaceHeader.Context,
james@77 742 XBT_NIL, TmpPath, &Value);
james@77 743 if (!Value)
james@77 744 {
james@77 745 KdPrint((__DRIVER_NAME " mac Read Failed\n"));
james@77 746 status = NDIS_STATUS_FAILURE;
james@77 747 goto err;
james@77 748 }
james@77 749 else
james@77 750 {
james@77 751 char *s, *e;
james@77 752 int i;
james@77 753 s = Value;
james@77 754 for (i = 0; i < ETH_ALEN; i++) {
james@77 755 xi->perm_mac_addr[i] = (UINT8)simple_strtoul(s, &e, 16);
james@77 756 if ((s == e) || (*e != ((i == ETH_ALEN-1) ? '\0' : ':'))) {
james@77 757 KdPrint((__DRIVER_NAME "Error parsing MAC address\n"));
james@77 758 xi->XenInterface.FreeMem(Value);
james@77 759 status = NDIS_STATUS_FAILURE;
james@77 760 goto err;
andy@22 761 }
james@77 762 s = e + 1;
andy@22 763 }
james@77 764 memcpy(xi->curr_mac_addr, xi->perm_mac_addr, ETH_ALEN);
james@77 765 xi->XenInterface.FreeMem(Value);
andy@11 766 }
andy@11 767
andy@11 768 return NDIS_STATUS_SUCCESS;
andy@11 769
andy@11 770 err:
andy@11 771 NdisFreeMemory(xi, 0, 0);
andy@11 772 return status;
andy@11 773 }
andy@11 774
james@78 775 // Q = Query Mandatory, S = Set Mandatory
andy@11 776 NDIS_OID supported_oids[] =
andy@11 777 {
andy@51 778 /* general OIDs */
james@78 779 OID_GEN_SUPPORTED_LIST, // Q
james@78 780 OID_GEN_HARDWARE_STATUS, // Q
james@78 781 OID_GEN_MEDIA_SUPPORTED, // Q
james@78 782 OID_GEN_MEDIA_IN_USE, // Q
james@78 783 OID_GEN_MAXIMUM_LOOKAHEAD, // Q
james@78 784 OID_GEN_MAXIMUM_FRAME_SIZE, // Q
james@78 785 OID_GEN_LINK_SPEED, // Q
james@78 786 OID_GEN_TRANSMIT_BUFFER_SPACE, // Q
james@78 787 OID_GEN_RECEIVE_BUFFER_SPACE, // Q
james@78 788 OID_GEN_TRANSMIT_BLOCK_SIZE, // Q
james@78 789 OID_GEN_RECEIVE_BLOCK_SIZE, // Q
james@78 790 OID_GEN_VENDOR_ID, // Q
james@78 791 OID_GEN_VENDOR_DESCRIPTION, // Q
james@78 792 OID_GEN_CURRENT_PACKET_FILTER, // QS
james@78 793 OID_GEN_CURRENT_LOOKAHEAD, // QS
james@78 794 OID_GEN_DRIVER_VERSION, // Q
james@78 795 OID_GEN_MAXIMUM_TOTAL_SIZE, // Q
james@78 796 OID_GEN_PROTOCOL_OPTIONS, // S
james@78 797 OID_GEN_MAC_OPTIONS, // Q
james@78 798 OID_GEN_MEDIA_CONNECT_STATUS, // Q
james@78 799 OID_GEN_MAXIMUM_SEND_PACKETS, // Q
andy@51 800 /* stats */
james@78 801 OID_GEN_XMIT_OK, // Q
james@78 802 OID_GEN_RCV_OK, // Q
james@78 803 OID_GEN_XMIT_ERROR, // Q
james@78 804 OID_GEN_RCV_ERROR, // Q
james@78 805 OID_GEN_RCV_NO_BUFFER, // Q
andy@51 806 /* media-specific OIDs */
andy@11 807 OID_802_3_PERMANENT_ADDRESS,
andy@11 808 OID_802_3_CURRENT_ADDRESS,
andy@11 809 OID_802_3_MULTICAST_LIST,
andy@11 810 OID_802_3_MAXIMUM_LIST_SIZE,
andy@11 811 };
andy@11 812
andy@11 813 NDIS_STATUS
andy@11 814 XenNet_QueryInformation(
andy@11 815 IN NDIS_HANDLE MiniportAdapterContext,
andy@11 816 IN NDIS_OID Oid,
andy@11 817 IN PVOID InformationBuffer,
andy@11 818 IN ULONG InformationBufferLength,
andy@11 819 OUT PULONG BytesWritten,
andy@11 820 OUT PULONG BytesNeeded)
andy@11 821 {
andy@11 822 struct xennet_info *xi = MiniportAdapterContext;
andy@11 823 UCHAR vendor_desc[] = XN_VENDOR_DESC;
andy@51 824 ULONG64 temp_data;
andy@11 825 PVOID data = &temp_data;
andy@51 826 UINT len = 4;
andy@11 827 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
andy@11 828
james@78 829 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 830
andy@11 831 switch(Oid)
andy@11 832 {
andy@11 833 case OID_GEN_SUPPORTED_LIST:
andy@11 834 data = supported_oids;
andy@11 835 len = sizeof(supported_oids);
andy@11 836 break;
andy@11 837 case OID_GEN_HARDWARE_STATUS:
andy@50 838 if (!xi->connected)
andy@50 839 temp_data = NdisHardwareStatusInitializing;
andy@50 840 else
andy@50 841 temp_data = NdisHardwareStatusReady;
andy@11 842 break;
andy@11 843 case OID_GEN_MEDIA_SUPPORTED:
andy@11 844 temp_data = NdisMedium802_3;
andy@11 845 break;
andy@11 846 case OID_GEN_MEDIA_IN_USE:
andy@11 847 temp_data = NdisMedium802_3;
andy@11 848 break;
andy@11 849 case OID_GEN_MAXIMUM_LOOKAHEAD:
andy@11 850 temp_data = XN_DATA_SIZE;
andy@11 851 break;
andy@11 852 case OID_GEN_MAXIMUM_FRAME_SIZE:
james@83 853 // According to the specs, OID_GEN_MAXIMUM_FRAME_SIZE does not include the header, so
james@83 854 // it is XN_DATA_SIZE not XN_MAX_PKT_SIZE
james@83 855 temp_data = XN_DATA_SIZE; // XN_MAX_PKT_SIZE;
andy@11 856 break;
andy@11 857 case OID_GEN_LINK_SPEED:
andy@11 858 temp_data = 10000000; /* 1Gb */
andy@11 859 break;
andy@11 860 case OID_GEN_TRANSMIT_BUFFER_SPACE:
andy@11 861 /* pkts times sizeof ring, maybe? */
andy@11 862 temp_data = XN_MAX_PKT_SIZE * NET_TX_RING_SIZE;
andy@11 863 break;
andy@11 864 case OID_GEN_RECEIVE_BUFFER_SPACE:
andy@11 865 /* pkts times sizeof ring, maybe? */
james@83 866 temp_data = XN_MAX_PKT_SIZE * NET_RX_RING_SIZE;
andy@11 867 break;
andy@11 868 case OID_GEN_TRANSMIT_BLOCK_SIZE:
andy@11 869 temp_data = XN_MAX_PKT_SIZE;
andy@11 870 break;
andy@11 871 case OID_GEN_RECEIVE_BLOCK_SIZE:
andy@11 872 temp_data = XN_MAX_PKT_SIZE;
andy@11 873 break;
andy@11 874 case OID_GEN_VENDOR_ID:
james@78 875 temp_data = 0xFFFFFF; // Not guaranteed to be XENSOURCE_MAC_HDR;
andy@11 876 break;
andy@11 877 case OID_GEN_VENDOR_DESCRIPTION:
andy@11 878 data = vendor_desc;
andy@11 879 len = sizeof(vendor_desc);
andy@11 880 break;
andy@11 881 case OID_GEN_CURRENT_PACKET_FILTER:
andy@11 882 temp_data = xi->packet_filter;
andy@11 883 break;
andy@11 884 case OID_GEN_CURRENT_LOOKAHEAD:
james@78 885 // TODO: we should store this...
andy@11 886 temp_data = XN_MAX_PKT_SIZE;
andy@11 887 break;
andy@11 888 case OID_GEN_DRIVER_VERSION:
andy@11 889 temp_data = (NDIS_MAJOR_VER << 8) | NDIS_MINOR_VER;
andy@11 890 len = 2;
andy@11 891 break;
andy@11 892 case OID_GEN_MAXIMUM_TOTAL_SIZE:
andy@11 893 temp_data = XN_MAX_PKT_SIZE;
andy@11 894 break;
andy@11 895 case OID_GEN_MAC_OPTIONS:
andy@11 896 temp_data = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
andy@11 897 NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
andy@11 898 NDIS_MAC_OPTION_NO_LOOPBACK;
andy@11 899 break;
andy@11 900 case OID_GEN_MEDIA_CONNECT_STATUS:
andy@23 901 if (xi->connected)
andy@23 902 temp_data = NdisMediaStateConnected;
andy@23 903 else
andy@23 904 temp_data = NdisMediaStateDisconnected;
andy@11 905 break;
andy@11 906 case OID_GEN_MAXIMUM_SEND_PACKETS:
andy@11 907 temp_data = XN_MAX_SEND_PKTS;
andy@11 908 break;
andy@51 909 case OID_GEN_XMIT_OK:
andy@51 910 temp_data = xi->stat_tx_ok;
andy@51 911 len = sizeof(ULONG64);
andy@51 912 break;
andy@51 913 case OID_GEN_RCV_OK:
andy@51 914 temp_data = xi->stat_rx_ok;
andy@51 915 len = sizeof(ULONG64);
andy@51 916 break;
andy@51 917 case OID_GEN_XMIT_ERROR:
andy@51 918 temp_data = xi->stat_tx_error;
andy@51 919 len = sizeof(ULONG64);
andy@51 920 break;
andy@51 921 case OID_GEN_RCV_ERROR:
andy@51 922 temp_data = xi->stat_rx_error;
andy@51 923 len = sizeof(ULONG64);
andy@51 924 break;
andy@51 925 case OID_GEN_RCV_NO_BUFFER:
andy@51 926 temp_data = xi->stat_rx_no_buffer;
andy@51 927 len = sizeof(ULONG64);
andy@51 928 break;
andy@11 929 case OID_802_3_PERMANENT_ADDRESS:
andy@11 930 data = xi->perm_mac_addr;
andy@11 931 len = ETH_ALEN;
andy@11 932 break;
andy@11 933 case OID_802_3_CURRENT_ADDRESS:
andy@11 934 data = xi->curr_mac_addr;
andy@11 935 len = ETH_ALEN;
andy@11 936 break;
andy@11 937 case OID_802_3_MULTICAST_LIST:
andy@11 938 data = NULL;
andy@11 939 len = 0;
andy@11 940 case OID_802_3_MAXIMUM_LIST_SIZE:
andy@11 941 temp_data = 0; /* no mcast support */
andy@11 942 break;
andy@11 943 default:
james@78 944 KdPrint(("Get Unknown OID 0x%x\n", Oid));
andy@11 945 status = NDIS_STATUS_NOT_SUPPORTED;
andy@11 946 }
andy@11 947
andy@11 948 if (!NT_SUCCESS(status))
andy@11 949 {
james@80 950 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " (returned error)\n"));
andy@11 951 return status;
andy@11 952 }
andy@11 953
andy@11 954 if (len > InformationBufferLength)
andy@11 955 {
andy@11 956 *BytesNeeded = len;
james@80 957 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " (returned error)\n"));
andy@11 958 return NDIS_STATUS_BUFFER_TOO_SHORT;
andy@11 959 }
andy@11 960
andy@11 961 *BytesWritten = len;
andy@11 962 if (len)
andy@11 963 {
andy@11 964 NdisMoveMemory(InformationBuffer, data, len);
andy@11 965 }
andy@11 966
andy@51 967 //KdPrint(("Got OID 0x%x\n", Oid));
james@78 968 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@11 969
andy@11 970 return status;
andy@11 971 }
andy@11 972
andy@11 973 NDIS_STATUS
andy@11 974 XenNet_SetInformation(
andy@11 975 IN NDIS_HANDLE MiniportAdapterContext,
andy@11 976 IN NDIS_OID Oid,
andy@11 977 IN PVOID InformationBuffer,
andy@11 978 IN ULONG InformationBufferLength,
andy@11 979 OUT PULONG BytesRead,
andy@11 980 OUT PULONG BytesNeeded
andy@11 981 )
andy@11 982 {
james@78 983 NTSTATUS status;
james@78 984 struct xennet_info *xi = MiniportAdapterContext;
james@78 985 PULONG64 data = InformationBuffer;
james@78 986
james@85 987 KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 988
andy@14 989 UNREFERENCED_PARAMETER(MiniportAdapterContext);
andy@14 990 UNREFERENCED_PARAMETER(InformationBufferLength);
andy@14 991 UNREFERENCED_PARAMETER(BytesRead);
andy@14 992 UNREFERENCED_PARAMETER(BytesNeeded);
andy@14 993
james@78 994 switch(Oid)
james@78 995 {
james@78 996 case OID_GEN_SUPPORTED_LIST:
james@78 997 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 998 KdPrint(("Unsupported set OID_GEN_SUPPORTED_LIST\n"));
james@78 999 break;
james@78 1000 case OID_GEN_HARDWARE_STATUS:
james@78 1001 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1002 KdPrint(("Unsupported set OID_GEN_HARDWARE_STATUS\n"));
james@78 1003 break;
james@78 1004 case OID_GEN_MEDIA_SUPPORTED:
james@78 1005 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1006 KdPrint(("Unsupported set OID_GEN_MEDIA_SUPPORTED\n"));
james@78 1007 break;
james@78 1008 case OID_GEN_MEDIA_IN_USE:
james@78 1009 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1010 KdPrint(("Unsupported set OID_GEN_MEDIA_IN_USE\n"));
james@78 1011 break;
james@78 1012 case OID_GEN_MAXIMUM_LOOKAHEAD:
james@78 1013 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1014 KdPrint(("Unsupported set OID_GEN_MAXIMUM_LOOKAHEAD\n"));
james@78 1015 break;
james@78 1016 case OID_GEN_MAXIMUM_FRAME_SIZE:
james@78 1017 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1018 KdPrint(("Unsupported set OID_GEN_MAXIMUM_FRAME_SIZE\n"));
james@78 1019 break;
james@78 1020 case OID_GEN_LINK_SPEED:
james@78 1021 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1022 KdPrint(("Unsupported set OID_GEN_LINK_SPEED\n"));
james@78 1023 break;
james@78 1024 case OID_GEN_TRANSMIT_BUFFER_SPACE:
james@78 1025 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1026 KdPrint(("Unsupported set OID_GEN_TRANSMIT_BUFFER_SPACE\n"));
james@78 1027 break;
james@78 1028 case OID_GEN_RECEIVE_BUFFER_SPACE:
james@78 1029 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1030 KdPrint(("Unsupported set OID_GEN_RECEIVE_BUFFER_SPACE\n"));
james@78 1031 break;
james@78 1032 case OID_GEN_TRANSMIT_BLOCK_SIZE:
james@78 1033 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1034 KdPrint(("Unsupported set OID_GEN_TRANSMIT_BLOCK_SIZE\n"));
james@78 1035 break;
james@78 1036 case OID_GEN_RECEIVE_BLOCK_SIZE:
james@78 1037 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1038 KdPrint(("Unsupported set OID_GEN_RECEIVE_BLOCK_SIZE\n"));
james@78 1039 break;
james@78 1040 case OID_GEN_VENDOR_ID:
james@78 1041 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1042 KdPrint(("Unsupported set OID_GEN_VENDOR_ID\n"));
james@78 1043 break;
james@78 1044 case OID_GEN_VENDOR_DESCRIPTION:
james@78 1045 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1046 KdPrint(("Unsupported set OID_GEN_VENDOR_DESCRIPTION\n"));
james@78 1047 break;
james@78 1048 case OID_GEN_CURRENT_PACKET_FILTER:
james@78 1049 KdPrint(("Set OID_GEN_CURRENT_PACKET_FILTER\n"));
james@78 1050 xi->packet_filter = *(ULONG *)data;
james@78 1051 status = NDIS_STATUS_SUCCESS;
james@78 1052 break;
james@78 1053 case OID_GEN_CURRENT_LOOKAHEAD:
james@78 1054 KdPrint(("Set OID_GEN_CURRENT_LOOKAHEAD %d\n", *(int *)data));
james@78 1055 // TODO: We should do this...
james@78 1056 status = NDIS_STATUS_SUCCESS;
james@78 1057 break;
james@78 1058 case OID_GEN_DRIVER_VERSION:
james@78 1059 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1060 KdPrint(("Unsupported set OID_GEN_DRIVER_VERSION\n"));
james@78 1061 break;
james@78 1062 case OID_GEN_MAXIMUM_TOTAL_SIZE:
james@78 1063 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1064 KdPrint(("Unsupported set OID_GEN_MAXIMUM_TOTAL_SIZE\n"));
james@78 1065 break;
james@78 1066 case OID_GEN_PROTOCOL_OPTIONS:
james@78 1067 KdPrint(("Unsupported set OID_GEN_PROTOCOL_OPTIONS\n"));
james@78 1068 // TODO - actually do this...
james@78 1069 status = NDIS_STATUS_SUCCESS;
james@78 1070 break;
james@78 1071 case OID_GEN_MAC_OPTIONS:
james@78 1072 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1073 KdPrint(("Unsupported set OID_GEN_MAC_OPTIONS\n"));
james@78 1074 break;
james@78 1075 case OID_GEN_MEDIA_CONNECT_STATUS:
james@78 1076 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1077 KdPrint(("Unsupported set OID_GEN_MEDIA_CONNECT_STATUS\n"));
james@78 1078 break;
james@78 1079 case OID_GEN_MAXIMUM_SEND_PACKETS:
james@78 1080 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1081 KdPrint(("Unsupported set OID_GEN_MAXIMUM_SEND_PACKETS\n"));
james@78 1082 break;
james@78 1083 case OID_GEN_XMIT_OK:
james@78 1084 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1085 KdPrint(("Unsupported set OID_GEN_XMIT_OK\n"));
james@78 1086 break;
james@78 1087 case OID_GEN_RCV_OK:
james@78 1088 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1089 KdPrint(("Unsupported set OID_GEN_RCV_OK\n"));
james@78 1090 break;
james@78 1091 case OID_GEN_XMIT_ERROR:
james@78 1092 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1093 KdPrint(("Unsupported set OID_GEN_XMIT_ERROR\n"));
james@78 1094 break;
james@78 1095 case OID_GEN_RCV_ERROR:
james@78 1096 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1097 KdPrint(("Unsupported set OID_GEN_RCV_ERROR\n"));
james@78 1098 break;
james@78 1099 case OID_GEN_RCV_NO_BUFFER:
james@78 1100 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1101 KdPrint(("Unsupported set OID_GEN_RCV_NO_BUFFER\n"));
james@78 1102 break;
james@78 1103 case OID_802_3_PERMANENT_ADDRESS:
james@78 1104 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1105 KdPrint(("Unsupported set OID_802_3_PERMANENT_ADDRESS\n"));
james@78 1106 break;
james@78 1107 case OID_802_3_CURRENT_ADDRESS:
james@78 1108 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1109 KdPrint(("Unsupported set OID_802_3_CURRENT_ADDRESS\n"));
james@78 1110 break;
james@78 1111 case OID_802_3_MULTICAST_LIST:
james@78 1112 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1113 KdPrint(("Unsupported set OID_802_3_MULTICAST_LIST\n"));
james@78 1114 break;
james@78 1115 case OID_802_3_MAXIMUM_LIST_SIZE:
james@78 1116 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1117 KdPrint(("Unsupported set OID_802_3_MAXIMUM_LIST_SIZE\n"));
james@78 1118 break;
james@78 1119 default:
james@78 1120 KdPrint(("Set Unknown OID 0x%x\n", Oid));
james@78 1121 status = NDIS_STATUS_NOT_SUPPORTED;
james@78 1122 break;
james@78 1123 }
james@85 1124 KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
james@78 1125 return status;
andy@11 1126 }
andy@11 1127
andy@11 1128 VOID
andy@11 1129 XenNet_ReturnPacket(
andy@11 1130 IN NDIS_HANDLE MiniportAdapterContext,
andy@11 1131 IN PNDIS_PACKET Packet
andy@11 1132 )
andy@11 1133 {
andy@44 1134 PNDIS_BUFFER buffer;
andy@44 1135 PVOID buff_va;
andy@44 1136 UINT buff_len;
andy@44 1137 UINT tot_buff_len;
andy@43 1138
james@79 1139 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 1140
andy@44 1141 UNREFERENCED_PARAMETER(MiniportAdapterContext);
andy@44 1142
andy@44 1143 NdisGetFirstBufferFromPacketSafe(Packet, &buffer, &buff_va, &buff_len,
andy@44 1144 &tot_buff_len, NormalPagePriority);
andy@44 1145 ASSERT(buff_len == tot_buff_len);
andy@44 1146
andy@44 1147 NdisFreeMemory(buff_va, 0, 0);
andy@44 1148 NdisFreeBuffer(buffer);
andy@44 1149 NdisFreePacket(Packet);
andy@14 1150
andy@65 1151 //KdPrint((__FUNCTION__ " called\n"));
james@79 1152 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@11 1153 }
andy@11 1154
andy@32 1155 PMDL
andy@32 1156 XenNet_Linearize(PNDIS_PACKET Packet)
andy@32 1157 {
andy@69 1158 NDIS_STATUS status;
andy@32 1159 PMDL pmdl;
andy@32 1160 char *start;
andy@32 1161 PNDIS_BUFFER buffer;
andy@32 1162 PVOID buff_va;
andy@32 1163 UINT buff_len;
andy@32 1164 UINT tot_buff_len;
andy@32 1165
james@79 1166 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 1167
andy@69 1168 NdisGetFirstBufferFromPacketSafe(Packet, &buffer, &buff_va, &buff_len,
andy@69 1169 &tot_buff_len, NormalPagePriority);
andy@69 1170 ASSERT(tot_buff_len <= XN_MAX_PKT_SIZE);
andy@32 1171
andy@69 1172 status = NdisAllocateMemoryWithTag(&start, tot_buff_len, XENNET_POOL_TAG);
andy@69 1173 if (!NT_SUCCESS(status))
andy@32 1174 {
andy@69 1175 KdPrint(("Could not allocate memory for linearization\n"));
andy@32 1176 return NULL;
andy@32 1177 }
andy@69 1178 pmdl = IoAllocateMdl(start, tot_buff_len, FALSE, FALSE, FALSE);
andy@69 1179 if (!pmdl)
andy@69 1180 {
andy@69 1181 KdPrint(("Could not allocate MDL for linearization\n"));
andy@69 1182 NdisFreeMemory(start, 0, 0);
andy@69 1183 return NULL;
andy@69 1184 }
andy@69 1185 MmBuildMdlForNonPagedPool(pmdl);
andy@32 1186
andy@32 1187 while (buffer)
andy@32 1188 {
andy@32 1189 NdisQueryBufferSafe(buffer, &buff_va, &buff_len, NormalPagePriority);
andy@32 1190 RtlCopyMemory(start, buff_va, buff_len);
andy@32 1191 start += buff_len;
andy@32 1192 NdisGetNextBuffer(buffer, &buffer);
andy@32 1193 }
andy@32 1194
james@79 1195 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@32 1196 return pmdl;
andy@32 1197 }
andy@32 1198
andy@11 1199 VOID
andy@11 1200 XenNet_SendPackets(
andy@11 1201 IN NDIS_HANDLE MiniportAdapterContext,
andy@11 1202 IN PPNDIS_PACKET PacketArray,
andy@11 1203 IN UINT NumberOfPackets
andy@11 1204 )
andy@11 1205 {
andy@11 1206 struct xennet_info *xi = MiniportAdapterContext;
andy@11 1207 PNDIS_PACKET curr_packet;
andy@11 1208 UINT i;
andy@30 1209 struct netif_tx_request *tx;
andy@30 1210 unsigned short id;
andy@32 1211 int notify;
andy@32 1212 PMDL pmdl;
andy@32 1213 UINT pkt_size;
andy@87 1214 KIRQL OldIrql;
andy@11 1215
james@79 1216 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
james@78 1217
andy@91 1218 KeAcquireSpinLock(&xi->TxLock, &OldIrql);
james@80 1219
andy@11 1220 for (i = 0; i < NumberOfPackets; i++)
andy@11 1221 {
andy@11 1222 curr_packet = PacketArray[i];
andy@11 1223 ASSERT(curr_packet);
andy@11 1224
andy@32 1225 NdisQueryPacket(curr_packet, NULL, NULL, NULL, &pkt_size);
andy@30 1226
andy@65 1227 //KdPrint(("sending pkt, len %d\n", pkt_size));
andy@51 1228
andy@32 1229 pmdl = XenNet_Linearize(curr_packet);
andy@32 1230 if (!pmdl)
andy@32 1231 {
andy@32 1232 KdPrint((__DRIVER_NAME "Couldn't linearize packet!\n"));
andy@32 1233 NdisMSendComplete(xi, curr_packet, NDIS_STATUS_FAILURE);
andy@32 1234 break;
andy@32 1235 }
andy@44 1236 *((PMDL *)curr_packet->MiniportReservedEx) = pmdl;
andy@30 1237
andy@32 1238 id = get_id_from_freelist(xi->tx_pkts);
andy@32 1239 xi->tx_pkts[id] = curr_packet;
andy@30 1240
andy@32 1241 tx = RING_GET_REQUEST(&xi->tx, xi->tx.req_prod_pvt);
andy@32 1242 tx->id = id;
james@76 1243 tx->gref = xi->XenInterface.GntTbl_GrantAccess(
james@76 1244 xi->XenInterface.InterfaceHeader.Context,
andy@32 1245 0,
andy@44 1246 *MmGetMdlPfnArray(pmdl),
andy@32 1247 TRUE);
andy@42 1248 xi->grant_tx_ref[id] = tx->gref;
andy@69 1249 tx->offset = (uint16_t)MmGetMdlByteOffset(pmdl);
andy@32 1250 tx->size = (UINT16)pkt_size;
james@78 1251 // NETTXF_csum_blank should only be used for tcp and udp packets...
james@78 1252 tx->flags = 0; //NETTXF_csum_blank;
andy@30 1253
andy@32 1254 xi->tx.req_prod_pvt++;
andy@32 1255
andy@32 1256 // NDIS_SET_PACKET_STATUS(curr_packet, NDIS_STATUS_SUCCESS);
andy@32 1257 // NdisMSendComplete(xi, curr_packet, NDIS_STATUS_SUCCESS);
andy@11 1258 }
andy@11 1259
andy@32 1260 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xi->tx, notify);
andy@32 1261 if (notify)
andy@32 1262 {
james@76 1263 xi->XenInterface.EvtChn_Notify(xi->XenInterface.InterfaceHeader.Context,
andy@32 1264 xi->event_channel);
andy@32 1265 }
james@78 1266
andy@91 1267 KeReleaseSpinLock(&xi->TxLock, OldIrql);
james@80 1268
james@79 1269 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
andy@11 1270 }
andy@11 1271
andy@11 1272 VOID
andy@11 1273 XenNet_PnPEventNotify(
andy@11 1274 IN NDIS_HANDLE MiniportAdapterContext,
andy@11 1275 IN NDIS_DEVICE_PNP_EVENT PnPEvent,
andy@11 1276 IN PVOID InformationBuffer,
andy@11 1277 IN ULONG InformationBufferLength
andy@11 1278 )
andy@11 1279 {
andy@14 1280 UNREFERENCED_PARAMETER(MiniportAdapterContext);
andy@14 1281 UNREFERENCED_PARAMETER(PnPEvent);
andy@14 1282 UNREFERENCED_PARAMETER(InformationBuffer);
andy@14 1283 UNREFERENCED_PARAMETER(InformationBufferLength);
andy@14 1284
andy@11 1285 KdPrint((__FUNCTION__ " called\n"));
andy@11 1286 }
andy@11 1287
andy@11 1288 VOID
andy@11 1289 XenNet_Shutdown(
andy@11 1290 IN NDIS_HANDLE MiniportAdapterContext
andy@11 1291 )
andy@11 1292 {
andy@87 1293 //struct xennet_info *xi = MiniportAdapterContext;
andy@87 1294 UNREFERENCED_PARAMETER(MiniportAdapterContext);
james@85 1295
james@85 1296 // I think all we are supposed to do here is reset the adapter, which for us might be nothing...
andy@14 1297
andy@11 1298 KdPrint((__FUNCTION__ " called\n"));
andy@11 1299 }
andy@11 1300
andy@11 1301 NTSTATUS
andy@11 1302 DriverEntry(
andy@11 1303 PDRIVER_OBJECT DriverObject,
andy@11 1304 PUNICODE_STRING RegistryPath
andy@11 1305 )
andy@11 1306 {
andy@11 1307 NTSTATUS status;
andy@11 1308 WDF_DRIVER_CONFIG config;
andy@11 1309 NDIS_HANDLE ndis_wrapper_handle;
andy@11 1310 NDIS_MINIPORT_CHARACTERISTICS mini_chars;
andy@11 1311
andy@11 1312 RtlZeroMemory(&mini_chars, sizeof(mini_chars));
andy@11 1313
andy@11 1314 WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
andy@11 1315 config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;
andy@11 1316
andy@11 1317 status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES,
andy@11 1318 &config, WDF_NO_HANDLE);
andy@11 1319 if (!NT_SUCCESS(status))
andy@11 1320 {
andy@11 1321 KdPrint(("WdfDriverCreate failed err = 0x%x\n", status));
andy@11 1322 return status;
andy@11 1323 }
andy@11 1324
andy@11 1325 NdisMInitializeWrapper(&ndis_wrapper_handle, DriverObject, RegistryPath, NULL);
andy@11 1326 if (!ndis_wrapper_handle)
andy@11 1327 {
andy@11 1328 KdPrint(("NdisMInitializeWrapper failed\n"));
andy@11 1329 return NDIS_STATUS_FAILURE;
andy@11 1330 }
andy@11 1331
andy@11 1332 /* NDIS 5.1 driver */
andy@50 1333 mini_chars.MajorNdisVersion = NDIS_MAJOR_VER;
andy@50 1334 mini_chars.MinorNdisVersion = NDIS_MINOR_VER;
andy@11 1335
andy@11 1336 mini_chars.HaltHandler = XenNet_Halt;
andy@11 1337 mini_chars.InitializeHandler = XenNet_Init;
andy@11 1338 mini_chars.ISRHandler = NULL; // needed if we register interrupt?
andy@11 1339 mini_chars.QueryInformationHandler = XenNet_QueryInformation;
andy@11 1340 mini_chars.ResetHandler = NULL; //TODO: fill in
andy@11 1341 mini_chars.SetInformationHandler = XenNet_SetInformation;
andy@11 1342 /* added in v.4 -- use multiple pkts interface */
andy@11 1343 mini_chars.ReturnPacketHandler = XenNet_ReturnPacket;
andy@11 1344 mini_chars.SendPacketsHandler = XenNet_SendPackets;
andy@11 1345 /* added in v.5.1 */
andy@11 1346 mini_chars.PnPEventNotifyHandler = XenNet_PnPEventNotify;
andy@11 1347 mini_chars.AdapterShutdownHandler = XenNet_Shutdown;
andy@11 1348
andy@11 1349 /* TODO: we don't have hardware, but we have "resources", so do we need to implement fns to handle this? */
andy@11 1350
andy@11 1351 /* set up upper-edge interface */
andy@11 1352 status = NdisMRegisterMiniport(ndis_wrapper_handle, &mini_chars, sizeof(mini_chars));
andy@11 1353 if (!NT_SUCCESS(status))
andy@11 1354 {
andy@11 1355 KdPrint(("NdisMRegisterMiniport failed, status = 0x%x\n", status));
andy@11 1356 NdisTerminateWrapper(ndis_wrapper_handle, NULL);
andy@11 1357 return status;
andy@11 1358 }
andy@11 1359
andy@11 1360 return status;
andy@11 1361 }