Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3393 lines
94 KiB

/*----------------------------------------------------------------------
initc.c - init, common code(pull out of custom init.c, put in here)
1-27-99 - take out "\device\" in hardware\serialcomm reg entries, kpb.
1-20-99 - adjust unique_id in CreatePortDevice to start names at "RocketPort0".
1-25-99 - adjust again from "\Device\RocketPort0" to "RocketPort0". kpb.
|----------------------------------------------------------------------*/
#include "precomp.h"
//------------ local variables -----------------------------------
static int CheckPortName(IN OUT char *name,
IN PSERIAL_DEVICE_EXTENSION extension);
static int IsPortNameInHardwareMap(char *name);
static char *szDosDevices = {"\\DosDevices\\"};
static char *szDevice = {"\\Device\\"};
#ifdef S_RK
static char *szRocket = {"rocket"};
static char *szRocketSys = {"rocketsys"};
#else
char *szRocket = {"vslinka"};
char *szRocketSys = {"vslinkasys"};
#endif
typedef struct
{
char *response[2];
int response_length[2];
int nextstate[2];
} MSTATE_CHOICE;
static USHORT ErrNum = 1; // used with event logging
#define SEND_CMD_STRING(portex,string) \
ModemWrite(portex,(char *)string,sizeof(string) - 1)
#define SEND_CMD_DELAY_STRING(portex,string) \
ModemWriteDelay(portex,(char *)string,sizeof(string) - 1)
#define READ_RESPONSE_STRINGS(portex,s0,s1,retries) \
ModemReadChoice(portex,(char *)s0,sizeof(s0) - 1,(char *)s1,sizeof(s1) - 1,retries)
#define READ_RESPONSE_STRING(portex,string,retries) \
ModemRead(portex,(char *)string,sizeof(string) - 1,retries)
#define ONE_SECOND 10
#define TWO_SECONDS (2 * ONE_SECOND)
#define THREE_SECONDS (3 * ONE_SECOND)
#define FOUR_SECONDS (4 * ONE_SECOND)
#define FIVE_SECONDS (5 * ONE_SECOND)
#define TENTH_SECOND (ONE_SECOND / 10)
#define HALF_SECOND (ONE_SECOND / 2)
#define MAX_MODEM_ATTEMPTS 3
#ifdef S_RK
#define MAX_STALL 50 // fifo stall count
#define RMODEM_FAILED 0
#define RMODEM_NOT_LOADED 1
#define RMODEM_LOADED 2
#define VERSION_CHAR 'V'
char ChecksumString[16];
int gModemToggle = 0;
typedef struct {
int status;
unsigned long index;
PSERIAL_DEVICE_EXTENSION portex;
} MODEM_STATE;
//------------ local variables -----------------------------------
void ModemTxFIFOWait(PSERIAL_DEVICE_EXTENSION ext);
void ModemResetAll(PSERIAL_DEVICE_EXTENSION ext);
void ChecksumAscii(unsigned short *valueptr);
int IssueEvent(PSERIAL_DEVICE_EXTENSION ext,int (*modemfunc)(),MODEM_STATE *pModemState);
void DownModem(MODEM_STATE *pModemState);
#endif
/*----------------------------------------------------------------------
SerialUnload -
This routine cleans up all of the memory associated with
any of the devices belonging to the driver. It will
loop through the device list.
Arguments:
DriverObject - Pointer to the driver object controling all of the
devices.
Return Value:
None.
|----------------------------------------------------------------------*/
VOID SerialUnload (IN PDRIVER_OBJECT DriverObject)
{
PDEVICE_OBJECT currentDevice = DriverObject->DeviceObject;
// char full_sysname[40];
#ifdef S_VS
int i;
#endif //S_VS
#ifdef S_RK
if (Driver.InterruptObject != NULL)
{
CONTROLLER_T *CtlP; /* ptr to controller structure */
// Disable interupts from RocketPort clear the EOI and
CtlP = Driver.irq_ext->CtlP;
if(CtlP->BusType == Isa)
{
MyKdPrint(D_Init,("Clear ISA IRQ\n"))
sDisGlobalInt(CtlP);
sControllerEOI(CtlP);
}
if(CtlP->BusType == PCIBus)
{
MyKdPrint(D_Init,("Clear PCI IRQ\n"))
sDisGlobalIntPCI(CtlP);
sPCIControllerEOI(CtlP);
}
IoDisconnectInterrupt(Driver.InterruptObject);
Driver.InterruptObject = NULL;
}
#endif
#ifdef S_VS
if (Driver.threadHandle != NULL)
{
ZwClose(Driver.threadHandle);
Driver.threadHandle = NULL; // tell thread to kill itself
time_stall(15); // wait 1.5 second
}
#endif
if (Driver.TimerCreated != 0)
{
KeCancelTimer(&Driver.PollTimer);
Driver.TimerCreated = 0;
}
if (DriverObject->DeviceObject != NULL)
{
// delete all the Deviceobjects and symbolic links
RcktDeleteDevices(DriverObject);
DriverObject->DeviceObject = NULL;
}
#ifdef S_VS
if (Driver.MicroCodeImage != NULL)
{
our_free(Driver.MicroCodeImage, "MCI");
Driver.MicroCodeImage = NULL;
}
if (Driver.nics != NULL)
{
for (i=0; i<VS1000_MAX_NICS; i++)
{
if (Driver.nics[i].NICHandle != NULL) {
NicClose(&Driver.nics[i]);
}
}
our_free(Driver.nics, "nics");
}
Driver.nics = NULL;
if (Driver.NdisProtocolHandle != NULL)
NicProtocolClose();
Driver.NdisProtocolHandle = NULL;
if (Driver.BindNames != NULL)
ExFreePool(Driver.BindNames);
Driver.BindNames = NULL;
#endif
if (Driver.DebugQ.QBase)
{
ExFreePool(Driver.DebugQ.QBase);
Driver.DebugQ.QBase = NULL;
}
if (Driver.RegPath.Buffer != NULL)
{
ExFreePool(Driver.RegPath.Buffer);
Driver.RegPath.Buffer = NULL;
}
if (Driver.OptionRegPath.Buffer != NULL)
{
ExFreePool(Driver.OptionRegPath.Buffer);
Driver.OptionRegPath.Buffer = NULL;
}
}
/*----------------------------------------------------------------------
CreateDriverDevice - Create "rocket" driver object, this is for access to the
driver as a whole. The monitoring program uses this to open up
a channel to get driver information.
Creates a symbolic link name to do special IOctl calls
|----------------------------------------------------------------------*/
NTSTATUS CreateDriverDevice(IN PDRIVER_OBJECT DriverObject,
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension)
{
PDEVICE_OBJECT deviceObject = NULL;
NTSTATUS ntStatus;
PSERIAL_DEVICE_EXTENSION extension = NULL;
char full_ntname[40];
char full_symname[40];
MyKdPrint(D_Init,("CreateDriverDevice\n"))
// Create an device object
{
strcpy(full_ntname,szDevice); // "\\Device\\"
strcat(full_ntname,szRocketSys); // "RocketSys"
// special name
strcpy(full_symname,szDosDevices); // "\\DosDevices\\"
strcat(full_symname,szRocket); // "ROCKET" or "VSLINKA"
ntStatus = IoCreateDevice(
DriverObject,
sizeof(SERIAL_DEVICE_EXTENSION),
CToU1(full_ntname),
//#ifdef NT50
// FILE_DEVICE_BUS_EXTENDER,
//#else
0, // unknown device? , so make a 0 device(unknown?)
//#endif
0, // file characteristics
FALSE, // exclusive?
&deviceObject); // create this
if (!NT_SUCCESS(ntStatus))
{
MyKdPrint(D_Init,("Err CDD1A\n"))
switch (ntStatus)
{
case STATUS_INSUFFICIENT_RESOURCES:
MyKdPrint(D_Init,("Err CDD1B\n"))
break;
case STATUS_OBJECT_NAME_EXISTS:
MyKdPrint(D_Init,("Err CDD1C\n"))
break;
case STATUS_OBJECT_NAME_COLLISION:
MyKdPrint(D_Init,("Err CDD1D\n"))
break;
default:
MyKdPrint(D_Init,("Err CDD1E\n"))
break;
}
return(ntStatus);
}
MyKdPrint(D_Init,("CreateDriver DevObj[%x]: NT:%s\n",
deviceObject, szRocketSys))
//
// Create a symbolic link, e.g. a name that a Win32 app can specify
// to open the device
//
// initialize some of the extension values to make it look like
// another serial port to fake out the supporting functions
// ie open,close, ...
deviceObject->Flags |= DO_BUFFERED_IO;
#ifdef NT50
//
// Enables Irp assignments to be accepted
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
#endif
extension = deviceObject->DeviceExtension;
// Initialize the list heads for the read, write, and mask queues.
// These lists will hold all of the queued IRP's for the device.
InitializeListHead(&extension->ReadQueue);
InitializeListHead(&extension->WriteQueue);
InitializeListHead(&extension->PurgeQueue);
KeInitializeEvent(&extension->PendingIRPEvent, SynchronizationEvent,
FALSE);
// init to 1, so on irp enter its 1 to 2, on exit 2 to 1. 0 on pnp stop.
extension->PendingIRPCnt = 1;
// Mark this device as not being opened by anyone. We keep a
// variable around so that spurious interrupts are easily
// dismissed by the ISR.
extension->DeviceIsOpen = FALSE;
extension->WriteLength = 0;
extension->DeviceObject = deviceObject;
strcpy(extension->NtNameForPort, szRocketSys); // "RocketSys"
extension->DeviceType = DEV_BOARD; // really a driver type, but..
extension->UniqueId = 0;
#ifdef NT50
extension->PowerState = PowerDeviceD0;
#endif
//------ add to the global links
Driver.driver_ext = extension;
// make the public ROCKET or VSLINKA name for applications
ntStatus = IoCreateSymbolicLink(CToU1(full_symname),
CToU2(full_ntname));
if (!NT_SUCCESS(ntStatus))
{
// Symbolic link creation failed- note this & then delete th
MyKdPrint(D_Init,("CDD1E\n"))
return(ntStatus);
}
extension->CreatedSymbolicLink = TRUE;
strcpy(extension->SymbolicLinkName, szRocket); // "ROCKET"
//Driver.RocketSysDeviceObject = deviceObject; //set global device object
//extension->config = ExAllocatePool(NonPagedPool, sizeof(DEVICE_CONFIG));
//RtlZeroMemory(extension->config, sizeof(DEVICE_CONFIG));
#ifdef S_RK
//extension->CtlP = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_T));
//RtlZeroMemory(extension->config, sizeof(CONTROLLER_T));
#endif
//------- Pass back the extension to the caller.
if (DeviceExtension != NULL)
*DeviceExtension = extension;
}
return(ntStatus);
}
/*----------------------------------------------------------------------
CreateBoardDevice - Create "rocket" driver object, this is for access to the
driver as a whole. The monitoring program uses this to open up
a channel to get driver information.
Creates a symbolic link name to do special IOctl calls
Need one for each board so we can use them to do IOReportResources
per board(needed for diferent buses.)
The first board device gets a "ROCKET" symbolic link so we can
open it and query the driver as a whole.
|----------------------------------------------------------------------*/
NTSTATUS CreateBoardDevice(IN PDRIVER_OBJECT DriverObject,
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension)
{
PDEVICE_OBJECT deviceObject = NULL;
NTSTATUS ntStatus;
PSERIAL_DEVICE_EXTENSION extension = NULL;
char full_ntname[40];
char full_symname[40];
char ntname[40];
// for naming device objects, resource submitting, etc we need a
// unique name or id which is unique to the driver. We used to
// use board number or port number for this, but with pnp, things
// come and go on the fly, so instead we create a unique number
// each time we create one of these things.
static int unique_id = 0;
MyKdPrint(D_Init,("CreateBoardDevice\n"))
// Create an EXCLUSIVE device object (only 1 thread at a time
// can make requests to this device)
{
strcpy(ntname, szRocketSys);
our_ultoa(unique_id, &ntname[strlen(ntname)], 10);
strcpy(full_ntname,szDevice); // "\\Device\\"
strcat(full_ntname,ntname); // "RocketPort#"
full_symname[0] = 0;
ntStatus = IoCreateDevice(
DriverObject,
sizeof(SERIAL_DEVICE_EXTENSION),
CToU1(full_ntname),
#ifdef NT50
FILE_DEVICE_BUS_EXTENDER,
#else
0, // unknown device? , so make a 0 device(unknown?)
#endif
0, // file characteristics
FALSE, // exclusive?
&deviceObject); // create this
if (!NT_SUCCESS(ntStatus))
{
MyKdPrint(D_Error,("CBD1A\n"))
switch (ntStatus)
{
case STATUS_INSUFFICIENT_RESOURCES:
MyKdPrint(D_Error,("CBD1B\n"))
break;
case STATUS_OBJECT_NAME_EXISTS:
MyKdPrint(D_Error,("CBD1C\n"))
break;
case STATUS_OBJECT_NAME_COLLISION:
MyKdPrint(D_Error,("CBD1D\n"))
break;
default:
MyKdPrint(D_Error,("CBD1E\n"))
break;
}
return(ntStatus);
}
++unique_id; // go to next id so next call will be different.
// Create a symbolic link, e.g. a name that a Win32 app can specify
// to open the device
//
// initialize some of the extension values to make it look like
// another serial port to fake out the supporting functions
// ie open,close, ...
deviceObject->Flags |= DO_BUFFERED_IO;
#ifdef NT50
//
// Enables Irp assignments to be accepted
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
#endif
MyKdPrint(D_Init,("CreateBoard DevObj[%x]: NT:%s\n",
deviceObject, ntname))
extension = deviceObject->DeviceExtension;
// Initialize the list heads for the read, write, and mask queues.
// These lists will hold all of the queued IRP's for the device.
InitializeListHead(&extension->ReadQueue);
InitializeListHead(&extension->WriteQueue);
//InitializeListHead(&extension->MaskQueue);
InitializeListHead(&extension->PurgeQueue);
KeInitializeEvent(&extension->PendingIRPEvent, SynchronizationEvent,
FALSE);
// init to 1, so on irp enter its 1 to 2, on exit 2 to 1. 0 on pnp stop.
extension->PendingIRPCnt = 1;
// Mark this device as not being opened by anyone. We keep a
// variable around so that spurious interrupts are easily
// dismissed by the ISR.
extension->DeviceIsOpen = FALSE;
extension->WriteLength = 0;
extension->DeviceObject = deviceObject;
strcpy(extension->NtNameForPort, ntname); // "RocketSys"
extension->DeviceType = DEV_BOARD;
extension->UniqueId = unique_id;
#ifdef NT50
extension->PowerState = PowerDeviceD0;
#endif
//------ add to the chain of boards
if (Driver.board_ext == NULL)
Driver.board_ext = extension;
else
{
PSERIAL_DEVICE_EXTENSION add_ext;
add_ext = Driver.board_ext;
while (add_ext->board_ext != NULL)
add_ext = add_ext->board_ext;
add_ext->board_ext = extension;
}
extension->SymbolicLinkName[0] = 0;
extension->config = ExAllocatePool(NonPagedPool, sizeof(DEVICE_CONFIG));
if ( extension->config == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(extension->config, sizeof(DEVICE_CONFIG));
#ifdef S_RK
extension->CtlP = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_T));
if ( extension->CtlP == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(extension->config, sizeof(CONTROLLER_T));
#endif
#ifdef S_VS
// allocate the hdlc & port manager structs
extension->hd = (Hdlc *)our_locked_alloc(sizeof(Hdlc), "Dhd");
extension->pm = (PortMan *)our_locked_alloc(sizeof(PortMan),"Dpm");
extension->pm->hd = extension->hd; // set this up, avoids trouble
#endif
//------- Pass back the extension to the caller.
if (DeviceExtension != NULL)
*DeviceExtension = extension;
}
return(ntStatus);
}
/*----------------------------------------------------------------------
CreateReconfigPortDevices -
This routine attempts to resize a rocketport or vs1000 number of
ports.
|----------------------------------------------------------------------*/
NTSTATUS CreateReconfigPortDevices(IN PSERIAL_DEVICE_EXTENSION board_ext,
int new_num_ports)
{
PSERIAL_DEVICE_EXTENSION newExtension = NULL;
PSERIAL_DEVICE_EXTENSION next_ext;
PSERIAL_DEVICE_EXTENSION port_ext;
int ch;
NTSTATUS stat;
// bugbug: if pnp-ports, we should be adding and removing pdo's,
// not fdo's.
int is_fdo = 1;
int existing_ports;
MyKdPrint(D_Init,("ReconfigNumPorts"))
if (board_ext == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
#ifdef S_RK
// doesn't make as much sense to redo this on the fly as in VS.
// rocketport special re-configure startup code would be needed.
return STATUS_INSUFFICIENT_RESOURCES;
#endif
// code needs work! don't allow for nt40 as well....
return STATUS_INSUFFICIENT_RESOURCES;
#ifdef NT50
// if we are doing pnp-ports, we probably need to remove the
// pdo's then inform the os to rescan pdos.
if (!Driver.NoPnpPorts)
return STATUS_INSUFFICIENT_RESOURCES;
#endif
existing_ports = NumPorts(board_ext);
if (new_num_ports == existing_ports)
return STATUS_SUCCESS;
if (new_num_ports == 0)
return STATUS_INSUFFICIENT_RESOURCES;
MyKdPrint(D_Init,("ReconfigNumPorts B"))
++Driver.Stop_Poll; // flag to stop poll access
if (new_num_ports < existing_ports) // want less ports
{
// see if anyones got the ports we want to kill off open.
port_ext = board_ext->port_ext;
for (ch=0; ch<existing_ports; ch++)
{
if (ch>=new_num_ports)
{
if (port_ext->DeviceIsOpen)
{
--Driver.Stop_Poll; // flag to stop poll access
MyKdPrint(D_Error,("Port OpenErr\n"))
return STATUS_INSUFFICIENT_RESOURCES; // no they are open
}
}
port_ext = port_ext->port_ext;
}
MyKdPrint(D_Error,("Removing Ports\n"))
//---- must be ok to kill them off
port_ext = board_ext->port_ext;
for (ch=0; ch<existing_ports; ch++)
{
next_ext = port_ext->port_ext;
if (ch>=new_num_ports)
{
RcktDeletePort(port_ext);
}
port_ext = next_ext;
}
}
else if (new_num_ports > existing_ports) // want more ports
{
for (ch=existing_ports; ch<new_num_ports; ch++)
{
stat = CreatePortDevice(Driver.GlobalDriverObject,
board_ext,
&newExtension,
ch,is_fdo);
if (stat != STATUS_SUCCESS)
{
--Driver.Stop_Poll; // flag to stop poll access
MyKdPrint(D_Error,("StartErr 8E"))
return stat;
}
} // loop thru ports
} // if more ports
board_ext->config->NumPorts = new_num_ports;
#ifdef S_VS
stat = VSSpecialStartup(board_ext);
if (stat != STATUS_SUCCESS)
{
--Driver.Stop_Poll; // flag to start poll access
MyKdPrint(D_Error,("StartErr 8F"))
return stat;
}
#endif
--Driver.Stop_Poll; // flag to stop poll access
return STATUS_SUCCESS;
}
/*----------------------------------------------------------------------
CreatePortDevices -
This routine attempts to initialize all the ports on a multiport board
Arguments:
DriverObject - Simply passed on to the controller initialization routine.
ConfigData - A linked list of configuration information for all
the ports on a multiport card.
DeviceExtension - Will point to the first successfully initialized
port on the multiport card.
Return Value: None.
|----------------------------------------------------------------------*/
NTSTATUS CreatePortDevices(IN PDRIVER_OBJECT DriverObject)
{
PSERIAL_DEVICE_EXTENSION newExtension = NULL;
int ch, bd;
NTSTATUS stat;
int is_fdo = 1;
PSERIAL_DEVICE_EXTENSION ext;
ext = Driver.board_ext;
bd = 0;
while (ext)
{
for (ch=0; ch<ext->config->NumPorts; ch++)
{
stat = CreatePortDevice(DriverObject,
ext,
&newExtension,
ch,is_fdo);
if (stat != STATUS_SUCCESS)
return stat;
stat = StartPortHardware(newExtension, ch);
if (stat != STATUS_SUCCESS)
return stat;
}
++bd;
ext = ext->board_ext; // next in chain
} // while ext
return STATUS_SUCCESS;
}
/*----------------------------------------------------------------------
StartPortHardware -
|----------------------------------------------------------------------*/
NTSTATUS StartPortHardware(IN PSERIAL_DEVICE_EXTENSION port_ext,
int chan_num)
{
#ifdef S_VS
int i;
PSERIAL_DEVICE_EXTENSION board_ext;
board_ext = port_ext->board_ext;
MyKdPrint(D_Pnp, ("StartHrdw bd:%d ch:%d\n",
BoardExtToNumber(board_ext), chan_num))
if (port_ext->Port == NULL)
{
port_ext->Port = board_ext->pm->sp[chan_num];
if (port_ext->Port == NULL)
{
MyKdPrint(D_Error,("FATAL Err4F\n"))
KdBreakPoint();
}
}
#else
CONTROLLER_T *CtlP; /* ptr to controller structure */
PSERIAL_DEVICE_EXTENSION board_ext;
int aiop_i, ch_i;
board_ext = port_ext->board_ext;
//board_num = BoardExtToNumber(board_ext);
MyKdPrint(D_Pnp,("StartHrdw bd:%d ch:%d\n",
BoardExtToNumber(board_ext), chan_num))
CtlP = board_ext->CtlP; // point to our board struct
// Set pointers to the Rocket's info
port_ext->ChP = &port_ext->ch;
// bugbug: what about special rocketmodem startup? Should we
// be doing this for pdo's and fdo's? Should we have a flag
// indicating job done?
aiop_i = chan_num / CtlP->PortsPerAiop;
ch_i = chan_num % CtlP->PortsPerAiop;
if(!sInitChan(CtlP, // ptr to controller struct
port_ext->ChP, // ptr to chan struct
aiop_i, // aiop #
(unsigned char)ch_i)) // chan #
{
Eprintf("Err Ch %d on Brd %d", chan_num+1,
BoardExtToNumber(board_ext)+1);
return STATUS_INSUFFICIENT_RESOURCES;
}
#endif
return STATUS_SUCCESS;
}
/*----------------------------------------------------------------------
CreatePortDevice -
Forms and sets up names, creates the device, initializes kernel
synchronization structures, allocates the typeahead buffer,
sets up defaults, etc.
Arguments:
DriverObject - Just used to create the device object.
ParentExtension - a pnp port this will be null.
DeviceExtension - Points to the device extension of the successfully
initialized controller. We return this handle.
chan_num - 0,1,2,... port index
is_fdo - is a functional device object(normal port) as apposed to
a pdo(physical device object) which is used to pnp enumerate
"found" hardware by our driver.
Return Value:
STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
otherwise.
|----------------------------------------------------------------------*/
NTSTATUS CreatePortDevice(
IN PDRIVER_OBJECT DriverObject,
IN PSERIAL_DEVICE_EXTENSION ParentExtension,
OUT PSERIAL_DEVICE_EXTENSION *DeviceExtension,
IN int chan_num, // 0,1,2,... port index
IN int is_fdo) // is a functional device object(normal port)
{
char full_ntname[40];
char comname[16];
char ntname[20];
NTSTATUS status = STATUS_SUCCESS;
int stat;
ULONG do_type;
PUNICODE_STRING pucodename;
static int unique_id = 0;
ULONG do_characteristics;
BOOLEAN do_is_exclusive;
// Points to the device object (not the extension) created
// for this device.
PDEVICE_OBJECT deviceObject;
// Points to the device extension for the device object
// (see above) created for the device we are initializing.
PSERIAL_DEVICE_EXTENSION extension = NULL;
#ifdef S_VS
strcpy(ntname, "Vslinka");
#else
strcpy(ntname, "RocketPort");
#endif
// copy over the name in the configuration for dos-name
strcpy(comname, ParentExtension->config->port[chan_num].Name);
// setup the nt io-object nt-name
if (is_fdo)
{
strcpy(full_ntname, szDevice); // "\\Device\\"
}
else
{
// this is what serenum does for naming its pdo's
strcpy(full_ntname, "\\Serial\\");
strcat(ntname, "Pdo"); // just to make sure its unique
}
our_ultoa(unique_id, &ntname[strlen(ntname)], 10);
strcat(full_ntname, ntname);
if (is_fdo)
{
++unique_id; // go to next id so next call will be different.
// normal case(nt40), and a functional device object in nt5
stat = CheckPortName(comname, NULL); // ensure name is unique
if (stat) // name changed
{
// save back the new name to the configuration struct
strcpy(ParentExtension->config->port[chan_num].Name, comname);
}
do_type = FILE_DEVICE_SERIAL_PORT;
do_characteristics = 0;
do_is_exclusive = TRUE;
}
else
{
// nt5 pnp physical device object(spawns a fdo later)
//do_type = FILE_DEVICE_BUS_EXTENDER;
do_type = FILE_DEVICE_UNKNOWN;
#ifdef NT50
// nt4 doesn't know what FILE_AUTOGENERATED_DEVICE_NAME is.
do_characteristics = FILE_AUTOGENERATED_DEVICE_NAME;
#else
do_characteristics = 0;
#endif
do_is_exclusive = FALSE;
//pucodename = NULL; // no name if a PDO
}
pucodename = CToU1(full_ntname);
//---------------------------- Create the device object for this device.
status = IoCreateDevice(
DriverObject,
sizeof(SERIAL_DEVICE_EXTENSION),
pucodename, // name
do_type, // FILE_DEVICE_BUS_EXTENDER, FILE_DEVICE_SERIAL_PORT, etc
do_characteristics,// characteristics
do_is_exclusive, // exclusive
&deviceObject); // new thing this call creates
// If we couldn't create the device object, then there
// is no point in going on.
if (!NT_SUCCESS(status))
{
MyKdPrint(D_Init,("Err, IoCreate: NT:%s, SYM:%s\n",
ntname, comname))
EventLog(DriverObject,
status,
SERIAL_DEVICEOBJECT_FAILED,
0, NULL);
return STATUS_INSUFFICIENT_RESOURCES;
}
// The device object has a pointer to an area of non-paged
// pool allocated for this device. This will be the device extension.
extension = deviceObject->DeviceExtension;
// Zero all of the memory associated with the device extension.
RtlZeroMemory(extension, sizeof(SERIAL_DEVICE_EXTENSION));
extension->PortIndex = chan_num; // record the port index 0,1,2..
// for NT5.0, set this up here so we don't crash.(NT4.0 sets
// up prior to this.
extension->port_config = &ParentExtension->config->port[chan_num];
extension->UniqueId = unique_id;
if (!is_fdo)
{
MyKdPrint(D_Init,("PDO-"))
}
MyKdPrint(D_Init,("CreatePort DevObj[%x]: NT:%s, SYM:%s\n",
deviceObject, ntname, comname))
// save off a ptr to our parent board extension
extension->board_ext = ParentExtension;
{
PSERIAL_DEVICE_EXTENSION add_ext = NULL;
if (is_fdo)
{
//------ add to the chain of ports under board ext
if (ParentExtension->port_ext == NULL)
ParentExtension->port_ext = extension;
else
add_ext = ParentExtension->port_ext;
}
else // pdo, ejected pnp enumeration
{
//------ add to the chain of pdo-ports under board ext
if (ParentExtension->port_pdo_ext == NULL)
ParentExtension->port_pdo_ext = extension;
else
add_ext = ParentExtension->port_pdo_ext;
}
if (add_ext)
{
while (add_ext->port_ext != NULL)
add_ext = add_ext->port_ext;
add_ext->port_ext = extension;
}
}
// Initialize the list heads for the read, write, and mask queues.
// These lists will hold all of the queued IRP's for the device.
InitializeListHead(&extension->ReadQueue);
InitializeListHead(&extension->WriteQueue);
//InitializeListHead(&extension->MaskQueue);
InitializeListHead(&extension->PurgeQueue);
// Initialize the spinlock associated with fields read (& set)
// by IO Control functions.
KeInitializeSpinLock(&extension->ControlLock);
// Initialize the timers used to timeout operations.
KeInitializeTimer(&extension->ReadRequestTotalTimer);
KeInitializeTimer(&extension->ReadRequestIntervalTimer);
KeInitializeTimer(&extension->WriteRequestTotalTimer);
KeInitializeTimer(&extension->XoffCountTimer);
KeInitializeDpc(&extension->CompleteWriteDpc,
SerialCompleteWrite,
extension);
KeInitializeDpc(&extension->CompleteReadDpc,
SerialCompleteRead,
extension);
// Timeout Dpc initialization
KeInitializeDpc(&extension->TotalReadTimeoutDpc,
SerialReadTimeout,
extension);
KeInitializeDpc(&extension->IntervalReadTimeoutDpc,
SerialIntervalReadTimeout,
extension);
KeInitializeDpc(&extension->TotalWriteTimeoutDpc,
SerialWriteTimeout,
extension);
KeInitializeDpc(&extension->CommErrorDpc,
SerialCommError,
extension);
KeInitializeDpc(&extension->CommWaitDpc,
SerialCompleteWait,
extension);
KeInitializeDpc(&extension->XoffCountTimeoutDpc,
SerialTimeoutXoff,
extension);
KeInitializeDpc(&extension->XoffCountCompleteDpc,
SerialCompleteXoff,
extension);
// Get a "back pointer" to the device object and specify
// that this driver only supports buffered IO. This basically
// means that the IO system copies the users data to and from
// system supplied buffers.
extension->DeviceObject = deviceObject;
extension->DevStatus = 0;
deviceObject->Flags |= DO_BUFFERED_IO;
#ifdef NT50
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
if (!is_fdo) // its a PDO, so adjust stack requirements
{
deviceObject->StackSize += ParentExtension->DeviceObject->StackSize;
}
#endif
KeInitializeEvent(&extension->PendingIRPEvent, SynchronizationEvent,
FALSE);
// init to 1, so on irp enter its 1 to 2, on exit 2 to 1. 0 on pnp stop.
extension->PendingIRPCnt = 1;
// Set up the default device control fields.
// Note that if the values are changed after
// the file is open, they do NOT revert back
// to the old value at file close.
extension->SpecialChars.XonChar = SERIAL_DEF_XON;
extension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
extension->SpecialChars.ErrorChar=0;
extension->SpecialChars.EofChar=0;
extension->SpecialChars.EventChar=0;
extension->SpecialChars.BreakChar=0;
extension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
extension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
extension->BaudRate = 9600;
extension->LineCtl.Parity = NO_PARITY;
extension->LineCtl.WordLength = 8;
extension->LineCtl.StopBits = STOP_BIT_1;
#ifdef S_RK
extension->ModemCtl=COM_MDM_RTS | COM_MDM_DTR;
extension->IntEnables =(RXINT_EN | MCINT_EN | CHANINT_EN | TXINT_EN);
#endif
// We set up the default xon/xoff limits.
extension->HandFlow.XoffLimit = extension->BufferSize >> 3;
extension->HandFlow.XonLimit = extension->BufferSize >> 1;
extension->BufferSizePt8 = ((3*(extension->BufferSize>>2))+
(extension->BufferSize>>4));
// Initialize stats counters
extension->OurStats.ReceivedCount = 0L;
extension->OurStats.TransmittedCount = 0L;
extension->OurStats.ParityErrorCount = 0L;
extension->OurStats.FrameErrorCount = 0L;
extension->OurStats.SerialOverrunErrorCount = 0L;
extension->OurStats.BufferOverrunErrorCount = 0L;
// Mark this device as not being opened by anyone. We keep a
// variable around so that spurious interrupts are easily
// dismissed by the ISR.
extension->DeviceIsOpen = FALSE;
extension->WriteLength = 0;
#ifdef NT50
extension->PowerState = PowerDeviceD0;
#endif
// This call will set up the naming necessary for
// external applications to get to the driver. It
// will also set up the device map.
strcpy(extension->NtNameForPort, ntname); // RocketPort# or VSLINKA#
strcpy(extension->SymbolicLinkName, comname); // "COM#"
if (is_fdo)
{
SerialSetupExternalNaming(extension); // Configure ports!!!!
// Check for default settings in registry
InitPortsSettings(extension);
}
else
{
// eject PDOs (physical device objects)representing port hardware.
extension->IsPDO = 1; // we are a pdo
}
// Store values into the extension for interval timing.
// If the interval timer is less than a second then come
// in with a short "polling" loop.
// For large ( >2 seconds) use a 1 second poller.
extension->ShortIntervalAmount.QuadPart = -1;
extension->LongIntervalAmount.QuadPart = -10000000;
extension->CutOverAmount.QuadPart = 200000000;
//------- Pass back the extension to the caller.
*DeviceExtension = extension;
return STATUS_SUCCESS;
}
/*-----------------------------------------------------------------------
RcktDeleteDriverObj - This routine will delete a board and all its ports
for PnP remove handling.
|----------------------------------------------------------------------*/
VOID RcktDeleteDriverObj(IN PSERIAL_DEVICE_EXTENSION extension)
{
//int i;
//PSERIAL_DEVICE_EXTENSION ext;
PSERIAL_DEVICE_EXTENSION del_ext;
MyKdPrint(D_Init,("Delete Driver Obj:%x\n", extension->DeviceObject))
MyKdPrint(D_Init,(" IrpCnt:%x\n", extension->PendingIRPCnt))
if (NULL == extension)
{
MyKdPrint(D_Init,("Err8U\n"))
return;
}
++Driver.Stop_Poll; // flag to stop poll access
del_ext = extension; // now kill board
SerialCleanupDevice(del_ext); // delete any port stuff on ext.
#ifdef NT50
if (del_ext->LowerDeviceObject != NULL)
{
IoDetachDevice(del_ext->LowerDeviceObject);
del_ext->LowerDeviceObject = NULL;
}
#endif
IoDeleteDevice(del_ext->DeviceObject);
--Driver.Stop_Poll; // flag to stop poll access
}
/*----------------------------------------------------------------------
RcktDeleteDevices - This routine will delete all devices
|----------------------------------------------------------------------*/
VOID RcktDeleteDevices(IN PDRIVER_OBJECT DriverObject)
{
PDEVICE_OBJECT currentDevice = DriverObject->DeviceObject;
int i;
i = 0;
while(currentDevice)
{
PSERIAL_DEVICE_EXTENSION extension = currentDevice->DeviceExtension;
currentDevice = currentDevice->NextDevice;
SerialCleanupDevice(extension);
#ifdef NT50
if (extension->LowerDeviceObject != NULL)
{
IoDetachDevice(extension->LowerDeviceObject);
extension->LowerDeviceObject = NULL;
}
#endif
MyKdPrint(D_Init,("RcktDeleteDev Obj:%x\n", extension->DeviceObject))
//MyKdPrint(D_Init,(" IrpCnt:%x\n", extension->PendingIRPCnt))
IoDeleteDevice(extension->DeviceObject);
i++;
}
MyKdPrint(D_Init,("Deleted %d Device Objects\n", i))
}
/*----------------------------------------------------------------------
RcktDeleteBoard - This routine will delete a board and all its ports
for PnP remove handling.
|----------------------------------------------------------------------*/
VOID RcktDeleteBoard(IN PSERIAL_DEVICE_EXTENSION extension)
{
int i;
PSERIAL_DEVICE_EXTENSION ext;
PSERIAL_DEVICE_EXTENSION del_ext;
MyKdPrint(D_Init, ("Delete Board\n"))
if (NULL == extension)
{
MyKdPrint(D_Error,("Err9X\n"))
return;
}
++Driver.Stop_Poll; // flag to stop poll access
MyKdPrint(D_Init, ("Delete Ports\n"))
// release any port things
ext = extension->port_ext;
i = 0;
while (ext)
{
del_ext = ext; // kill this one
ext = ext->port_ext; // next in list
SerialCleanupDevice(del_ext); // delete any allocated stuff on ext.
#ifdef NT50
if (del_ext->LowerDeviceObject != NULL)
{
IoDetachDevice(del_ext->LowerDeviceObject);
del_ext->LowerDeviceObject = NULL;
}
#endif
MyKdPrint(D_Init,("RcktDeleteBoard Port Obj:%x\n", del_ext->DeviceObject))
//MyKdPrint(D_Init,(" IrpCnt:%x\n", del_ext->PendingIRPCnt))
IoDeleteDevice(del_ext->DeviceObject);
i++;
}
extension->port_ext = NULL;
MyKdPrint(D_Init,("Deleted %d Ports\n", i))
// release any PDO port things
ext = extension->port_pdo_ext;
i = 0;
while (ext)
{
del_ext = ext; // kill this one
ext = ext->port_ext; // next in list
SerialCleanupDevice(del_ext); // delete any port stuff on ext.
#ifdef NT50
if (del_ext->LowerDeviceObject != NULL)
{
IoDetachDevice(del_ext->LowerDeviceObject);
del_ext->LowerDeviceObject = NULL;
}
#endif
MyKdPrint(D_Init,("RcktDeleteBoard PDO Port Obj:%x\n", del_ext->DeviceObject))
//MyKdPrint(D_Init,(" IrpCnt:%x\n", del_ext->PendingIRPCnt))
IoDeleteDevice(del_ext->DeviceObject);
i++;
}
extension->port_pdo_ext = NULL;
MyKdPrint(D_Init,("Deleted PDO %d Ports\n", i))
del_ext = NULL;
// take out of linked list
ext = Driver.board_ext;
if (ext == extension) // first in list
{
del_ext = extension; // kill this board
Driver.board_ext = extension->board_ext;
}
else
{
while (ext)
{
if (ext->board_ext == extension) // found in list, so take out of list
{
del_ext = extension; // kill this board
ext->board_ext = extension->board_ext; // link around deleted one
break;
}
ext = ext->board_ext;
}
}
MyKdPrint(D_Init,("Delete board_ext:%x, remaining: %d\n",
extension, NumDevices()))
if (del_ext != NULL)
{
SerialCleanupDevice(del_ext); // delete any port stuff on ext.
#ifdef NT50
if (del_ext->LowerDeviceObject != NULL)
{
IoDetachDevice(del_ext->LowerDeviceObject);
del_ext->LowerDeviceObject = NULL;
}
#endif
MyKdPrint(D_Init,("RcktDeleteBoard Obj:%x\n", del_ext->DeviceObject))
//MyKdPrint(D_Init,(" IrpCnt:%x\n", del_ext->PendingIRPCnt))
IoDeleteDevice(del_ext->DeviceObject);
}
--Driver.Stop_Poll; // flag to stop poll access
}
/*----------------------------------------------------------------------
RcktDeletePort - This routine will delete a port and is used for
PnP remove, start handling. I don't think we ever delete PDO's,
(other than driver unload) here.
|----------------------------------------------------------------------*/
VOID RcktDeletePort(IN PSERIAL_DEVICE_EXTENSION extension)
{
PSERIAL_DEVICE_EXTENSION ext;
PSERIAL_DEVICE_EXTENSION del_ext;
MyKdPrint(D_Init,("RcktDeletePort\n"))
if (NULL == extension)
{
MyKdPrint(D_Error,("Err8X\n"))
return;
}
++Driver.Stop_Poll; // flag to stop poll access
MyKdPrint(D_Init, ("Delete Port\n"))
del_ext = NULL;
ext = extension->board_ext; // parent board extension
while (ext)
{
if (ext->port_ext == extension) // found the one before it
{
del_ext = extension;
ext->port_ext = extension->port_ext; // skip link to next
break;
}
ext = ext->port_ext;
}
if (del_ext != NULL)
{
SerialCleanupDevice(del_ext); // delete any port stuff on ext.
#ifdef NT50
if (del_ext->LowerDeviceObject != NULL)
{
IoDetachDevice(del_ext->LowerDeviceObject);
del_ext->LowerDeviceObject = NULL;
}
#endif
MyKdPrint(D_Init,("RcktDeletePort Obj:%x\n", del_ext->DeviceObject))
//MyKdPrint(D_Init,(" IrpCnt:%x\n", del_ext->PendingIRPCnt))
IoDeleteDevice(del_ext->DeviceObject);
MyKdPrint(D_Init,("Deleted Port\n"))
}
--Driver.Stop_Poll; // flag to stop poll access
}
/*----------------------------------------------------------------------
SerialCleanupDevice -
This routine will deallocate all of the memory used for
a particular device. It will also disconnect any resources
if need be.
Arguments:
Extension - Pointer to the device extension which is getting
rid of all it's resources.
Return Value:
None.
|----------------------------------------------------------------------*/
VOID SerialCleanupDevice (IN PSERIAL_DEVICE_EXTENSION Extension)
{
MyKdPrint(D_Test, ("Mem Alloced Start:%d\n", Driver.mem_alloced))
++Driver.Stop_Poll; // flag to stop poll access
if (Extension)
{
if (Extension->DeviceType == DEV_PORT)
{
//KeRemoveQueueDpc(&Extension->RocketReadDpc);
//KeRemoveQueueDpc(&Extension->RocketWriteDpc);
KeCancelTimer(&Extension->ReadRequestTotalTimer);
KeCancelTimer(&Extension->ReadRequestIntervalTimer);
KeCancelTimer(&Extension->WriteRequestTotalTimer);
KeCancelTimer(&Extension->XoffCountTimer);
KeRemoveQueueDpc(&Extension->CompleteWriteDpc);
KeRemoveQueueDpc(&Extension->CompleteReadDpc);
// Timeout
KeRemoveQueueDpc(&Extension->TotalReadTimeoutDpc);
KeRemoveQueueDpc(&Extension->IntervalReadTimeoutDpc);
KeRemoveQueueDpc(&Extension->TotalWriteTimeoutDpc);
// Timeout
KeRemoveQueueDpc(&Extension->CommErrorDpc);
KeRemoveQueueDpc(&Extension->CommWaitDpc);
KeRemoveQueueDpc(&Extension->XoffCountTimeoutDpc);
KeRemoveQueueDpc(&Extension->XoffCountCompleteDpc);
}
else // board device
{
#ifdef S_VS
if (Extension->hd)
{
hdlc_close(Extension->hd);
our_free(Extension->hd, "Dhd");
Extension->hd = NULL;
}
if (Extension->pm)
{
portman_close(Extension->pm);
our_free(Extension->pm,"Dpm");
Extension->pm = NULL;
}
#endif
#ifdef S_RK
if (Extension->io_reported) // tells that we should deallocate on unload.
{
SerialUnReportResourcesDevice(Extension); // give back io,irq resources
Extension->io_reported = 0;
}
if (Extension->CtlP)
{
ExFreePool(Extension->CtlP);
Extension->CtlP = NULL;
}
#endif
// free board config if present
if (Extension->config)
{
ExFreePool(Extension->config);
Extension->config = NULL;
}
} // board dev
// Get rid of all external naming as well as removing
// the device map entry.
SerialCleanupExternalNaming(Extension);
} // if not a null extension
MyKdPrint(D_Test, ("Mem Alloced End:%d\n", Driver.mem_alloced))
--Driver.Stop_Poll; // flag to stop poll access
}
#ifdef S_RK
/*------------------------------------------------------------------
SerialGetMappedAddress -
This routine maps an IO address to system address space.
Arguments:
BusType - what type of bus - eisa, mca, isa
IoBusNumber - which IO bus (for machines with multiple buses).
IoAddress - base device address to be mapped.
NumberOfBytes - number of bytes for which address is valid.
AddressSpace - Denotes whether the address is in io space or memory.
MappedAddress - indicates whether the address was mapped.
This only has meaning if the address returned
is non-null.
Return Value:
Mapped address
----------------------------------------------------------------------*/
PVOID SerialGetMappedAddress(
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
PHYSICAL_ADDRESS IoAddress,
ULONG NumberOfBytes,
ULONG AddressSpace,
PBOOLEAN MappedAddress,
BOOLEAN DoTranslation)
{
PHYSICAL_ADDRESS cardAddress;
PVOID address;
if (DoTranslation)
{
if(!HalTranslateBusAddress(
BusType,
BusNumber,
IoAddress,
&AddressSpace,
&cardAddress)){
// if the translate address call failed return null so we don't load
address = NULL;
return address;
}
}
else
{
cardAddress = IoAddress;
}
// Map the device base address into the virtual address space
// if the address is in memory space.
if (!AddressSpace) {
address = MmMapIoSpace(cardAddress,
NumberOfBytes,
FALSE);
*MappedAddress = (BOOLEAN)((address)?(TRUE):(FALSE));
}
else
{
address = (PVOID)cardAddress.LowPart;
*MappedAddress = FALSE;
}
return address;
}
#endif
/*------------------------------------------------------------------
Routine Description:
This routine will be used to create a symbolic link
to the driver name in the given object directory.
It will also create an entry in the device map for
this device - IF we could create the symbolic link.
Arguments:
Extension - Pointer to the device extension.
Return Value:
None.
-------------------------------------------------------------------*/
VOID SerialSetupExternalNaming (IN PSERIAL_DEVICE_EXTENSION Extension)
{
char full_ntname[50];
char full_comname[40];
NTSTATUS status;
strcpy(full_ntname, szDevice); // "\\Device\\"
strcat(full_ntname, Extension->NtNameForPort); // "Rocket#"
strcpy(full_comname, szDosDevices); // "\\DosDevices\\"
strcat(full_comname, Extension->SymbolicLinkName); // "COM#"
MyKdPrint(D_Init,("SetupExtName:%s\n", Extension->SymbolicLinkName))
status = IoCreateSymbolicLink(
CToU2(full_comname), // like "\\DosDevices\\COM5"
CToU1(full_ntname)); // like "\\Device\\RocketPort0"
if (NT_SUCCESS(status)) {
MyKdPrint( D_Init, ("Symbolic link %s created\n", full_comname ))
}
else {
MyKdPrint(D_Init,("Err SymLnkCreate.\n"))
// Oh well, couldn't create the symbolic link. No point
// in trying to create the device map entry.
SerialLogError(
Extension->DeviceObject->DriverObject,
Extension->DeviceObject,
0,
0,
0,
ErrNum++,
status,
SERIAL_NO_SYMLINK_CREATED,
CToU1(Extension->SymbolicLinkName)->Length+sizeof(WCHAR),
CToU1(Extension->SymbolicLinkName)->Buffer);
return;
}
Extension->CreatedSymbolicLink = TRUE;
// Add entry to let system and apps know about our ports
// after V3.23 I added "\device\" into the registry entry(this was wrong)
// 1-26-99, bugfix, don't add "\device\" into the registry entry,
// this is not what serial.sys does. kpb.
status = RtlWriteRegistryValue(
RTL_REGISTRY_DEVICEMAP,
L"SERIALCOMM",
CToU2(Extension->NtNameForPort)->Buffer, // "RocketPort0"
//CToU2(full_ntname)->Buffer, // "\Device\Vslinka0"
REG_SZ,
CToU1(Extension->SymbolicLinkName)->Buffer, // COM#
CToU1(Extension->SymbolicLinkName)->Length+sizeof(WCHAR));
if (!NT_SUCCESS(status))
{
MyKdPrint(D_Init,("GenError C2.\n"))
SerialLogError(Extension->DeviceObject->DriverObject,
Extension->DeviceObject,
0,
0,
0,
ErrNum++,
status,
SERIAL_NO_DEVICE_MAP_CREATED,
CToU1(Extension->SymbolicLinkName)->Length+sizeof(WCHAR),
CToU1(Extension->SymbolicLinkName)->Buffer);
}
}
/*---------------------------------------------------------------------
SerialCleanupExternalNaming -
This routine will be used to delete a symbolic link
to the driver name in the given object directory.
It will also delete an entry in the device map for
this device if the symbolic link had been created.
Arguments:
Extension - Pointer to the device extension.
|----------------------------------------------------------------------*/
VOID SerialCleanupExternalNaming(IN PSERIAL_DEVICE_EXTENSION Extension)
{
char name[60];
NTSTATUS status;
// We're cleaning up here. One reason we're cleaning up
// is that we couldn't allocate space for the directory
// name or the symbolic link.
if (Extension->CreatedSymbolicLink)
{
MyKdPrint(D_Init,("KillSymLink:%s\n", Extension->SymbolicLinkName))
strcpy(name, szDosDevices); // "\\DosDevices\\"
strcat(name, Extension->SymbolicLinkName); // like "COM5"
IoDeleteSymbolicLink(CToU1(name));
#ifdef NT50
// Only for ports!
if (Extension->DeviceType == DEV_PORT &&
&Extension->DeviceClassSymbolicName != NULL &&
Extension->DeviceClassSymbolicName.Buffer != NULL) {
MyKdPrint(D_Init,("KillInterface:%s\n",
UToC1(&Extension->DeviceClassSymbolicName)))
status = IoSetDeviceInterfaceState( &Extension->DeviceClassSymbolicName, FALSE );
if (!NT_SUCCESS(status)) {
MyKdPrint(D_Error,("Couldn't clear class association for %s\n",
UToC1(&Extension->DeviceClassSymbolicName)))
}
else {
MyKdPrint(D_Init, ("Cleared class association for device: %s\n",
UToC1(&Extension->DeviceClassSymbolicName)))
}
RtlFreeUnicodeString( &Extension->DeviceClassSymbolicName );
Extension->DeviceClassSymbolicName.Buffer = NULL;
}
#endif
Extension->CreatedSymbolicLink = 0;
}
if (Extension->DeviceType == DEV_PORT)
{
// Delete any reg entry to let system and apps know about our ports
strcpy(name, szDevice); // "\\Device\\"
strcat(name, Extension->NtNameForPort); // "Rocket#"
status = RtlDeleteRegistryValue(
RTL_REGISTRY_DEVICEMAP,
L"SERIALCOMM",
CToU1(Extension->NtNameForPort)->Buffer); // "RocketPort0"
//CToU1(name)->Buffer);
MyKdPrint(D_Init, ("RtlDeleteRegistryValue:%s\n",Extension->NtNameForPort))
#if NT50
// Make sure the ComDB binary data is cleared for the specific port. There
// are some problems with W2000 PnP Manager taking care of this in every
// circumstance.
(void)clear_com_db( Extension->SymbolicLinkName );
#endif
}
}
/*-----------------------------------------------------------------------
SerialLogError -
This routine allocates an error log entry, copies the supplied data
to it, and requests that it be written to the error log file.
Arguments:
DriverObject - A pointer to the driver object for the device.
DeviceObject - A pointer to the device object associated with the
device that had the error, early in initialization, one may not
yet exist.
P1,P2 - If phyical addresses for the controller ports involved
with the error are available, put them through as dump data.
SequenceNumber - A ulong value that is unique to an IRP over the
life of the irp in this driver - 0 generally means an error not
associated with an irp.
MajorFunctionCode - If there is an error associated with the irp,
this is the major function code of that irp.
RetryCount - The number of times a particular operation has been
retried.
UniqueErrorValue - A unique long word that identifies the particular
call to this function.
FinalStatus - The final status given to the irp that was associated
with this error. If this log entry is being made during one of
the retries this value will be STATUS_SUCCESS.
SpecificIOStatus - The IO status for a particular error.
LengthOfInsert1 - The length in bytes (including the terminating NULL)
of the first insertion string.
Return Value:
None.
|-----------------------------------------------------------------------*/
VOID SerialLogError(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
IN ULONG SequenceNumber,
IN UCHAR MajorFunctionCode,
IN UCHAR RetryCount,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIOStatus,
IN ULONG LengthOfInsert1,
IN PWCHAR Insert1)
{
PIO_ERROR_LOG_PACKET errorLogEntry;
PVOID objectToUse;
PUCHAR ptrToFirstInsert;
if (DeviceObject != NULL)
objectToUse = DeviceObject;
else
objectToUse = DriverObject;
errorLogEntry = IoAllocateErrorLogEntry(
objectToUse,
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET) + LengthOfInsert1));
if ( errorLogEntry == NULL)
{
MyKdPrint(D_Init,("SerialLogErr, Err A size:%d obj:%x\n",
LengthOfInsert1,objectToUse))
return;
}
errorLogEntry->ErrorCode = SpecificIOStatus;
errorLogEntry->SequenceNumber = SequenceNumber;
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
errorLogEntry->RetryCount = RetryCount;
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
errorLogEntry->FinalStatus = FinalStatus;
errorLogEntry->DumpDataSize = 0;
ptrToFirstInsert = (PUCHAR)&errorLogEntry->DumpData[0];
if (LengthOfInsert1)
{
errorLogEntry->NumberOfStrings = 1;
errorLogEntry->StringOffset = (USHORT)(ptrToFirstInsert -
(PUCHAR)errorLogEntry);
RtlCopyMemory(ptrToFirstInsert,
Insert1,
LengthOfInsert1);
}
IoWriteErrorLogEntry(errorLogEntry);
}
/*-----------------------------------------------------------------------
EventLog - To put a shell around the SerialLogError to make calls easier
to use.
|-----------------------------------------------------------------------*/
VOID EventLog(
IN PDRIVER_OBJECT DriverObject,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIOStatus,
IN ULONG LengthOfInsert1,
IN PWCHAR Insert1)
{
SerialLogError(
DriverObject,
NULL,
0,
0,
0,
ErrNum++,
FinalStatus,
SpecificIOStatus,
LengthOfInsert1,
Insert1);
return;
}
/*-----------------------------------------------------------------------
InitPortsSettings - Read registry default Port setting
|-----------------------------------------------------------------------*/
VOID InitPortsSettings(IN PSERIAL_DEVICE_EXTENSION extension)
{
RTL_QUERY_REGISTRY_TABLE paramTable[2];
#define MAX_STRING 256
WCHAR StrValBuf[MAX_STRING+1];
char comname[20];
UNICODE_STRING USReturn;
USReturn.Buffer = NULL;
RtlInitUnicodeString(&USReturn, NULL);
USReturn.MaximumLength = sizeof(WCHAR)*MAX_STRING;
USReturn.Buffer = StrValBuf;
if(strlen(extension->SymbolicLinkName) < sizeof(comname))
{
strncpy(comname, extension->SymbolicLinkName, strlen(extension->SymbolicLinkName) + 1);
strcat(comname, ":");
}
RtlZeroMemory(&paramTable[0],sizeof(paramTable));
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = CToU1(comname)->Buffer;
paramTable[0].EntryContext = &USReturn;
paramTable[0].DefaultType = REG_SZ;
paramTable[0].DefaultData = L"";
paramTable[0].DefaultLength = 0;
if (!NT_SUCCESS(RtlQueryRegistryValues(
// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
RTL_REGISTRY_WINDOWS_NT,
L"Ports",
&paramTable[0],
NULL,
NULL
)))
{
// no entry
return;
}
// Check for data, indicates settings exist for COMX
if (USReturn.Length == 0)
{
// no entry
return;
}
#define TOKENS 5
#define MAX_DIGITS 6
{
unsigned int TokenCounter;
unsigned int CharCounter;
unsigned int LastCount;
WCHAR *TokenPtrs[TOKENS];
ULONG BaudRateValue;
// Make sure all Token ptrs point to NULL
for(TokenCounter = 0; TokenCounter < TOKENS; TokenCounter++)
TokenPtrs[TokenCounter] = NULL;
// init counters
TokenCounter = 0;
LastCount = 0;
for(CharCounter = 0; CharCounter < USReturn.Length; CharCounter++)
{
if(StrValBuf[CharCounter] == ',')
{
StrValBuf[CharCounter] = '\0'; //Null terminate DbgPrint
TokenPtrs[TokenCounter++] = &StrValBuf[LastCount];
//point to beginning of next string
LastCount = CharCounter +1;
}
}
// set up the last token
if(CharCounter < MAX_STRING)
StrValBuf[CharCounter] = '\0'; //Null terminate DbgPrint NULL
if(TokenCounter < TOKENS)
TokenPtrs[TokenCounter] = &StrValBuf[LastCount];
// token 0: baud rate
if(TokenPtrs[0] != NULL)
{
BaudRateValue = 0;
CharCounter = 0;
while( (TokenPtrs[0][CharCounter] != '\0') && //DbgPrint NULL
(CharCounter < MAX_DIGITS) &&
(BaudRateValue != ~0x0L) )
{
BaudRateValue *= 10;
switch(TokenPtrs[0][CharCounter++])
{
case '0': break;
case '1': BaudRateValue += 1; break;
case '2': BaudRateValue += 2; break;
case '3': BaudRateValue += 3; break;
case '4': BaudRateValue += 4; break;
case '5': BaudRateValue += 5; break;
case '6': BaudRateValue += 6; break;
case '7': BaudRateValue += 7; break;
case '8': BaudRateValue += 8; break;
case '9': BaudRateValue += 9; break;
default: BaudRateValue = ~0x0UL; break;
}
}
if ((BaudRateValue >= 50) && (BaudRateValue <= 460800))
extension->BaudRate = BaudRateValue; // allow any baud rate
#ifdef COMMENT_OUT
switch (BaudRateValue)
{
// Valid baud rates
case 50: case 75: case 110:
case 134: case 150: case 200:
case 300: case 600: case 1200:
case 1800: case 2400: case 4800:
case 7200: case 9600: case 19200:
case 38400: case 57600: case 76800:
case 115200: case 230400: case 460800:
extension->BaudRate = BaudRateValue;
break;
default:
// Selected baud rate not available for RocketPort COMX
break;
}
#endif
}
// token 1: parity
if(TokenPtrs[1] != NULL)
{
switch (TokenPtrs[1][0])
{
case 'n':
extension->LineCtl.Parity = NO_PARITY;
break;
case 'o':
extension->LineCtl.Parity = ODD_PARITY;
break;
case 'e':
extension->LineCtl.Parity = EVEN_PARITY;
break;
default:
// Selected parity not available for RocketPort COMX
break;
}
}
// token 2: data bits
if(TokenPtrs[2] != NULL)
{
switch (TokenPtrs[2][0])
{
case '7':
extension->LineCtl.WordLength = 7;
break;
case '8':
extension->LineCtl.WordLength = 8;
break;
default:
// Selected databits not available for RocketPort COMX
break;
}
}
// token 3: Stop bits
if(TokenPtrs[3] != NULL)
{
switch (TokenPtrs[3][0])
{
case '1':
extension->LineCtl.StopBits = STOP_BIT_1;
break;
case '2':
extension->LineCtl.StopBits = STOP_BITS_2;
break;
default:
break;
}
}
// token 4: flow control: rts/cts or XON/XOFF
if(TokenPtrs[4] != NULL)
{
switch (TokenPtrs[4][0])
{
case 'x': // XON/XOFF f/c
extension->HandFlow.FlowReplace |=
(SERIAL_AUTO_TRANSMIT | SERIAL_AUTO_RECEIVE) ;
break;
case 'p': // RTS/CTS f/c
extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK;
extension->HandFlow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
extension->HandFlow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
break;
default:
break;
} // Selected flowcontrol not available for RocketPort COMX
} // flow control token
}
}
/*----------------------------------------------------------------------
CheckPortName - If the name is already used, then derive one that is
not in use.
name - name of port to check. Modify if a problem.
Return - 0=name ok, 1=generated modified name, other= error.
|----------------------------------------------------------------------*/
static int CheckPortName(IN OUT char *name,
IN PSERIAL_DEVICE_EXTENSION extension)
{
int i;
char prefix[20];
int num;
int new_name_given = 0;
MyKdPrint(D_Init, ("CheckPortName:%s\n", name));
// if no name, give a reasonable default.
if (name[0] == 0)
{
new_name_given = 1; // flag it
strcpy(name, "COM3");
}
// load prefix(such as "COM" from "COM25" from name)
i = 0;
while ( (!our_isdigit(name[i])) && (name[i] != 0) && (i < 18))
{
prefix[i] = name[i];
++i;
}
prefix[i] = 0;
// now grab post-fix number value incase we need to derive a new name
num = 0;
if (our_isdigit(name[i]))
num = getint(&name[i], NULL);
i = 0;
for (;;)
{
// if we are already using this name, or if its in the registry
if ((find_ext_by_name(name, NULL) != NULL) ||
(IsPortNameInHardwareMap(name)) )
{
// name already in use, so derive a new one
new_name_given = 1; // flag it
++num; // give a new post-fix index(so "COM4" to "COM5")
Sprintf(name, "%s%d", prefix, num);
}
else
{ // name is ok
if (new_name_given)
{
MyKdPrint(D_Init, ("Form new name:%s\n", name))
}
return new_name_given; // return 0 if no change made, 1 if changed
}
++i;
if (i > 5000)
{
// problems
return 2; // return error
}
}
}
/*----------------------------------------------------------------------
IsPortNameInHardwareMap - For Pnp operation, we startup before configuration,
so pick a reasonable starting com-port name. We do this by finding
registry entries for all existing com-ports in the system. This
info is used to determine a name for the port.
|----------------------------------------------------------------------*/
static int IsPortNameInHardwareMap(char *name)
{
static char *szRegRMHDS =
{"\\Registry\\Machine\\Hardware\\DeviceMap\\SerialComm"};
HANDLE KeyHandle = NULL;
ULONG data_type;
int node_num = 0;
char buffer[200];
char KeyNameStr[60];
char *data_ptr;
int stat;
//MyKdPrint(D_Init, ("IsPortNameInHardwareMap\n"))
stat = our_open_key(&KeyHandle, NULL, szRegRMHDS, KEY_READ);
if (stat)
{
MyKdPrint(D_Init, ("Failed OpenKey\n"))
return 0; // return no name clash
}
KeyNameStr[0] = 0;
for(;;)
{
stat = our_enum_value(KeyHandle,
node_num,
buffer,
200,
&data_type,
&data_ptr,
KeyNameStr);
++node_num;
if (stat)
{
//MyKdPrint(D_Init, ("Done\n"))
break;
}
//MyKdPrint(D_Init, ("Got Value:%s\n", KeyNameStr))
if (data_type != REG_SZ)
{
MyKdPrint(D_Init, ("Not RegSZ\n"))
break;
}
WStrToCStr(KeyNameStr, (PWCHAR)data_ptr, 18);
//MyKdPrint(D_Init, ("KeyFound:%s\n", KeyNameStr))
if (my_lstricmp(KeyNameStr, name) == 0) // match
{
// we got trouble, our name matches one already in registry
//MyKdPrint(D_Init, ("Not a good name.\n"))
return 1; // err: name clash
}
}
return 0; // ok, no name clash
}
/*----------------------------------------------------------------------
RcktInitPollTimer - Initialize the poll timer for no interrupt operation.
The fastest we can poll seems to be 10ms under NT.
|----------------------------------------------------------------------*/
NTSTATUS RcktInitPollTimer(void)
{
MyKdPrint(D_Init,("RcktInitPollTimer\n"))
KeInitializeDpc(&Driver.TimerDpc, TimerDpc, NULL);
KeInitializeTimer(&Driver.PollTimer);
// ScanRate is registry option in MS units.
if (Driver.ScanRate < 1) Driver.ScanRate = 1;
if (Driver.ScanRate > 40) Driver.ScanRate = 40;
// NT Interval unit is 100nsec so to get Freq polls/sec
Driver.PollIntervalTime.QuadPart = Driver.ScanRate * -10000;
#ifdef S_VS
Driver.Tick100usBase = 100; // 100us base units(typical:100)
Driver.TickBaseCnt = Driver.ScanRate * 10;
KeQuerySystemTime(&Driver.IsrSysTime);
KeQuerySystemTime(&Driver.LastIsrSysTime);
#endif
Driver.TimerCreated = 1; // tells to deallocate
return STATUS_SUCCESS;
}
/*-----------------------------------------------------------------------
InitSocketModems -
This function is responsible for clearing the initial reset state on
any device with SocketModems and initializing the location information
(ROW) for each SocketModem on the device. We only initialize extensions
for which the device extension has the ModemDevice field enabled in
the config information. VS2000 devices don't need to be cleared from
reset since the firmware does that.
|-----------------------------------------------------------------------*/
void InitSocketModems(PSERIAL_DEVICE_EXTENSION ext)
{
DEVICE_CONFIG *cfg = ext->config;
PSERIAL_DEVICE_EXTENSION portex,head_portex;
MyKdPrint(D_Init,("InitSocketModems\n"))
// use the PDO port list, if present since they start up first under nt5
head_portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
if (!cfg->ModemDevice) return;
#ifdef S_RK
/*
RMII boards don't require ROW codes set...
*/
if (
((cfg->PCI_DevID == PCI_DEVICE_RMODEM6)
||
(cfg->PCI_DevID == PCI_DEVICE_RMODEM4))
&&
(cfg->PCI_RevID == PCI_REVISION_RMODEM_II)
)
return;
#ifdef MDM_RESET
// in case the modems are hung up, we'd like server reloads to clear them
// up...so, even though it's likely the modems are in reset state already,
// put them there again...
portex = head_portex;
while (portex)
{
ModemReset(portex,1);
portex = portex->port_ext;
}
// allow the socketmodems to reset...
time_stall(Driver.MdmSettleTime);
#endif
// clear the ports on the board from the reset state
portex = head_portex;
while (portex)
{
ModemReset(portex, 0);
portex = portex->port_ext;
}
// allow the socketmodems to settle after clearing them from reset
time_stall(Driver.MdmSettleTime);
#endif
time_stall(20);
/*
send the localization string (ROW) to each socketmodem, whether internal
or external (VS2000)...
*/
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
while (portex) {
ModemWriteROW(portex, Driver.MdmCountryCode);
portex = portex->port_ext;
}
MyKdPrint(D_Init,("InitSocketModems: exit\n"))
}
#ifdef TRYED_IT_WORKED_REALLY_BAD
/*-----------------------------------------------------------------------
DumpTracefile -
|-----------------------------------------------------------------------*/
static int DumpTracefile(void)
{
NTSTATUS ntStatus;
HANDLE NtFileHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
USTR_160 uname;
FILE_STANDARD_INFORMATION StandardInfo;
//ULONG LengthOfFile;
static char *def_filename = {"\\SystemRoot\\system32\\VSLINKA\\trace.txt"};
BYTE *buf;
buf = our_locked_alloc(1010,"dump");
CToUStr((PUNICODE_STRING)&uname, def_filename, sizeof(uname));
InitializeObjectAttributes ( &ObjectAttributes,
&uname.ustr,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
#ifdef COMMENT_OUT
ntStatus = ZwCreateFile( &NtFileHandle,
SYNCHRONIZE | FILE_WRITE_DATA | FILE_APPEND_DATA,
// GENERIC_WRITE | SYNCHRONIZE,
&ObjectAttributes,
&IoStatus,
NULL, // alloc size = none
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE,
FILE_SUPERSEDE,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, // eabuffer
0); // ealength
if (!NT_SUCCESS(ntStatus))
{
Eprintf("Dump Error B");
our_free(buf, "dump");
return 1;
}
// Write the file from our buffer.
ntStatus = ZwWriteFile(NtFileHandle,
NULL,NULL,NULL,
&IoStatus,
"Start of file> ",
14,
FILE_WRITE_TO_END_OF_FILE, NULL);
while (!q_empty(&Driver.DebugQ))
{
int q_cnt;
q_cnt = q_count(&Driver.DebugQ);
if (q_cnt > 1000)
q_cnt = 1000;
q_get(&Driver.DebugQ, buf, q_cnt);
// Write the file from our buffer.
ntStatus = ZwWriteFile(NtFileHandle,
NULL,NULL,NULL,
&IoStatus,
buf,
q_cnt,
FILE_WRITE_TO_END_OF_FILE, NULL);
}
if (!NT_SUCCESS(ntStatus))
Eprintf("Dump Error A:%d",ntStatus);
ZwClose(NtFileHandle);
#endif
our_free(buf, "dump");
return 0;
}
#endif
/********************************************************************
RocketModem II loader stuff...
********************************************************************/
#ifdef S_RK
/*
responses are forced to upper case for ease in checking (response case
varies depending on whether the modem was loaded already or not...
*/
#define MODEM_LOADCHECK_CMD "ATI3\r"
#define MODEM_RESET_CMD "ATZ0\r"
#define MODEM_LOAD_CMD "AT**\r"
#define MODEM_LOADCHECK_RESP "V2.101A2-V90_2M_DLS_RAM\r\n"
#define DOWNLOAD_INITIATED_RESP "DOWNLOAD INITIATED ..\r\n"
#define CSM_READY_RESP "115.2K\r\n"
#define FIRMWARE_READY_RESP "DEVICE SUCCESSFULLY PROGRAMMED\r\nCHECKSUM: "
#define OK_RESP "OK"
/**********************************************************************
send ATI3 to determine if modem is loaded...
**********************************************************************/
static int
RM_Snd_ATI3_Command(MODEM_STATE *pModemState)
{
/*
discard any data currently in the receive FIFO...
*/
if (RxFIFOReady(pModemState->portex)) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem on %s overrun",
pModemState->portex->SymbolicLinkName);
return(0);
}
SEND_CMD_STRING(pModemState->portex,MODEM_LOADCHECK_CMD);
return(1);
}
/**********************************************************************
check response to ATI3 - modem loaded or unloaded...
**********************************************************************/
static int
RM_Rcv_ATI3_Response(MODEM_STATE *pModemState)
{
int index;
index = READ_RESPONSE_STRINGS(pModemState->portex,
OK_RESP,
MODEM_LOADCHECK_RESP,
ONE_SECOND);
switch (index) {
/*
loaded with the firmware revision this release of RocketPort NT driver expects...
*/
case 0: {
pModemState->status = RMODEM_NOT_LOADED;
break;
}
case 1: {
pModemState->status = RMODEM_LOADED;
break;
}
default: {
/*
either it didn't respond, or responded with the wrong string. either way,
we'll reset it (again) and then reload it...
*/
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem on %s no response (I3)",
pModemState->portex->SymbolicLinkName);
return(0);
}
}
return(1);
}
/**********************************************************************
response to AT** command received...
**********************************************************************/
static int
RM_Rcv_ModemLoad_Response(MODEM_STATE *pModemState)
{
int index;
index = READ_RESPONSE_STRING(
pModemState->portex,
DOWNLOAD_INITIATED_RESP,
FIVE_SECONDS);
if (index) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem on %s no response (LL)",
pModemState->portex->SymbolicLinkName);
return(0);
}
return(1);
}
/**********************************************************************
CSM loaded response...
**********************************************************************/
static int
RM_Rcv_FirmwareLoader_Loaded(MODEM_STATE *pModemState)
{
int index;
index = READ_RESPONSE_STRING(
pModemState->portex,
CSM_READY_RESP,
FIVE_SECONDS);
if (index) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem on %s no response (FL)",
pModemState->portex->SymbolicLinkName);
return(0);
}
return(1);
}
/**********************************************************************
check if firmware loaded successfully...
**********************************************************************/
static int
RM_Rcv_FirmwareLoaded_Response(MODEM_STATE *pModemState)
{
int index;
char workstring[sizeof(FIRMWARE_READY_RESP) + 4];
char *to,*from;
from = FIRMWARE_READY_RESP;
to = workstring;
index = sizeof(FIRMWARE_READY_RESP) - 1;
while (index--)
*(to++) = *(from++);
from = ChecksumString;
index = 4;
while (index--)
*(to++) = *(from++);
*(to++) = 0;
index = 0;
index = READ_RESPONSE_STRING(
pModemState->portex,
workstring,
FIVE_SECONDS);
if (index) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem %s bad response to load",
pModemState->portex->SymbolicLinkName);
return(0);
}
pModemState->status = RMODEM_LOADED;
return(1);
}
/**********************************************************************
write a CSM byte. flush any '.' response...
**********************************************************************/
static int
RM_Snd_Loader_Data(MODEM_STATE *pModemState)
{
int loop;
loop = 100;
/*
see if there's any available space in the transmit FIFO. if not, pause...
*/
while (
(!TxFIFOReady(pModemState->portex))
&&
(loop-- > 0)
) {
/*
pause for any characters currently in the transmit FIFO to move on out...
*/
ms_time_stall(1);
}
/*
if still no room, bail out...
*/
if (!TxFIFOReady(pModemState->portex)) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem %s won't accept loader",
pModemState->portex->SymbolicLinkName);
return(0);
}
/*
write a byte, then go on to next modem...
*/
ModemWrite(
pModemState->portex,
(char *)&Driver.ModemLoaderCodeImage[pModemState->index++],
(int)1);
/*
discard any data currently in the receive FIFO...
*/
if (RxFIFOReady(pModemState->portex)) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem %s loader overrun",
pModemState->portex->SymbolicLinkName);
return(0);
}
return(1);
}
/**********************************************************************
write a firmware byte. flush any '.' response...
**********************************************************************/
static int
RM_Snd_Firmware_Data(MODEM_STATE *pModemState)
{
int origcount;
int loop;
origcount = (int)TxFIFOStatus(pModemState->portex);
loop = 100;
/*
see if there's any available space in the transmit FIFO. if not, pause...
*/
while (
(!TxFIFOReady(pModemState->portex))
&&
(loop-- > 0)
) {
/*
pause for characters currently in the transmit FIFO to make room...
*/
ms_time_stall(1);
}
if (!TxFIFOReady(pModemState->portex)) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem %s won't accept firmware",
pModemState->portex->SymbolicLinkName);
return(0);
}
/*
write a byte, then go on to next modem...
*/
ModemWrite(
pModemState->portex,
(char *)&Driver.ModemCodeImage[pModemState->index++],
(int)1);
/*
discard any data currently in the receive FIFO...
*/
if (RxFIFOReady(pModemState->portex)) {
pModemState->status = RMODEM_FAILED;
Eprintf("Warning: Modem %s firmware overrun",
pModemState->portex->SymbolicLinkName);
return(0);
}
return(1);
}
/**********************************************************************
send modem load AT command...
**********************************************************************/
static int
RM_Snd_ModemLoad_Command(MODEM_STATE *pModemState)
{
SEND_CMD_STRING(pModemState->portex,MODEM_LOAD_CMD);
return(1);
}
/**********************************************************************
shutdown modem and port...
**********************************************************************/
static int
RM_CleanUp(MODEM_STATE *pModemState)
{
if (pModemState->status == RMODEM_FAILED) {
DownModem(pModemState);
return(0);
}
ModemUnReady(pModemState->portex);
return(1);
}
#endif
/**********************************************************************
load RocketModemII devices...
**********************************************************************/
void
InitRocketModemII(PSERIAL_DEVICE_EXTENSION ext)
{
#ifdef S_RK
DEVICE_CONFIG * cfg;
PSERIAL_DEVICE_EXTENSION portex,head_portex;
MODEM_STATE ModemState[8];
int modem_count,
loaded_modem_count,
modem_index,
retry;
ULONG index,version_index;
long checksum;
char VersionString[9];
char *cptr,*endptr;
// Eprintf("RocketModemII init start"); // turn on for timing purposes...
cfg = ext->config;
/*
verify this is a RMII board, 4 or 6 port, before proceeding further...
*/
if (!cfg->ModemDevice) {
return;
}
if (
(cfg->PCI_DevID != PCI_DEVICE_RMODEM6)
&&
(cfg->PCI_DevID != PCI_DEVICE_RMODEM4)
)
return;
if (cfg->PCI_RevID != PCI_REVISION_RMODEM_II)
return;
/*
use the PDO port list, if present since they start up first under nt5.
prepare the ports to each modem...
*/
head_portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
if (head_portex == (PSERIAL_DEVICE_EXTENSION)NULL) {
MyKdPrint(D_Init,("InitRocketModemII: No port extensions\r"))
return;
}
retry = 1;
do {
modem_count = 0;
head_portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
portex = head_portex;
while ((void *)portex) {
ModemIOReady(portex,115200);
ModemState[modem_count].status = RMODEM_NOT_LOADED;
ModemState[modem_count].portex = portex;
ModemState[modem_count].index = 0;
portex = portex->port_ext;
++modem_count;
}
time_stall(ONE_SECOND);
/*
after pausing for ports to set up, start with modem hardware reset, before issuing ATI3,
making sure that modems are cleaned up and in command mode...
*/
ModemResetAll(ext);
/*
enable RMII speaker...
*/
ModemSpeakerEnable(head_portex);
/*
start with check on modem load status, by issuing ATI3 to just the first modem. if the first modem
isn't loaded, assume all the others aren't either. if the first modem is loaded, check the rest. if the
first modem receive fifo doesn't clear, mark accordingly, then proceed with loading...
*/
(void) RM_Snd_ATI3_Command(ModemState);
ModemTxFIFOWait(ext);
(void) RM_Rcv_ATI3_Response(ModemState);
loaded_modem_count = 0;
if (ModemState[0].status == RMODEM_LOADED) {
/*
modem 0 was loaded. check remaining modems. if any aren't loaded, load them all...
*/
++loaded_modem_count;
modem_index = 1;
portex = head_portex->port_ext;
while ((void *)portex) {
if (ModemState[modem_index].status != RMODEM_FAILED)
(void) RM_Snd_ATI3_Command(&ModemState[modem_index]);
++modem_index;
portex = portex->port_ext;
}
/*
ATI3 load probe command sent, waiting for OK or loaded firmware revision
response. if no modems respond, ignore for now...
*/
ModemTxFIFOWait(ext);
modem_index = 1;
portex = head_portex->port_ext;
while ((void *)portex) {
if (ModemState[modem_index].status != RMODEM_FAILED)
(void) RM_Rcv_ATI3_Response(&ModemState[modem_index]);
++modem_index;
portex = portex->port_ext;
}
/*
now see if any modems require loading. if any do, reset all modems again,
and then issue the download modem command to all modems...
*/
modem_index = 0;
portex = head_portex->port_ext;
while ((void *)portex) {
if (ModemState[modem_index++].status == RMODEM_LOADED)
++loaded_modem_count;
portex = portex->port_ext;
}
}
/*
if any modems are unloaded, load them all...
*/
if (loaded_modem_count != modem_count) {
ModemResetAll(ext);
(void) IssueEvent(ext,RM_Snd_ModemLoad_Command,ModemState);
/*
load commands output. while they're leaving the transmit FIFO,
read in the CSM loader and modem firmware files...
*/
if (LoadModemCode((char *)NULL,(char *)NULL)) {
Eprintf("Warning: Modem firmware file error");
FreeModemFiles();
continue;
}
/*
wait until the download commands are truly gone. then start waiting for
the response. if no modems respond, bail out...
*/
ModemTxFIFOWait(ext);
if (IssueEvent(ext,RM_Rcv_ModemLoad_Response,ModemState) == 0) {
FreeModemFiles();
continue;
}
modem_index = 0;
while (modem_index < modem_count)
ModemState[modem_index++].index = 0;
/*
response received, apparently. grind through CSM loader file, sending a byte to
all modems...
*/
index = 0;
while (index++ < Driver.ModemLoaderCodeSize)
(void) IssueEvent(ext,RM_Snd_Loader_Data,ModemState);
/*
spin while transmit FIFOs clear, then pause for responses to arrive...
*/
ModemTxFIFOWait(ext);
/*
wait for loading at 115.2K response to CSM load. after response, pause
a moment for any remaining receive data to arrive. bail out if no modems
respond...
*/
if (IssueEvent(ext,RM_Rcv_FirmwareLoader_Loaded,ModemState) == 0) {
MyKdPrint(D_Init,("InitRocketModemII: No recognized responses to loader load datastream\r"))
FreeModemFiles();
continue;
}
time_stall(HALF_SECOND);
modem_index = 0;
while (modem_index < modem_count)
ModemState[modem_index++].index = 0;
/*
grind through firmware file, sending a byte to all modems. skip the location
in the binary where the checksum will reside - it's just trash right now, but
space still has to be set aside for it - but don't include the trash in the
checksum (usually 0xFFFF)...
*/
checksum = 0x00008000;
index = 0;
version_index = 0;
while (index < Driver.ModemCodeSize) {
(void) IssueEvent(ext,RM_Snd_Firmware_Data,ModemState);
if (
(index != (unsigned long)0xFFBE)
&&
(index != (unsigned long)0xFFBF)
)
checksum += Driver.ModemCodeImage[index];
/*
attempt to isolate the firmware version. version should be in form 'Vn.nnnan'. note
that we _could_ send another ati3 command to a representative modem to pick up the version
number after the load is complete, but that would take additional time...
also, note that though we've sent an ati3 command to at least one modem - so we have a
pretty good idea what the version is supposed to be based on the string we're expecting
on the response - we'll pretend that isn't applicable at this point to avoid dependencies
on the ati3 command...
whether that's a good idea or not remains to be seen. but the following processing seems
harmless at this time. if the form of the version changes, though, it might be annoying
to change the ati3 response string AND the following code to fit the new version form...
*/
if (
(Driver.ModemCodeImage[index] == VERSION_CHAR)
&&
(!gModemToggle)
&&
(!version_index)
) {
/*
only look for the version on the first modem board load, and if we haven't found the version yet,
see if the current character is a 'V'. if so, start the process of examining the following characters...
*/
cptr = &Driver.ModemCodeImage[index];
endptr = Driver.ModemCodeImage + Driver.ModemCodeSize;
while (version_index < sizeof(VersionString)) {
/*
are we about to go past the end of the file? if so, bail out...
*/
if (cptr >= endptr) {
version_index = 0;
break;
}
/*
check if this character looks ok...
*/
if (
(*cptr < '.')
||
(*cptr > 'Z')
) {
/*
not a printable-enough character. have we enough characters to assume this is the version string? if not,
give up, start search over. if we do, though, then we're done, bail out...
*/
if (version_index != (sizeof(VersionString) - 1))
version_index = 0;
break;
}
/*
printable character. if this is the third character in the string, though, it must be a dot. if not,
give up, start search over...
*/
if (
((*cptr == '.')
&&
(version_index != 2))
||
((*cptr != '.')
&&
(version_index == 2))
) {
version_index = 0;
break;
}
/*
printable character, save it away for later. this includes the leading 'V', incidentally...
*/
VersionString[version_index++] = *(cptr++);
VersionString[version_index] = 0;
}
}
index++;
}
ChecksumAscii((unsigned short *)&checksum);
/*
output one time messages. the version shouldn't change from modem board to modem board, and
neither should the computed checksum (though we do recompute it)...
*/
if (!gModemToggle) {
if (version_index) {
Eprintf("RocketModemII firmware %s-%s",VersionString,ChecksumString);
}
else if (ChecksumString[0]) {
Eprintf("RocketModemII checksum %s",ChecksumString);
}
}
/*
all done with files, release them...
*/
FreeModemFiles();
/*
spin while transmit FIFOs clear, then pause for response to arrive...
*/
ModemTxFIFOWait(ext);
/*
wait for successful load message from each modem...
*/
if (IssueEvent(ext,RM_Rcv_FirmwareLoaded_Response,ModemState) == 0) {
MyKdPrint(D_Init,("InitRocketModemII: No recognized responses to firmware load datastream\r"))
continue;
}
}
/*
pause for newly-loaded modems to settle down...
*/
time_stall(HALF_SECOND);
/*
unready ports, reset ports associated with any failing modems. bail out if done...
*/
if (IssueEvent(ext,RM_CleanUp,ModemState) == modem_count)
break;
} while (retry--);
++gModemToggle;
// Eprintf("RocketModemII init end"); // turn on for timing purposes...
#endif
}
#ifdef S_RK
/**********************************************************************
check response...
**********************************************************************/
int
IssueEvent(PSERIAL_DEVICE_EXTENSION ext,int (*modemfunc)(),MODEM_STATE *pModemState)
{
PSERIAL_DEVICE_EXTENSION portex;
int responding_modem_count;
/*
issue event to each modem...
*/
responding_modem_count = 0;
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
while ((void *)portex) {
if (pModemState->status != RMODEM_FAILED) {
responding_modem_count += (modemfunc)(pModemState);
}
++pModemState;
portex = portex->port_ext;
}
return(responding_modem_count);
}
/**********************************************************************
dynamic delay for transmit. waits only as long as necessary, but
doesn't get caught if a transmit fifo stalls (for whatever reason)...
**********************************************************************/
void
ModemTxFIFOWait(PSERIAL_DEVICE_EXTENSION ext)
{
PSERIAL_DEVICE_EXTENSION portex;
int index,activity;
int fifo_count[16]; // arbitrary, but reasonably safe, array size
int fifo_stall[16]; // ditto
/*
build baseline transmit fifo counts, init stall counts to zero...
*/
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
index = 0;
while ((void *)portex) {
fifo_count[index] = (int)TxFIFOStatus(portex);
fifo_stall[index] = 0;
++index;
portex = portex->port_ext;
}
/*
loop until all transmit fifos are empty, or we've given up on the stalled ones...
*/
do {
index = 0;
activity = 0;
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
while ((void *)portex) {
/*
check only those ports that indicate data in the transmit fifo, but then only as
long as they don't appear to be stalled...
*/
if (
((int)TxFIFOStatus(portex))
&&
(fifo_stall[index] < MAX_STALL)
) {
if (fifo_count[index] == (int)TxFIFOStatus(portex)) {
/*
pause for a non-moving transmit fifo, flag this fifo as suspect...
*/
fifo_stall[index]++;
ms_time_stall(1);
}
else {
/*
this particular transmit fifo count changed. pick up new value to monitor. unflag this
fifo as suspect...
*/
fifo_count[index] = (int)TxFIFOStatus(portex);
fifo_stall[index] = 0;
}
/*
whether they're stalled or not, flag fifos as still active. this goes on until
they're empty, or stall limit count is reached...
*/
++activity;
}
portex = portex->port_ext;
++index;
}
/*
still some (apparent) activity in transmit fifos? yep, loop some more...
*/
} while (activity);
}
/**********************************************************************
unready and reset modem...
**********************************************************************/
void
DownModem(MODEM_STATE *pModemState)
{
ModemUnReady(pModemState->portex);
ModemReset(pModemState->portex,1);
time_stall(Driver.MdmSettleTime);
ModemReset(pModemState->portex,0);
}
/**********************************************************************
reset all modems on this board at the same time...
**********************************************************************/
void
ModemResetAll(PSERIAL_DEVICE_EXTENSION ext)
{
PSERIAL_DEVICE_EXTENSION portex;
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
while ((void *)portex) {
ModemReset(portex,1);
portex = portex->port_ext;
}
time_stall(HALF_SECOND);
portex = (ext->port_pdo_ext) ? ext->port_pdo_ext : ext->port_ext;
while ((void *)portex) {
ModemReset(portex,0);
portex = portex->port_ext;
}
time_stall(ONE_SECOND);
}
/**********************************************************************
2 byte conversion to ascii...
**********************************************************************/
void
ChecksumAscii(unsigned short *valueptr)
{
int count,index;
unsigned short work;
ChecksumString[4] = 0;
index = 0;
count = 3;
do {
work = (*valueptr) & 0x7fff;
work >>= (count * 4);
work &= 0x000F;
if (work > 9)
work += '7';
else
work += '0';
ChecksumString[index++] = (unsigned char)work;
} while (count--);
}
#endif
/*********************************************************************************
*
* get_comdb_offsets
*
*********************************************************************************/
static int get_comdb_offsets( IN char *portLabel, OUT int *indx, OUT BYTE *mask )
{
char *pComLbl;
int i, portNum, portIndx;
BYTE portMask;
// Make sure a COMxx string is being passed in
ASSERT( portLabel );
ASSERT( indx );
ASSERT( mask );
if ( strlen( portLabel ) < 4 ) {
return 0;
}
if ( strncmp( portLabel, "COM", 3 ) ) {
return 0;
}
// A lot of checking, but if the wrong ComDB bit is cleared, the
// corresponding COM# may get reassigned although another device
// is using it.
pComLbl = portLabel + 3;
for ( i = 0; pComLbl[i]; i++ ) {
if (!our_isdigit( pComLbl[i] )) {
return 0;
}
}
// Convert the string to numeric, then translate into bit & byte
// offsets
portNum = getint( pComLbl, NULL );
portMask = (BYTE) (1 << ( (portNum - 1) % 8 ));
portIndx = (int) ((portNum - 1) >> 3);
MyKdPrint( D_Init, ("Mask value for COM%d is 0x%02x at byte index %d\n",
portNum, portMask, portIndx ) );
*indx = portIndx;
*mask = portMask;
return portNum;
}
/*********************************************************************************
*
* get_com_db
*
* Makes sure the bit in the \Registry\Machine\System\CurrentControlSet\Control\COM Name Arbiter
* for the specific port gets cleared on an uninstall. Ordinarily the PnP Manager
* does this automatically but old builds of W2000 don't nor do more recent builds
* under certain circumstances. If this bit isn't cleared the OS won't reuse the
* COM port number if the RocketPort is re-installed or another serial device is
* installed.
*
*********************************************************************************/
static char *szRMSCCComNameArbiter =
{ "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter" };
static char *szValueID = { "ComDB" };
int clear_com_db( char *szComport )
{
HANDLE key_handle = NULL;
// BYTE *buffer;
BYTE *data_ptr = NULL;
int i, stat, indx, port_num;
BYTE portMask;
USTR_40 ubuf; // Unicode key name
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
ULONG length;
// Get the COM #
indx = 0;
portMask = 0;
if ( szComport != NULL ) {
port_num = get_comdb_offsets( szComport, &indx, &portMask );
if ( port_num < 3 || port_num > 256 ) {
MyKdPrint( D_Error, ("Invalid COM port number from %d\n", szComport) );
return 1;
}
}
else {
MyKdPrint( D_Error, ("COM port parameter was NULL\n") );
return 1;
}
// Open the registry key
stat = our_open_key( &key_handle,
NULL,
szRMSCCComNameArbiter,
KEY_ALL_ACCESS );
if ( stat ) {
MyKdPrint(D_Error, ("Unable to find Com Port Arbiter key\n"));
return 1;
}
// convert our name to unicode
CToUStr((PUNICODE_STRING) &ubuf, // where unicode struct & string gets put
szValueID, // our c-string we wish to convert
sizeof(ubuf));
// The 2-pass ZwQueryValueKey approach ensures accurate buffer size allocation.
// Pass 1 with a NULL buffer parameter returns the length of the
// PKEY_VALUE_PARTIAL_INFORMATION structure. After allocating a buffer of
// this length, pass 2 reads the structure. The trick is to ignore any return
// code on pass 1 except STATUS_OBJECT_NAME_NOT_FOUND, i.e., the value doesn't
// exist.
// Determine the required size for the registry data buffer
stat = ZwQueryValueKey( key_handle,
(PUNICODE_STRING) &ubuf,
KeyValuePartialInformation,
NULL,
0,
&length);
if ( stat == STATUS_OBJECT_NAME_NOT_FOUND || length == 0L ) {
MyKdPrint(D_Error, ("Unable to find %s in specified key\n", szValueID));
ZwClose( key_handle );
return 1;
}
MyKdPrint(D_Init,
("Allocating PKEY_VALUE_PARTIAL_INFORMATION buffer: %d bytes\n", length));
// Make a buffer for the KEY_VALUE_PARTIAL_INFORMATION struct
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePool( PagedPool, length );
if ( KeyValueInfo == NULL ) {
MyKdPrint(D_Error, ("Unable to allocate PKEY_VALUE_PARTIAL_INFORMATION struct\n"))
ZwClose( key_handle );
return 1;
}
RtlZeroMemory( KeyValueInfo, length );
// Now get the actual data structure
stat = ZwQueryValueKey( key_handle,
(PUNICODE_STRING) &ubuf,
KeyValuePartialInformation,
KeyValueInfo,
length,
&length );
if ( !NT_SUCCESS(stat) || length == 0L ) {
MyKdPrint(D_Error, ("Unable to read PKEY_VALUE_PARTIAL_INFORMATION struct\n"));
ExFreePool( KeyValueInfo );
ZwClose( key_handle );
return 1;
}
length = KeyValueInfo->DataLength;
MyKdPrint(D_Init, ("Data buffer length is %d bytes\n", length));
if ( KeyValueInfo->Type != REG_BINARY ) {
MyKdPrint(D_Error, ("Unexpected registry type in PKEY_VALUE_PARTIAL_INFORMATION struct\n"));
ExFreePool( KeyValueInfo );
ZwClose( key_handle );
return 1;
}
data_ptr = (PCHAR)(&KeyValueInfo->Data[0]);
if ( data_ptr ) {
MyKdPrint(D_Init, ("ComDB byte %d is 0x%02x\n", indx, data_ptr[indx]));
if ( (data_ptr[indx] & portMask) != 0 ) {
MyKdPrint(D_Init,
("Clearing bit position 0x%02x in ComDB byte value 0x%02x\n",
portMask, data_ptr[indx]));
data_ptr[indx] &= ~portMask;
// Now we write the modified data back to the registry
stat = our_set_value( key_handle,
(char *)szValueID,
data_ptr,
length,
REG_BINARY);
if ( stat ) {
MyKdPrint(D_Error, ("Unable to write ComDB value\n"));
ExFreePool( KeyValueInfo );
ZwClose( key_handle );
return 1;
}
}
else {
// Previously cleared
MyKdPrint(D_Init,
("Bit position 0x%02x already cleared in ComDB byte value 0x%02x!\n",
portMask, data_ptr[indx]));
}
}
// cleanup
ExFreePool( KeyValueInfo );
ZwClose( key_handle );
return 0;
}