935 lines
21 KiB
C
935 lines
21 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
atkdrvr.c
|
||
|
||
Abstract:
|
||
|
||
This module implements Appletalk Transport Provider driver interfaces
|
||
for NT
|
||
|
||
Author:
|
||
|
||
Jameel Hyder (jameelh@microsoft.com)
|
||
Nikhil Kamkolkar (nikhilk@microsoft.com)
|
||
|
||
Revision History:
|
||
19 Jun 1992 Initial Version
|
||
|
||
Notes: Tab stop: 4
|
||
--*/
|
||
|
||
#include <atalk.h>
|
||
#pragma hdrstop
|
||
|
||
// File module number for errorlogging
|
||
#define FILENUM ATKDRVR
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
#pragma alloc_text(PAGEINIT, AtalkCleanup)
|
||
#pragma alloc_text(PAGE, atalkUnload)
|
||
#pragma alloc_text(PAGE, AtalkDispatchCreate)
|
||
#pragma alloc_text(PAGE, AtalkDispatchCleanup)
|
||
#pragma alloc_text(PAGE, AtalkDispatchClose)
|
||
#pragma alloc_text(PAGE, AtalkDispatchDeviceControl)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the initialization routine for the Windows NT Appletalk
|
||
driver. This routine creates the device object for the Atalk
|
||
device and performs all other driver initialization.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by the system.
|
||
RegistryPath- Path to the root of the section in the registry for this
|
||
driver
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status from the initialization operation. If
|
||
this is not STATUS_SUCCESS the driver will not load.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING deviceName;
|
||
USHORT i, j;
|
||
|
||
|
||
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
|
||
("Appletalk DriverEntry - Entered !!!\n"));
|
||
|
||
|
||
TdiInitialize();
|
||
|
||
INITIALIZE_SPIN_LOCK(&AtalkStatsLock);
|
||
|
||
INITIALIZE_SPIN_LOCK(&AtalkSktCacheLock);
|
||
|
||
INITIALIZE_SPIN_LOCK(&ArapSpinLock);
|
||
|
||
#if DBG
|
||
INITIALIZE_SPIN_LOCK(&AtalkDebugSpinLock);
|
||
#endif
|
||
|
||
// Initialize event for locking/unlocking pageable sections. Set it to signalled state
|
||
// so that the first wait is satisfied.
|
||
KeInitializeMutex(&AtalkPgLkMutex, 0xFFFF);
|
||
|
||
// Create the device object. (IoCreateDevice zeroes the memory
|
||
// occupied by the object.)
|
||
for (i = 0; i < ATALK_NO_DEVICES; i++)
|
||
{
|
||
RtlInitUnicodeString(&deviceName, AtalkDeviceNames[i]);
|
||
status = IoCreateDevice(
|
||
DriverObject, // DriverObject
|
||
ATALK_DEV_EXT_LEN, // DeviceExtension
|
||
&deviceName, // DeviceName
|
||
FILE_DEVICE_NETWORK, // DeviceType
|
||
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
|
||
(BOOLEAN)FALSE, // Exclusive
|
||
(PDEVICE_OBJECT *) &AtalkDeviceObject[i]); // DeviceObject
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
LOG_ERROR(EVENT_ATALK_CANT_CREATE_DEVICE, status, NULL, 0);
|
||
|
||
// Delete all the devices created so far, if any
|
||
for (j = 0; j < i; j++)
|
||
{
|
||
IoDeleteDevice((PDEVICE_OBJECT)AtalkDeviceObject[j]);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
// Assumption:
|
||
// 'i' will correspond to the Device type in the ATALK_DEVICE_TYPE enum
|
||
AtalkDeviceObject[i]->Ctx.adc_DevType = (ATALK_DEV_TYPE)i;
|
||
|
||
// Initialize the provider info and statistics structures for this device
|
||
AtalkQueryInitProviderInfo((ATALK_DEV_TYPE)i,
|
||
&AtalkDeviceObject[i]->Ctx.adc_ProvInfo);
|
||
|
||
#if 0
|
||
// NOTE: Implement
|
||
AtalkQueryInitProviderStatistics((ATALK_DEV_TYPE)i,
|
||
&AtalkDeviceObject[i]->Ctx.adc_ProvStats);
|
||
#endif
|
||
}
|
||
|
||
// Initialize the driver object for this driver's entry points.
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = AtalkDispatchCreate;
|
||
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = AtalkDispatchCleanup;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtalkDispatchClose;
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AtalkDispatchDeviceControl;
|
||
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = AtalkDispatchInternalDeviceControl;
|
||
|
||
DriverObject->DriverUnload = atalkUnload;
|
||
|
||
// Get lock handles to all the conditional pageable sections
|
||
AtalkLockInit(&AtalkPgLkSection[NBP_SECTION], AtalkNbpAction);
|
||
AtalkLockInit(&AtalkPgLkSection[ZIP_SECTION], AtalkZipGetMyZone);
|
||
AtalkLockInit(&AtalkPgLkSection[TDI_SECTION], AtalkTdiCleanupAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[ATP_SECTION], AtalkAtpCloseAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[ASP_SECTION], AtalkAspCloseAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[PAP_SECTION], AtalkPapCleanupAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[ASPC_SECTION], AtalkAspCCloseAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[ADSP_SECTION], AtalkAdspCleanupAddress);
|
||
AtalkLockInit(&AtalkPgLkSection[ROUTER_SECTION], AtalkRtmpPacketInRouter);
|
||
AtalkLockInit(&AtalkPgLkSection[INIT_SECTION], AtalkInitRtmpStartProcessingOnPort);
|
||
AtalkLockInit(&AtalkPgLkSection[ARAP_SECTION], ArapExchangeParms);
|
||
AtalkLockInit(&AtalkPgLkSection[PPP_SECTION], AllocPPPConn);
|
||
|
||
AtalkLockInitIfNecessary();
|
||
|
||
status = AtalkInitializeTransport(DriverObject, RegistryPath);
|
||
|
||
AtalkUnlockInitIfNecessary();
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
#if DBG
|
||
// Make sure we are not unloading with any locked sections
|
||
for (i = 0; i < LOCKABLE_SECTIONS; i++)
|
||
{
|
||
ASSERT (AtalkPgLkSection[i].ls_LockCount == 0);
|
||
}
|
||
#endif
|
||
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
|
||
("DriverEntry: AtalkInitializeTransport failed %lx\n",status));
|
||
}
|
||
else
|
||
{
|
||
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
|
||
("DriverEntry: AtalkInitializeTransport complete %lx\n",status));
|
||
}
|
||
|
||
return status;
|
||
} // DriverEntry
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AtalkDispatchCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the dispatch routine for Create functions for the Appletalk driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for target device
|
||
|
||
pIrp - Pointer to I/O request packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
PFILE_FULL_EA_INFORMATION ea;
|
||
|
||
INT createObject;
|
||
TA_APPLETALK_ADDRESS tdiAddress;
|
||
CONNECTION_CONTEXT connectionContext;
|
||
PATALK_DEV_OBJ atalkDeviceObject;
|
||
|
||
UCHAR protocolType, socketType;
|
||
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchCreate: entered for irp %lx\n", pIrp));
|
||
|
||
// Make sure status information is consistent every time.
|
||
IoMarkIrpPending(pIrp);
|
||
pIrp->IoStatus.Status = STATUS_PENDING;
|
||
pIrp->IoStatus.Information = 0;
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
||
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
|
||
|
||
// Both opens must complete synchronously. It is possible we return
|
||
// status_pending to the system, but it will not return to the caller
|
||
// until the call actually completes. In our case, we block until the
|
||
// actions are complete. So we can be assured that we can complete the irp
|
||
// upon return from these calls.
|
||
|
||
createObject = AtalkIrpGetEaCreateType(pIrp);
|
||
ea = (PFILE_FULL_EA_INFORMATION)pIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
switch (createObject)
|
||
{
|
||
case TDI_TRANSPORT_ADDRESS_FILE :
|
||
if (ea->EaValueLength < sizeof(TA_APPLETALK_ADDRESS))
|
||
{
|
||
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
|
||
("AtalkDispatchCreate: addr size %d\n", ea->EaValueLength));
|
||
|
||
status = STATUS_EA_LIST_INCONSISTENT;
|
||
break;
|
||
}
|
||
|
||
// We have the AtalkTdiOpenAddress routine look at only the first
|
||
// address in the list of addresses by casting the passed address
|
||
// to TA_APPLETALK_ADDRESS.
|
||
RtlCopyMemory(
|
||
&tdiAddress,
|
||
(PBYTE)(&ea->EaName[ea->EaNameLength+1]),
|
||
sizeof(TA_APPLETALK_ADDRESS));
|
||
|
||
// Also, get the protocol type field for the socket
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchCreate: Remaining File Name : %S\n",
|
||
&pIrpSp->FileObject->FileName));
|
||
|
||
if (!NT_SUCCESS(AtalkGetProtocolSocketType(&atalkDeviceObject->Ctx,
|
||
&pIrpSp->FileObject->FileName,
|
||
&protocolType,
|
||
&socketType)))
|
||
{
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
break;
|
||
}
|
||
|
||
status = AtalkTdiOpenAddress(
|
||
pIrp,
|
||
pIrpSp,
|
||
&tdiAddress,
|
||
protocolType,
|
||
socketType,
|
||
&atalkDeviceObject->Ctx);
|
||
|
||
break;
|
||
|
||
case TDI_CONNECTION_FILE :
|
||
if (ea->EaValueLength < sizeof(CONNECTION_CONTEXT))
|
||
{
|
||
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
|
||
("AtalkDispatchCreate: Context size %d\n", ea->EaValueLength));
|
||
|
||
status = STATUS_EA_LIST_INCONSISTENT;
|
||
break;
|
||
}
|
||
|
||
RtlCopyMemory(&connectionContext,
|
||
&ea->EaName[ea->EaNameLength+1],
|
||
sizeof(CONNECTION_CONTEXT));
|
||
|
||
status = AtalkTdiOpenConnection(pIrp,
|
||
pIrpSp,
|
||
connectionContext,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
|
||
case TDI_CONTROL_CHANNEL_FILE :
|
||
status = AtalkTdiOpenControlChannel(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
default:
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_ERR,
|
||
("AtalkDispatchCreate: unknown EA passed!\n"));
|
||
|
||
status = STATUS_INVALID_EA_NAME;
|
||
break;
|
||
}
|
||
|
||
// Successful completion.
|
||
|
||
DBGPRINT(DBG_COMP_CREATE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchCreate complete irp %lx status %lx\n", pIrp, status));
|
||
|
||
if (NT_SUCCESS(status))
|
||
INTERLOCKED_INCREMENT_LONG(&AtalkHandleCount, &AtalkStatsLock);
|
||
|
||
if (status != STATUS_PENDING)
|
||
{
|
||
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
||
|
||
ASSERT (status != STATUS_PENDING);
|
||
TdiCompleteRequest(pIrp, status);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // AtalkDispatchCreate
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AtalkDispatchCleanup(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the dispatch routine for Cleanup functions for the Appletalk driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for target device
|
||
pIrp - Pointer to I/O request packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully
|
||
started/completed
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PATALK_DEV_OBJ atalkDeviceObject;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchCleanup: entered irp %lx\n", pIrp));
|
||
|
||
// Make sure status information is consistent every time.
|
||
IoMarkIrpPending (pIrp);
|
||
pIrp->IoStatus.Status = STATUS_PENDING;
|
||
pIrp->IoStatus.Information = 0;
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
||
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
|
||
|
||
switch ((ULONG_PTR)(pIrpSp->FileObject->FsContext2) & 0xFF)
|
||
{
|
||
case TDI_TRANSPORT_ADDRESS_FILE :
|
||
status = AtalkTdiCleanupAddress(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
|
||
break;
|
||
|
||
case TDI_CONNECTION_FILE :
|
||
status = AtalkTdiCleanupConnection(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
|
||
break;
|
||
|
||
case TDI_CONTROL_CHANNEL_FILE :
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_ERR,
|
||
("AtalkDispatchCleaup: Invalid object %s\n",
|
||
pIrpSp->FileObject->FsContext));
|
||
|
||
status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchCleanup complete irp %lx status %lx\n", pIrp, status));
|
||
|
||
if (status != STATUS_PENDING)
|
||
{
|
||
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
||
|
||
ASSERT (status != STATUS_PENDING);
|
||
TdiCompleteRequest(pIrp, status);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // AtalkDispatchCleanup
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AtalkDispatchClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the dispatch routine for Close functions for the Appletalk driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for target device
|
||
irp - Pointer to I/O request packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
PATALK_DEV_OBJ atalkDeviceObject;
|
||
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchClose: entered for IRP %lx\n", pIrp));
|
||
|
||
// Make sure status information is consistent every time.
|
||
IoMarkIrpPending(pIrp);
|
||
pIrp->IoStatus.Status = STATUS_PENDING;
|
||
pIrp->IoStatus.Information = 0;
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
||
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
|
||
|
||
switch ((ULONG_PTR)(pIrpSp->FileObject->FsContext2) & 0xFF)
|
||
{
|
||
case TDI_TRANSPORT_ADDRESS_FILE :
|
||
status = AtalkTdiCloseAddress(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
|
||
break;
|
||
|
||
case TDI_CONNECTION_FILE :
|
||
status = AtalkTdiCloseConnection(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
|
||
break;
|
||
|
||
case TDI_CONTROL_CHANNEL_FILE :
|
||
status = AtalkTdiCloseControlChannel(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
default:
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_ERR,
|
||
("AtalkDispatchClose: Invalid object %s\n",
|
||
pIrpSp->FileObject->FsContext));
|
||
|
||
status = STATUS_INVALID_HANDLE;
|
||
break;
|
||
}
|
||
|
||
DBGPRINT(DBG_COMP_CLOSE, DBG_LEVEL_INFO,
|
||
("AtalkDispatchClose complete irp %lx status %lx\n", pIrp, status));
|
||
|
||
if (status != STATUS_PENDING)
|
||
{
|
||
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
||
|
||
ASSERT (status != STATUS_PENDING);
|
||
TdiCompleteRequest(pIrp, status);
|
||
}
|
||
|
||
INTERLOCKED_DECREMENT_LONG(&AtalkHandleCount, &AtalkStatsLock);
|
||
|
||
return(status);
|
||
|
||
} // AtalkDispatchClose
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AtalkDispatchDeviceControl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the dispatch routine for Device Control functions for the Appletalk driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for target device
|
||
pIrp - Pointer to I/O request packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PATALK_DEV_OBJ atalkDeviceObject;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
ULONG IoControlCode;
|
||
|
||
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
|
||
("AtalkDispatchDeviceControl: irp %lx\n", pIrp));
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
||
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
|
||
|
||
|
||
IoControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
|
||
|
||
//
|
||
// if it's a request from ARAP, process it here and return
|
||
//
|
||
if (IoControlCode > IOCTL_ARAP_START && IoControlCode < IOCTL_ARAP_END)
|
||
{
|
||
status = ArapProcessIoctl(pIrp);
|
||
|
||
return(status);
|
||
}
|
||
|
||
// Do a map and call the internal device io control function.
|
||
// That will also perform the completion.
|
||
status = TdiMapUserRequest(DeviceObject,
|
||
pIrp,
|
||
pIrpSp);
|
||
|
||
if (status == STATUS_SUCCESS)
|
||
{
|
||
status = AtalkDispatchInternalDeviceControl(
|
||
DeviceObject,
|
||
pIrp);
|
||
|
||
//
|
||
// AtalkDispatchInternalDeviceControl expects to complete the
|
||
// irp
|
||
//
|
||
}
|
||
else
|
||
{
|
||
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_WARN,
|
||
("AtalkDispatchDeviceControl: TdiMap failed %lx\n", status));
|
||
|
||
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
||
|
||
ASSERT (status != STATUS_PENDING);
|
||
TdiCompleteRequest(pIrp, status);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // AtalkDispatchDeviceControl
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AtalkDispatchInternalDeviceControl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the dispatch routine for Internal Device Control functions
|
||
for the Appletalk driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object for target device
|
||
|
||
pIrp - Pointer to I/O request packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
PATALK_DEV_OBJ atalkDeviceObject;
|
||
KIRQL oldIrql;
|
||
|
||
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
|
||
("AtalkDispatchInternalDeviceControl entered for IRP %lx\n", pIrp));
|
||
|
||
// Make sure status information is consistent every time.
|
||
IoMarkIrpPending (pIrp);
|
||
pIrp->IoStatus.Status = STATUS_PENDING;
|
||
pIrp->IoStatus.Information = 0;
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
||
atalkDeviceObject = (PATALK_DEV_OBJ)DeviceObject;
|
||
|
||
|
||
IoAcquireCancelSpinLock(&oldIrql);
|
||
if (!pIrp->Cancel)
|
||
{
|
||
IoSetCancelRoutine(pIrp, (PDRIVER_CANCEL)AtalkTdiCancel);
|
||
}
|
||
else
|
||
{
|
||
IoReleaseCancelSpinLock(oldIrql);
|
||
status = STATUS_CANCELLED;
|
||
TdiCompleteRequest(pIrp, status);
|
||
return(status);
|
||
}
|
||
|
||
IoReleaseCancelSpinLock(oldIrql);
|
||
|
||
// Branch to the appropriate request handler.
|
||
switch (pIrpSp->MinorFunction)
|
||
{
|
||
case TDI_ACCEPT:
|
||
status = AtalkTdiAccept(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_RECEIVE_DATAGRAM:
|
||
status = AtalkTdiReceiveDgram(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_SEND_DATAGRAM:
|
||
status = AtalkTdiSendDgram(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_SET_EVENT_HANDLER:
|
||
status = AtalkTdiSetEventHandler(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_RECEIVE:
|
||
status = AtalkTdiReceive(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_SEND:
|
||
status = AtalkTdiSend(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_ACTION:
|
||
ASSERT(pIrp->MdlAddress != NULL);
|
||
status = AtalkTdiAction(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_ASSOCIATE_ADDRESS:
|
||
status = AtalkTdiAssociateAddress(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_DISASSOCIATE_ADDRESS:
|
||
status = AtalkTdiDisassociateAddress(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_CONNECT:
|
||
status = AtalkTdiConnect(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_DISCONNECT:
|
||
status = AtalkTdiDisconnect(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_LISTEN:
|
||
status = AtalkTdiListen(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_QUERY_INFORMATION:
|
||
ASSERT(pIrp->MdlAddress != NULL);
|
||
status = AtalkTdiQueryInformation(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
case TDI_SET_INFORMATION:
|
||
status = AtalkTdiSetInformation(pIrp,
|
||
pIrpSp,
|
||
&atalkDeviceObject->Ctx);
|
||
break;
|
||
|
||
default:
|
||
// Something we don't know about was submitted.
|
||
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_ERR,
|
||
("AtalkDispatchInternal: fnct %lx\n", pIrpSp->MinorFunction));
|
||
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
DBGPRINT(DBG_COMP_DISPATCH, DBG_LEVEL_INFO,
|
||
("AtalkDispatchInternal complete irp %lx status %lx\n", pIrp, status));
|
||
|
||
// Return the immediate status code to the caller.
|
||
if (status != STATUS_PENDING)
|
||
{
|
||
pIrpSp->Control &= ~SL_PENDING_RETURNED;
|
||
|
||
// Complete the request, this will also dereference it.
|
||
pIrp->CancelRoutine = NULL;
|
||
ASSERT (status != STATUS_PENDING);
|
||
TdiCompleteRequest(pIrp, status);
|
||
}
|
||
|
||
return status;
|
||
} // AtalkDispatchInternalDeviceControl
|
||
|
||
|
||
|
||
|
||
VOID
|
||
atalkUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the unload routine for the Appletalk driver.
|
||
|
||
NOTE: Unload will not be called until all the handles have been
|
||
closed successfully. We just shutdown all the ports, and do
|
||
misc. cleanup.
|
||
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object for this driver.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER (DriverObject);
|
||
|
||
AtalkBindnUnloadStates |= ATALK_UNLOADING;
|
||
|
||
// if we hit that timing window where binding or PnP event is going on,
|
||
// sleep (for a second each time) until that action completes
|
||
while (AtalkBindnUnloadStates & (ATALK_BINDING | ATALK_PNP_IN_PROGRESS))
|
||
{
|
||
AtalkSleep(1000);
|
||
}
|
||
|
||
AtalkLockInitIfNecessary();
|
||
|
||
AtalkCleanup();
|
||
|
||
AtalkUnlockInitIfNecessary();
|
||
|
||
#if DBG
|
||
{
|
||
int i;
|
||
|
||
// Make sure we are not unloading with any locked sections
|
||
for (i = 0; i < LOCKABLE_SECTIONS; i++)
|
||
{
|
||
ASSERT (AtalkPgLkSection[i].ls_LockCount == 0);
|
||
}
|
||
}
|
||
|
||
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
|
||
("Appletalk driver unloaded\n"));
|
||
|
||
#endif
|
||
} // atalkUnload
|
||
|
||
|
||
|
||
VOID
|
||
AtalkCleanup(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is synchronous and will block until Unload Completes
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PPORT_DESCRIPTOR pPortDesc;
|
||
LONG i;
|
||
KIRQL OldIrql;
|
||
|
||
|
||
// Stop the timer subsystem
|
||
AtalkTimerFlushAndStop();
|
||
|
||
ASSERT(KeGetCurrentIrql() == LOW_LEVEL);
|
||
|
||
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
|
||
|
||
// Shut down all ports
|
||
while ((pPortDesc = AtalkPortList) != NULL)
|
||
{
|
||
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
|
||
|
||
AtalkPortShutdown(pPortDesc);
|
||
|
||
ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql);
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql);
|
||
|
||
if (AtalkRegPath.Buffer != NULL)
|
||
{
|
||
AtalkFreeMemory(AtalkRegPath.Buffer);
|
||
}
|
||
|
||
if (AtalkDefaultPortName.Buffer != NULL)
|
||
{
|
||
AtalkFreeMemory(AtalkDefaultPortName.Buffer);
|
||
}
|
||
|
||
for (i = 0; i < ATALK_NO_DEVICES; i++)
|
||
{
|
||
//
|
||
// Delete all the devices created
|
||
//
|
||
IoDeleteDevice((PDEVICE_OBJECT)AtalkDeviceObject[i]);
|
||
}
|
||
|
||
// Deinitialize the Block Package
|
||
AtalkDeInitMemorySystem();
|
||
|
||
// Check if routing is on, if so unlock the router code now
|
||
if (AtalkRouter)
|
||
AtalkUnlockRouterIfNecessary();
|
||
|
||
// Free the rtmp tables
|
||
AtalkRtmpInit(FALSE);
|
||
|
||
// Free the zip tables
|
||
AtalkZipInit(FALSE);
|
||
|
||
// Release ndis resources (buffer/packet pools)
|
||
AtalkNdisReleaseResources();
|
||
|
||
// Deregister the protocol from ndis if handle is non-null
|
||
if (AtalkNdisProtocolHandle != (NDIS_HANDLE)NULL)
|
||
AtalkNdisDeregisterProtocol();
|
||
|
||
ASSERT(AtalkStatistics.stat_CurAllocSize == 0);
|
||
|
||
ASSERT(AtalkDbgMdlsAlloced == 0);
|
||
ASSERT(AtalkDbgIrpsAlloced == 0);
|
||
|
||
#ifdef PROFILING
|
||
ASSERT(AtalkStatistics.stat_CurAllocCount == 0);
|
||
ASSERT(AtalkStatistics.stat_CurMdlCount == 0);
|
||
#endif
|
||
}
|
||
|
||
|