#define WV_AF_INET 2\r
#define WV_AF_INET6 23\r
\r
+static void WvEpWorkHandler(WORK_ENTRY *pWork);\r
+\r
static void WvEpGet(WV_ENDPOINT *pEndpoint)\r
{\r
InterlockedIncrement(&pEndpoint->Ref);\r
}\r
\r
RtlZeroMemory(ep, sizeof(WV_ENDPOINT));\r
+ ep->pWork = ExAllocatePoolWithTag(NonPagedPool, sizeof(WV_WORK_ENTRY), 'wevw');\r
+ if (ep->pWork == NULL) {\r
+ status = STATUS_NO_MEMORY;\r
+ goto err1;\r
+ }\r
+\r
ep->Ref = 1;\r
ep->pProvider = pProvider;\r
ep->EpType = EpType;\r
status = WdfIoQueueCreate(ControlDevice, &config,\r
WDF_NO_OBJECT_ATTRIBUTES, &ep->Queue);\r
if (!NT_SUCCESS(status)) {\r
- goto err;\r
+ goto err2;\r
}\r
\r
*ppEndpoint = ep;\r
return STATUS_SUCCESS;\r
\r
-err:\r
+err2:\r
+ ExFreePoolWithTag(ep->pWork, 'wevw');\r
+err1:\r
ExFreePoolWithTag(ep, 'pevw');\r
return status;\r
}\r
}\r
KeReleaseGuardedMutex(&pProvider->Lock);\r
\r
+ WvWorkEntryInit(ep->pWork, *pId, WvEpWorkHandler, pProvider);\r
WdfRequestCompleteWithInformation(Request, status, sizeof(UINT64));\r
return;\r
\r
\r
WdfIoQueuePurgeSynchronously(pEndpoint->Queue);\r
WdfObjectDelete(pEndpoint->Queue);\r
+ if (pEndpoint->pWork != NULL) {\r
+ ExFreePoolWithTag(pEndpoint->pWork, 'wevw');\r
+ }\r
ExFreePoolWithTag(pEndpoint, 'pevw');\r
}\r
\r
return status;\r
}\r
\r
-static void WvEpDisconnectHandler(WORK_ENTRY *pWork)\r
+static NTSTATUS WvEpAsyncDisconnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
{\r
- WV_PROVIDER *prov;\r
- WDFREQUEST request;\r
WV_IO_EP_DISCONNECT *pattr;\r
UINT8 *out;\r
size_t outlen = 0;\r
NTSTATUS status;\r
\r
- request = (WDFREQUEST) pWork->Context;\r
- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
-\r
- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_DISCONNECT),\r
+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_DISCONNECT),\r
&pattr, NULL);\r
if (!NT_SUCCESS(status)) {\r
- goto complete;\r
+ return status;\r
}\r
\r
- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
+ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
- goto complete;\r
+ return status;\r
}\r
\r
- status = (NTSTATUS) WdfRequestGetInformation(request);\r
+ status = (NTSTATUS) WdfRequestGetInformation(Request);\r
if (NT_SUCCESS(status)) {\r
- status = WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
+ status = WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
} else {\r
- WvEpDisconnectQp(prov, pattr->QpId, out, outlen);\r
+ WvEpDisconnectQp(pEndpoint->pProvider, pattr->QpId, out, outlen);\r
}\r
\r
-complete:\r
- WdfRequestCompleteWithInformation(request, status, outlen);\r
- WvProviderPut(prov);\r
+ WdfRequestCompleteWithInformation(Request, status, outlen);\r
+ return STATUS_SUCCESS;\r
}\r
\r
-// We use IRP DriverContext to queue the request for further processing,\r
-// but the request/IRP are no longer owned by the framework.\r
static void WvEpCompleteDisconnect(WV_ENDPOINT *pEndpoint, NTSTATUS DiscStatus)\r
{\r
WDFREQUEST request;\r
+ WDFREQUEST disc_req = NULL;\r
WDF_REQUEST_PARAMETERS param;\r
- WORK_ENTRY *work;\r
NTSTATUS status;\r
\r
WdfObjectAcquireLock(pEndpoint->Queue);\r
- if (pEndpoint->State == WvEpDestroying) {\r
+ if (pEndpoint->State == WvEpDestroying || !pEndpoint->pWork) {\r
goto release;\r
}\r
pEndpoint->State = WvEpDisconnected;\r
\r
status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
while (NT_SUCCESS(status)) {\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
\r
WDF_REQUEST_PARAMETERS_INIT(¶m);\r
WdfRequestGetParameters(request, ¶m);\r
if (param.Parameters.DeviceIoControl.IoControlCode == WV_IOCTL_EP_DISCONNECT) {\r
- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(request));\r
WdfRequestSetInformation(request, DiscStatus);\r
- WorkEntryInit(work, WvEpDisconnectHandler, request);\r
WvProviderGet(pEndpoint->pProvider);\r
- WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, work);\r
+ WorkQueueInsert(&pEndpoint->pProvider->WorkQueue, &pEndpoint->pWork->Work);\r
+ pEndpoint->pWork = NULL;\r
+ disc_req = request;\r
} else {\r
WdfRequestComplete(request, DiscStatus);\r
}\r
\r
- WdfObjectAcquireLock(pEndpoint->Queue);\r
status = WdfIoQueueRetrieveNextRequest(pEndpoint->Queue, &request);\r
}\r
+\r
+ if (disc_req != NULL) {\r
+ WdfRequestRequeue(disc_req);\r
+ }\r
release:\r
WdfObjectReleaseLock(pEndpoint->Queue);\r
+\r
}\r
\r
static NTSTATUS WvEpIbCmHandler(iba_cm_id *pId, iba_cm_event *pEvent)\r
return STATUS_SUCCESS;\r
}\r
\r
-void WvEpConnectHandler(WORK_ENTRY *pWork)\r
+static NTSTATUS WvEpAsyncConnect(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
{\r
- WV_PROVIDER *prov;\r
- WDFREQUEST request;\r
WV_IO_EP_CONNECT *pattr;\r
- WV_ENDPOINT *ep;\r
WV_QUEUE_PAIR *qp;\r
iba_cm_req req;\r
NTSTATUS status;\r
UINT8 data[IB_REQ_PDATA_SIZE];\r
\r
- request = (WDFREQUEST) pWork->Context;\r
- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
-\r
- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_CONNECT),\r
+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
&pattr, NULL);\r
if (!NT_SUCCESS(status)) {\r
- goto complete;\r
- }\r
-\r
- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
- status = STATUS_INVALID_BUFFER_SIZE;\r
- goto complete;\r
- }\r
-\r
- ep = WvEpAcquire(prov, pattr->Id);\r
- if (ep == NULL) {\r
- status = STATUS_NOT_FOUND;\r
- goto complete;\r
+ return status;\r
}\r
\r
- qp = WvQpAcquire(prov, pattr->QpId);\r
+ qp = WvQpAcquire(pEndpoint->pProvider, pattr->QpId);\r
if (qp == NULL) {\r
- status = STATUS_NOT_FOUND;\r
- goto release;\r
+ return STATUS_NOT_FOUND;\r
}\r
\r
- ep->Attributes.PeerAddress = pattr->PeerAddress;\r
- WvFormatCmaHeader((IB_CMA_HEADER *) data, &ep->Attributes.LocalAddress,\r
- &ep->Attributes.PeerAddress);\r
+ pEndpoint->Attributes.PeerAddress = pattr->PeerAddress;\r
+ WvFormatCmaHeader((IB_CMA_HEADER *) data, &pEndpoint->Attributes.LocalAddress,\r
+ &pEndpoint->Attributes.PeerAddress);\r
\r
- req.service_id = WvGetServiceId(ep->EpType, &ep->Attributes.PeerAddress);\r
- req.p_primary_path = &ep->Route;\r
+ req.service_id = WvGetServiceId(pEndpoint->EpType, &pEndpoint->Attributes.PeerAddress);\r
+ req.p_primary_path = &pEndpoint->Route;\r
req.p_alt_path = NULL;\r
req.qpn = qp->Qpn;\r
req.qp_type = IB_QPT_RELIABLE_CONN;\r
req.srq = (qp->pSrq != NULL);\r
\r
WvQpRelease(qp);\r
- RtlCopyMemory(&ep->Attributes.Param.Connect, &pattr->Param,\r
+ RtlCopyMemory(&pEndpoint->Attributes.Param.Connect, &pattr->Param,\r
sizeof(pattr->Param));\r
\r
- WdfObjectAcquireLock(ep->Queue);\r
- if (ep->State != WvEpRouteResolved) {\r
+ WdfObjectAcquireLock(pEndpoint->Queue);\r
+ if (pEndpoint->State != WvEpRouteResolved) {\r
status = STATUS_NOT_SUPPORTED;\r
- goto unlock;\r
+ goto out;\r
}\r
\r
- status = IbCmInterface.CM.create_id(WvEpIbCmHandler, ep, &ep->pIbCmId);\r
+ status = IbCmInterface.CM.create_id(WvEpIbCmHandler, pEndpoint, &pEndpoint->pIbCmId);\r
if (!NT_SUCCESS(status)) {\r
- goto unlock;\r
+ goto out;\r
}\r
\r
- ep->State = WvEpActiveConnect;\r
- status = IbCmInterface.CM.send_req(ep->pIbCmId, &req);\r
+ pEndpoint->State = WvEpActiveConnect;\r
+ status = IbCmInterface.CM.send_req(pEndpoint->pIbCmId, &req);\r
if (NT_SUCCESS(status)) {\r
- status = WdfRequestForwardToIoQueue(request, ep->Queue);\r
+ status = WdfRequestRequeue(Request);\r
}\r
\r
if (!NT_SUCCESS(status)) {\r
- ep->State = WvEpDisconnected;\r
- }\r
-unlock:\r
- WdfObjectReleaseLock(ep->Queue);\r
-release:\r
- WvEpRelease(ep);\r
-complete:\r
- if (!NT_SUCCESS(status)) {\r
- WdfRequestComplete(request, status);\r
+ pEndpoint->State = WvEpDisconnected;\r
}\r
- WvProviderPut(prov);\r
+\r
+out:\r
+ WdfObjectReleaseLock(pEndpoint->Queue);\r
+ return status;\r
}\r
\r
-static void WvEpProcessAsync(WV_PROVIDER *pProvider, WDFREQUEST Request,\r
- void (*AsyncHandler)(struct _WORK_ENTRY *Work))\r
+static NTSTATUS WvEpProcessAsync(WV_PROVIDER *pProvider, UINT64 Id, WDFREQUEST Request)\r
{\r
- WORK_ENTRY *work;\r
+ WV_ENDPOINT *ep;\r
+ NTSTATUS status;\r
+\r
+ ep = WvEpAcquire(pProvider, Id);\r
+ if (ep == NULL) {\r
+ return STATUS_NOT_FOUND;\r
+ }\r
+\r
+ WdfObjectAcquireLock(ep->Queue);\r
+ if (!ep->pWork) {\r
+ status = STATUS_TOO_MANY_COMMANDS;\r
+ goto out;\r
+ }\r
\r
- work = WorkEntryFromIrp(WdfRequestWdmGetIrp(Request));\r
- WorkEntryInit(work, AsyncHandler, Request);\r
- WvProviderGet(pProvider);\r
- WorkQueueInsert(&pProvider->WorkQueue, work);\r
+ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
+ if (NT_SUCCESS(status)) {\r
+ WvProviderGet(pProvider);\r
+ WorkQueueInsert(&pProvider->WorkQueue, &ep->pWork->Work);\r
+ ep->pWork = NULL;\r
+ }\r
+\r
+out:\r
+ WdfObjectReleaseLock(ep->Queue);\r
+ WvEpRelease(ep);\r
+ return status;\r
}\r
\r
void WvEpConnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
{\r
- WvEpProcessAsync(pProvider, Request, WvEpConnectHandler);\r
+ WV_IO_EP_CONNECT *pattr;\r
+ NTSTATUS status;\r
+\r
+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_CONNECT),\r
+ &pattr, NULL);\r
+ if (!NT_SUCCESS(status)) {\r
+ goto out;\r
+ }\r
+\r
+ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
+ status = STATUS_INVALID_BUFFER_SIZE;\r
+ goto out;\r
+ }\r
+\r
+ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
+\r
+out:\r
+ if (!NT_SUCCESS(status)) {\r
+ WdfRequestComplete(Request, status);\r
+ }\r
}\r
\r
static NTSTATUS WvEpModifyQpRtr(WV_ENDPOINT *pEndpoint, WV_QUEUE_PAIR *pQp,\r
\r
status = IbCmInterface.CM.send_rep(pEndpoint->pIbCmId, &rep);\r
if (NT_SUCCESS(status)) {\r
- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
+ status = WdfRequestRequeue(Request);\r
}\r
\r
if (!NT_SUCCESS(status)) {\r
return status;\r
}\r
\r
-void WvEpAcceptHandler(WORK_ENTRY *pWork)\r
+static NTSTATUS WvEpAsyncAccept(WV_ENDPOINT *pEndpoint, WDFREQUEST Request)\r
{\r
- WV_PROVIDER *prov;\r
- WDFREQUEST request;\r
WV_IO_EP_ACCEPT *pattr;\r
- WV_ENDPOINT *ep;\r
NTSTATUS status;\r
UINT8 *out;\r
size_t outlen;\r
\r
- request = (WDFREQUEST) pWork->Context;\r
- prov = WvProviderGetContext(WdfRequestGetFileObject(request));\r
-\r
- status = WdfRequestRetrieveInputBuffer(request, sizeof(WV_IO_EP_ACCEPT),\r
+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
&pattr, NULL);\r
if (!NT_SUCCESS(status)) {\r
- goto complete;\r
+ return status;\r
}\r
\r
- status = WdfRequestRetrieveOutputBuffer(request, 0, &out, &outlen);\r
+ status = WdfRequestRetrieveOutputBuffer(Request, 0, &out, &outlen);\r
if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) {\r
- goto complete;\r
- }\r
-\r
- if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
- status = STATUS_INVALID_BUFFER_SIZE;\r
- goto complete;\r
- }\r
-\r
- ep = WvEpAcquire(prov, pattr->Id);\r
- if (ep == NULL) {\r
- status = STATUS_NOT_FOUND;\r
- goto complete;\r
+ return status;\r
}\r
\r
/* EP state is re-checked under lock in WvEpAccept* calls */\r
- switch (ep->State) {\r
+ switch (pEndpoint->State) {\r
case WvEpActiveConnect:\r
- status = WvEpAcceptActive(request, out, outlen, ep, pattr);\r
+ status = WvEpAcceptActive(Request, out, outlen, pEndpoint, pattr);\r
break;\r
case WvEpPassiveConnect:\r
- status = WvEpAcceptPassive(request, out, outlen, ep, pattr);\r
+ status = WvEpAcceptPassive(Request, out, outlen, pEndpoint, pattr);\r
break;\r
default:\r
status = STATUS_NOT_SUPPORTED;\r
break;\r
}\r
\r
- WvEpRelease(ep);\r
-complete:\r
+ return status;\r
+}\r
+\r
+static void WvEpWorkHandler(WORK_ENTRY *pWork)\r
+{\r
+ WV_PROVIDER *prov;\r
+ WV_ENDPOINT *ep;\r
+ WV_WORK_ENTRY *work;\r
+ WDFREQUEST request;\r
+ WDF_REQUEST_PARAMETERS param;\r
+ NTSTATUS status;\r
+\r
+ work = CONTAINING_RECORD(pWork, WV_WORK_ENTRY, Work);\r
+ prov = (WV_PROVIDER *) pWork->Context;\r
+\r
+ ep = WvEpAcquire(prov, work->Id);\r
+ if (ep == NULL) {\r
+ ExFreePoolWithTag(work, 'wevw');\r
+ goto out;\r
+ }\r
+\r
+ WdfObjectAcquireLock(ep->Queue);\r
+ ep->pWork = work;\r
+ status = WdfIoQueueRetrieveNextRequest(ep->Queue, &request);\r
+ WdfObjectReleaseLock(ep->Queue);\r
+\r
+ if (!NT_SUCCESS(status)) {\r
+ goto put;\r
+ }\r
+\r
+ WDF_REQUEST_PARAMETERS_INIT(¶m);\r
+ WdfRequestGetParameters(request, ¶m);\r
+ switch (param.Parameters.DeviceIoControl.IoControlCode) {\r
+ case WV_IOCTL_EP_CONNECT:\r
+ status = WvEpAsyncConnect(ep, request);\r
+ break;\r
+ case WV_IOCTL_EP_ACCEPT:\r
+ status = WvEpAsyncAccept(ep, request);\r
+ break;\r
+ case WV_IOCTL_EP_DISCONNECT:\r
+ status = WvEpAsyncDisconnect(ep, request);\r
+ break;\r
+ default:\r
+ status = STATUS_NOT_IMPLEMENTED;\r
+ }\r
+\r
if (!NT_SUCCESS(status)) {\r
WdfRequestComplete(request, status);\r
}\r
+put:\r
+ WvEpRelease(ep);\r
+out:\r
WvProviderPut(prov);\r
}\r
\r
void WvEpAccept(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
{\r
- WvEpProcessAsync(pProvider, Request, WvEpAcceptHandler);\r
+ WV_IO_EP_ACCEPT *pattr;\r
+ NTSTATUS status;\r
+\r
+ status = WdfRequestRetrieveInputBuffer(Request, sizeof(WV_IO_EP_ACCEPT),\r
+ &pattr, NULL);\r
+ if (!NT_SUCCESS(status)) {\r
+ goto out;\r
+ }\r
+\r
+ if (pattr->Param.DataLength > sizeof(pattr->Param.Data)) {\r
+ status = STATUS_INVALID_BUFFER_SIZE;\r
+ goto out;\r
+ }\r
+\r
+ status = WvEpProcessAsync(pProvider, pattr->Id, Request);\r
+\r
+out:\r
+ if (!NT_SUCCESS(status)) {\r
+ WdfRequestComplete(Request, status);\r
+ }\r
}\r
\r
void WvEpReject(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
WdfRequestComplete(Request, status);\r
}\r
\r
-static NTSTATUS WvEpDisconnectActive(WDFREQUEST Request,\r
- UINT8 *pVerbsData, size_t VerbsSize,\r
- WV_ENDPOINT *pEndpoint,\r
- WV_IO_EP_DISCONNECT *pAttr)\r
-{\r
- NTSTATUS status, failure;\r
-\r
- WdfObjectAcquireLock(pEndpoint->Queue);\r
- if (pEndpoint->State != WvEpConnected) {\r
- status = STATUS_NOT_SUPPORTED;\r
- goto release;\r
- }\r
-\r
- pEndpoint->State = WvEpActiveDisconnect;\r
- IbCmInterface.CM.send_dreq(pEndpoint->pIbCmId, NULL, 0);\r
-\r
- status = WdfRequestForwardToIoQueue(Request, pEndpoint->Queue);\r
- if (!NT_SUCCESS(status)) {\r
- pEndpoint->State = WvEpDisconnected;\r
- WvCompleteRequests(pEndpoint->Queue, STATUS_UNSUCCESSFUL);\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
-\r
- failure = status;\r
- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
- pVerbsData, VerbsSize);\r
- if (NT_SUCCESS(status)) {\r
- WdfRequestCompleteWithInformation(Request, failure, VerbsSize);\r
- }\r
- return status;\r
- }\r
-\r
-release:\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
- return status;\r
-}\r
-\r
-static NTSTATUS WvEpDisconnectPassive(WDFREQUEST Request,\r
- UINT8 *pVerbsData, size_t VerbsSize,\r
- WV_ENDPOINT *pEndpoint,\r
- WV_IO_EP_DISCONNECT *pAttr)\r
-{\r
- NTSTATUS status;\r
-\r
- WdfObjectAcquireLock(pEndpoint->Queue);\r
- if (pEndpoint->State != WvEpPassiveDisconnect) {\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
- return STATUS_NOT_SUPPORTED;\r
- }\r
-\r
- pEndpoint->State = WvEpDisconnected;\r
- WdfObjectReleaseLock(pEndpoint->Queue);\r
-\r
- IbCmInterface.CM.send_drep(pEndpoint->pIbCmId, NULL, 0);\r
-\r
- status = WvEpDisconnectQp(pEndpoint->pProvider, pAttr->QpId,\r
- pVerbsData, VerbsSize);\r
- if (NT_SUCCESS(status)) {\r
- WdfRequestCompleteWithInformation(Request, status, VerbsSize);\r
- }\r
-\r
- return status;\r
-}\r
-\r
+// The IB CM could have received and processed a DREQ that we haven't seen yet.\r
void WvEpDisconnect(WV_PROVIDER *pProvider, WDFREQUEST Request)\r
{\r
WV_IO_EP_DISCONNECT *pattr;\r
goto complete;\r
}\r
\r
- /* EP state is re-checked under lock in WvEpDisconnect* calls */\r
+ WdfObjectAcquireLock(ep->Queue);\r
switch (ep->State) {\r
case WvEpConnected:\r
- status = WvEpDisconnectActive(Request, out, outlen, ep, pattr);\r
- break;\r
+ status = IbCmInterface.CM.send_dreq(ep->pIbCmId, NULL, 0);\r
+ if (NT_SUCCESS(status)) {\r
+ status = WdfRequestForwardToIoQueue(Request, ep->Queue);\r
+ if (NT_SUCCESS(status)) {\r
+ ep->State = WvEpActiveDisconnect;\r
+ break;\r
+ }\r
+ }\r
+ /* Fall through to passive disconnect case on failure */\r
case WvEpPassiveDisconnect:\r
- status = WvEpDisconnectPassive(Request, out, outlen, ep, pattr);\r
- break;\r
+ ep->State = WvEpDisconnected;\r
+ WdfObjectReleaseLock(ep->Queue);\r
+\r
+ IbCmInterface.CM.send_drep(ep->pIbCmId, NULL, 0);\r
+\r
+ status = WvEpDisconnectQp(ep->pProvider, pattr->QpId, out, outlen);\r
+ if (NT_SUCCESS(status)) {\r
+ WdfRequestCompleteWithInformation(Request, status, outlen);\r
+ }\r
+ goto release;\r
default:\r
- status = STATUS_NOT_SUPPORTED;\r
+ status = STATUS_INVALID_DEVICE_STATE;\r
break;\r
}\r
+ WdfObjectReleaseLock(ep->Queue);\r
\r
+release:\r
WvEpRelease(ep);\r
complete:\r
if (!NT_SUCCESS(status)) {\r