Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

3343 lines
91 KiB

/***************************************************************************
*
* File Name: xiptal.c
*
* Copyright (C) 1993-1996 Hewlett-Packard Company.
* All rights reserved.
*
* 11311 Chinden Blvd.
* Boise, Idaho 83714
*
* This is a part of the HP JetAdmin Printer Utility
*
* This source code is only intended as a supplement for support and
* localization of HP JetAdmin by 3rd party Operating System vendors.
* Modification of source code cannot be made without the express written
* consent of Hewlett-Packard.
*
*
* Description:
*
* Author: Name
*
*
* Modification history:
*
* date initials change description
*
* mm-dd-yy MJB
*
*
*
*
*
*
***************************************************************************/
/*
For a big picture clue, read this.
What's going on here? Why do we need this file after all?
Most transports (udp, tcp, ipx) have a large address space,
usually 16 bits, which is used by services and clients. The
service either has a well known address in the transport (like port
mapper on udp == 111) or the service is assigned a random
address in a range of addresses that are free to be used.
The client reaches the port mapper on the well known address
and then obtains from port mapper the address of the service
with which it desires to converse.
MLC is not like this. It has a rather restricted number of
channels that are usable by the printer. The printer allocates
buffers at boot time which are dedicated to particular MLC channels.
For practicality reasons, we cannot use every MLC channel available
in the address space.
To allow MLC to look to the host like other transports, we insert
"Yet Another Abstraction Layer" (YAAL) between the host program
and MLC. YAAL then uses a single MLC channel and multiplexes
a whole bunch of addresses over that channel. The protocol used
over that single MLC channel is XIP.
Any time the host program would call TAL, it calls YAAL instead.
YAAL looks at the transport type of the call and if it is MLC,
it wraps the communication with an XIP header and sends it over MLC.
The XIP header addresses the service in the printer using MLC as
just a way to get it there. If the host call is not over mlc then
YAAL routes the call unmodified to TAL for other transports.
Besides the multiplexing of multiple addresses over a single MLC
channel, YAAL performs another key function: It emulates the XIP
section in the JetDirect card for the printer so that the printer
talks the same protocol regardless of the transport.
This emulation gets a bit tricky since the printer has timeouts and
assumes that it's talking to a captive JetDirect card which lives to
please the printer. But the host has its own jobs to do and only
gets around to the printer when it want to. Sounds like your friend's
marriage doesn't it? Anyways, this presents a multitude
of problems which hopefully are covered by this code.
The printer's services present themselves to the world by binding
themselves to addresses on each of the supported transports on the
JetDirect card. We respond just like the JetDirect card would and allow
the services to bind to addresses in YAAL.
YAAL keeps track of the different services that are bound and allows
many host programs to converse with these services simultaneously.
The printer thinks it's talking XIP to a JetDirect card over MIO
and the host programs think they're talking over a real protocol
to a printer.
Everyone is fat, dumb, and happy!
The End
*/
#include "rpsyshdr.h"
#include <ctype.h>
#include "yaalext.h"
#define ubyte unsigned short int
#define uint16 unsigned int
/* #define LETS_DO_CALLBACKS */
typedef enum xiptal_bool_t { xtFALSE, xtTRUE } xiptal_bool_t;
#ifdef PNVMS_PLATFORM_HPUX
#define LPVOID void *
#define HCHANNEL LPVOID
#define HPERIPHERAL LPVOID
#define CALLBACK static int
#define DWORD unsigned long
#define WORD unsigned int
#define BYTE unsigned short int
#define LPBYTE BYTE *
#define LPDWORD DWORD *
#define HWND DWORD
#define UINT unsigned int
#define WPARAM WORD
#define LPARAM DWORD
#define RC_SUCCESS 0
#define RC_FAILURE 0xFFFF
#define MLC_MESSAGE_RECEIVED 0
#define MLC_DISCONNECT 1
#define PACKET_TYPE_CHANNEL 2
#define CHANNEL_CONNECTION 3
#define STREAM_TYPE_CHANNEL 4
typedef struct mop
{
BYTE bType;
WORD *lpwBufferSize;
void *hWnd;
WORD wMessage;
WORD wParam;
void *lpCallBackFunc;
BYTE *lpbStatusLevel;
} MLCOpenParams;
#else
#include <p1284mlc.h>
#include <yield.h>
#endif
#include "xipprtcl.h"
#ifdef DBM_DEBUG_EROOSKI
#include <trace.h>
#endif /* DBM_DEBUG_EROOSKI */
#define XipTalStatus DWORD
/*
converts a native word to a byte.
grabs either the msbyte or lsbyte out of the native number.
Input is a native word.
Output is a byte.
*/
#define LSBYTEOF(a) ( (a) & 0xff)
#define MSBYTEOF(a) (((a) >> 8) & 0xff)
/*
converts a byte to native lsbyte or msbyte
take the result of this and OR it with your native number
Input is a byte.
Output is a native word.
*/
#define TONATIVE_LSBYTE(a) ( (a) & 0xff)
#define TONATIVE_MSBYTE(a) (((a) << 8) & 0xff00) /* important it is this way */
#ifdef DBM_DEBUG_EROOSKI
void
DbmLog(LPTSTR String)
{
TCHAR buffster[50];
FILE *FilePointer = _tfopen(TEXT("dbmdbm.dbm"), TEXT("a"));
_stprintf(buffster, TEXT("%ld "), GetTickCount());
fwrite(buffster, sizeof(TCHAR), _tcslen(buffster), FilePointer);
fwrite(String, sizeof(TCHAR), _tcslen(String), FilePointer);
fflush(FilePointer);
fclose(FilePointer);
} /* DbmLog */
void
DataDump(LPTSTR TempPointer, DWORD Amount)
{
unsigned int loopster;
TCHAR buffster[100];
_stprintf(buffster, TEXT("Number of bytes = %d decimal\n"), Amount);
DbmLog(buffster);
for (loopster = 0; loopster < Amount; ++loopster)
{
if (_istprint(*TempPointer))
{
_stprintf(buffster, TEXT("byte %d = %x hex %c ascii\n"), loopster,
*TempPointer, *TempPointer);
}
else
{
_stprintf(buffster, TEXT("byte %d = %x hex\n"), loopster, *TempPointer);
}
DbmLog(buffster);
++TempPointer;
}
} /* DataDump */
#endif /* DBM_DEBUG_EROOSKI */
#define DB_CONN_TYPE_LENGTH 32
#define XIP_MLC_TIME_OUT 5000 /* 5 seconds */
/*
This next number (MLC_BUFFER_SIZE) is the max size that we
expect to see over mlc for any packet. The printer limits
its XIP datagrams to 576 bytes. Tack on an MLC header and
that's the biggest we should see.
Until we are sure of the number the printer will return, let's
make sure we're just a little bigger than it.
*/
#define MLC_BUFFER_SIZE (512 + 64 + 64)
#define XIP_ADDRESS_LENGTH 2 /* byte length of protocol address (port) */
#define MLC_TRANSPORT_NUM_LENGTH 2 /* byte length of transport number */
#define MLC_TRANSPORT_NUMBER 0x1234
#define MLC_ADDRESS_FAMILY 0x5678
#define XIP_PACKET_PH_LENGTH 4 /* magic bytes sent by printer with bind */
#define XIP_LOCAL_ADDRESS_BYTES 2 /* size of an mlc address over xip */
#define MLC_TSDU_BYTES 1500
/*
This number (TSDU_BYTES) is pretty doggon important!
What we want here is a number that is greater than
any number that the MIO card gives back to the printer.
XIP on the printer takes this number from all of its
peers (UDP and IPX on the MIO card and us over MLC).
It then takes the smallest of these numbers and uses
this result as the max size that it will use for any
of the peers. If we were to give a number less than
one of the other peers, we run the risk (it happened)
of causing someone talking over the MIO card to suddenly
have its packet size reduced and poof...no more communication.
XIP on the printer thinks that it can put together as
many x_data_out and x_data_out_continue packets as it
wants to in any size that it wants as long as the combined
length (without the xip headers) doesn't exceed this TSDU_BYTES
number. So if we say TSDU_BYTES = 1500, we can expect to
get several packet from the printer of around 576 (that's the
printer's XIP datagram size) until it gets up to 1500.
*/
/*
We need a block of reserved addresses that can be dynamically
assigned when the printer wants to bind to an address and doesn't
care what it is (zero length address length in x_bind_request).
Addresses will be assigned starting at this address and
incremented by one each time.
There can only be a finite number of addresses allocated before
this scheme eventually tromps on a well known address. That's life.
Well known addresses should be either less than this starting
number or much larger so that there is little
chance that a dynamically assigned address will get that big.
*/
#define START_OF_XIP_RESERVED_AREA 0xdeac
#define x_new_state_request_bytes 10
#define x_bind_reply_bytes 12
#define x_get_info_reply_bytes 16
#define MORE_DATA_FLAG 1 /* flag that says more data in next packet */
#define X_DATA_IN_HEADER_LENGTH 20
#define X_DATA_IN_CONTINUE_HEADER_LENGTH 12
/*
Each time we get data from a printer we queue it up
for an XipCOLAChannelStruct. We use this XipPendingDataStruct
to hang onto the packet while we wait for the host to come
and get it.
If this packet was a data_out and the more_data flag was
set, we set the boolean MoreToFollow to true otherwise it is false.
*/
typedef struct XipPendingDataStruct
{
char *ThePacketData;
DWORD ThePacketDataLength;
xiptal_bool_t MoreToFollow;
struct XipPendingDataStruct *Next;
} XipPendingDataStruct;
/*
For each cola channel that is openned using YAALOpenChannel,
we build an XipCOLAChannelStruct.
Each XipCOLAChannelStruct receives its own unique id which
no other one has. This number is used in all xip packets
that are sent from this channel. Any data that is received
via xip has this number as the destination address in the xip
header and so that data is routed and buffered for this channel.
The PendingData pointers let us accumulate data sent by the
print that the host has not yet read.
*/
#define COLAChannelHandleType int
typedef struct XipCOLAChannelStruct
{
COLAChannelHandleType COLAChannelHandle;
XipPendingDataStruct *PendingDataList;
struct XipCOLAChannelStruct *Next;
} XipCOLAChannelStruct;
typedef enum { XcUnbound, XcBound } XipServiceState;
/*
For each service that gets bound by an xip_bind_request
command from the printer, we build an XipServiceStruct.
The ServiceNumber is the address specified in the x_bind_request
or assigned in the x_bind_reply if the address length was zero.
This ServiceNumber is stored here in host machine
native integer representation. It's the port number in /tcp/udp
and it's the socket number in ipx and it's the channel number in TAL.
Several host programs may want to converse with a service and
so may perform several YAALOpenChannel() calls to the same
service. Every time that occurs, a new XipCOLAChannelStruct is
created and tacked onto the XipCOLAChannelList.
*/
typedef struct XipServiceStruct
{
XipServiceState ServiceState;
char XipPacketPH[XIP_PACKET_PH_LENGTH]; /* in b.e. format */
DWORD ServiceNumber;
XipCOLAChannelStruct *XipCOLAChannelList;
struct XipServiceStruct *Next;
} XipServiceStruct;
/*
For each cola peripheral handle, we build an XipPrinterStruct.
XipPrinterStruct is responsible for things that are mapped directly
to a printer...one-to-one.
These items include
- managing the mlc channel that is openned to let us
talk to xip on the printer;
- handling the boot up of xip (x_new_state_*)
- handling transport specific stuff (x_get_info_*)
- handling the list of bindings to services that the printer
says it has (x_bind_*).
Each XipPrinterStruct has a list of services that the printer
has bound using xip.
Also, there can be one and only one XipCOLAChannel that is in
the midst of an x_data_out with x_data_out_continue(s) and we
keep a pointer to it here.
*/
typedef struct XipPrinterStruct
{
HPERIPHERAL COLAPrinterHandle;
DWORD timeoutSec;
DWORD timeoutUSec;
DWORD retries;
DWORD MaxTransferSize;
HCHANNEL MlcChannelHandle;
XipCOLAChannelStruct *ContinuingXipCOLAChannel;
HCHANNEL ContinuingCOLAChannelHandle;
XipServiceStruct *XipServiceList;
struct XipPrinterStruct *Next;
} XipPrinterStruct;
/* ---------------- GLOBAL VARIABLES ------------------ */
/* ---------------- GLOBAL VARIABLES ------------------ */
/* ---------------- GLOBAL VARIABLES ------------------ */
/* ---------------- GLOBAL VARIABLES ------------------ */
static XipPrinterStruct *ListOPrinters = 0;
static xiptal_bool_t BeenInitialized = xtFALSE;
/*
MagicNumber is used to generate COLAChannelHandle's. We must
choose it so that it doesn't collide with
any memory address that would be allocated as a COLAChannelHandle
by other COLA pieces (presumably using malloc).
It also must fit in 16 bits because we pass it in the xip packet
as the source address.
I'm going to say that we won't have memory addresses allocated
down at 1 up to about 50. If I'm wrong and a COLAChannelHandle
comes in to us that exactly matches one of the numbers we've
allocated, we die.
*/
static long int MagicNumber = 1;
static DWORD NextPortNumber = START_OF_XIP_RESERVED_AREA;
#ifdef LETS_DO_CALLBACKS
/*
This yields the cpu in a windows 3.1 environment.
A key property of this code is that it lets any window's
messages be processed.
This could be good or bad.
We needed to make DaversYield because the COLA WindowsYield
was filtering out the messages for the invisible window function
that actually calls my callback function. That invisible window
was not being called until we finally gave up and returned an
error from WaitForServiceToBind. At that point,
finally our invisible window got its message
and we called the callback -- postmortem.
*/
void DaversYield(int iPreferences, HWND hFilterWnd)
{
MSG msg;
UINT uiPeekFlag;
uiPeekFlag = PM_REMOVE;
while (PeekMessage(&msg, NULL, 0, 0, uiPeekFlag))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} /* DaversYield */
#endif /* LETS_DO_CALLBACKS */
#define TimeoutType DWORD
#define InitTimeoutStuff(t) ((t) = GetTickCount())
#define TimedOut(t) ((GetTickCount() - (t)) > XIP_MLC_TIME_OUT)
#define NotTimedOutYet(t) ((GetTickCount() - (t)) < XIP_MLC_TIME_OUT)
/*
This is called before we do anything else with xip.
*/
static DWORD
XipTALInit(void)
{
BeenInitialized = 1;
ListOPrinters = 0;
return RC_SUCCESS;
} /* XipTALInit */
/*
Every time we get a call of YAALOpenChannel, this
function gets called to allocate a unique number that
we use both as the COLAChannelHandle and the xip source
address (16 bits).
*/
static int
AllocateCOLAChannelHandle(void)
{
return (int)++MagicNumber;
} /* AllocateCOLAChannelHandle */
/*
--------------------------------------------------------------
-- utilities -------------------------------------------------
--------------------------------------------------------------
*/
typedef enum SearchType
{
ByCOLAChannelHandlePlease,
ByServiceNumberPlease
} SearchType;
/*
This searches all the services and XipCOLAChannels
for a given printer and finds one that matches the
input specification.
You can choose to search for a specific service or
XipCOLAChannel.
If you search for service and it succeeds, only
**XipServicePointerPointer gets updated.
If you search for XipCOLAChannel, both
**XipServicePointerPointer and **XipCOLAChannelPointerPointer
get updated.
Returns RC_SUCCESS if it finds what you seek.
*/
static DWORD
LookupEither
(
XipPrinterStruct *PrinterPointer,
HCHANNEL COLAChannelHandle,
DWORD ServiceNumber,
SearchType SearchKey,
XipCOLAChannelStruct **XipCOLAChannelPointerPointer, /* may be 0 */
XipServiceStruct **XipServicePointerPointer /* may be 0 */
)
{
XipServiceStruct *XipServicePointer = 0;
XipCOLAChannelStruct *XipCOLAChannelPointer = 0;
if (PrinterPointer == 0)
{
return RC_FAILURE;
}
XipServicePointer = PrinterPointer->XipServiceList;
while (XipServicePointer != 0)
{
if ((SearchKey == ByServiceNumberPlease) &&
(XipServicePointer->ServiceNumber ==
ServiceNumber))
{
if (XipServicePointerPointer != 0)
{
*XipServicePointerPointer = XipServicePointer;
return RC_SUCCESS;
}
}
if (SearchKey == ByCOLAChannelHandlePlease)
{
/* search the list of XipCOLAChannels for a match */
XipCOLAChannelPointer = XipServicePointer->XipCOLAChannelList;
while (XipCOLAChannelPointer != 0)
{
if ((HCHANNEL)(XipCOLAChannelPointer->COLAChannelHandle) ==
COLAChannelHandle)
{
if (XipServicePointerPointer != 0)
{
*XipServicePointerPointer = XipServicePointer;
}
if (XipCOLAChannelPointerPointer != 0)
{
*XipCOLAChannelPointerPointer = XipCOLAChannelPointer;
return RC_SUCCESS;
}
}
XipCOLAChannelPointer = XipCOLAChannelPointer->Next;
} /* while COLAChannels still exist */
}
XipServicePointer = XipServicePointer->Next;
} /* while services still exist */
return RC_FAILURE;
} /* LookupEither */
/*
This searches our list of known printers and if
it finds a match, it returns RC_SUCCESS and gives
you a valid **PrinterPointerPointer.
*/
static DWORD
LookupCOLAPrinterHandle
(
HPERIPHERAL COLAPrinterHandle,
XipPrinterStruct **PrinterPointerPointer
)
{
*PrinterPointerPointer = ListOPrinters;
while (*PrinterPointerPointer != 0)
{
if ((*PrinterPointerPointer)->COLAPrinterHandle == COLAPrinterHandle)
{
return RC_SUCCESS;
}
*PrinterPointerPointer = (*PrinterPointerPointer)->Next;
}
return RC_FAILURE;
} /* LookupCOLAPrinterHandle */
/*
This finds a specific COLAChannelHandle by looking on all
of our known printers and all of our known services.
If it finds a match it returns RC_SUCCESS and gives you
valid
**PrinterPointerPointer,
**XipServicePointerPointer, and
**XipCOLAChannelPointerPointer.
*/
static DWORD
LookupCOLAChannelHandle
(
HCHANNEL COLAChannelHandle,
XipPrinterStruct **PrinterPointerPointer,
XipServiceStruct **XipServicePointerPointer,
XipCOLAChannelStruct **XipCOLAChannelPointerPointer
)
{
*PrinterPointerPointer = ListOPrinters;
while (*PrinterPointerPointer != 0)
{
if (RC_SUCCESS == LookupEither(*PrinterPointerPointer,
COLAChannelHandle, 0,
ByCOLAChannelHandlePlease,
XipCOLAChannelPointerPointer,
XipServicePointerPointer))
{
return RC_SUCCESS;
}
*PrinterPointerPointer = (*PrinterPointerPointer)->Next;
}
return RC_FAILURE;
} /* LookupCOLAChannelHandle */
/*
This finds a specific service by looking through all known
services on the specified printer.
If it finds a match it returns RC_SUCCESS and gives you
valid
**XipServicePointerPointer.
*/
static DWORD
LookupServiceNumber
(
XipPrinterStruct *PrinterPointer,
DWORD ServiceNumber,
XipServiceStruct **XipServicePointerPointer
)
{
return LookupEither(PrinterPointer,
0, ServiceNumber,
ByServiceNumberPlease,
0, XipServicePointerPointer);
} /* LookupServiceNumber */
/*
This gave us the ability to debug right as
we were launching off to TAL. We could dump
the data and see how the write went.
We get the added benefit that this retries until
either failure or we get all the data written
since it is possible that we get success with
zero length written.
*/
static DWORD
PRETALWriteChannel
(
HCHANNEL MlcChannelHandle,
LPVOID ReplyBuffer,
LPDWORD WriteSizePointer,
LPVOID lpOptions
)
{
DWORD Result;
DWORD AmountWritten;
#ifdef DBM_DEBUG_EROOSKI
TCHAR buffster[100];
_stprintf(buffster, TEXT("PRETALWriteChannel: data length = %ld decimal\n"),
*WriteSizePointer);
DbmLog(buffster);
#endif /* DBM_DEBUG_EROOSKI */
do
{
AmountWritten = *WriteSizePointer;
Result = TALWriteChannel(MlcChannelHandle,
ReplyBuffer,
&AmountWritten,
lpOptions);
} while ((Result == RC_SUCCESS) &&
(AmountWritten != *WriteSizePointer));
#ifdef DBM_DEBUG_EROOSKI
if (Result == RC_SUCCESS)
{
DbmLog(TEXT("PRETALWriteChannel: TALWriteChannel succeeded\n"));
TRACE0(TEXT("PRETALWriteChannel: TALWriteChannel succeeded\n"));
}
else
{
DbmLog(TEXT("PRETALWriteChannel: TALWriteChannel failed\n"));
TRACE0(TEXT("PRETALWriteChannel: TALWriteChannel failed\n"));
}
#endif /* DBM_DEBUG_EROOSKI */
return Result;
} /* PRETALWriteChannel */
/*
This copies an xip peripheral handle (PH)
from its current spot at SourceBytePointer,
to its new home at DestinationBytePointer.
This does no memory allocation nor freeing.
*/
static void
CopyPH
(
char *SourceBytePointer,
char *DestinationBytePointer
)
{
unsigned int index;
for (index = 0; index < XIP_PACKET_PH_LENGTH; ++index)
{
*DestinationBytePointer = *SourceBytePointer;
++DestinationBytePointer;
++SourceBytePointer;
}
} /* CopyPH */
/*
This formats and writes an x_new_state_request
packet to the printer over mlc.
It assumes that the mlc channel is already open
and that it is the correct time in the xip conversation
to give the x_new_state_request.
*/
static DWORD
IssueNewStateRequest
(
XipPrinterStruct *PrinterPointer
)
{
char ReplyBuffer[x_new_state_request_bytes] =
{
/* this bad boy is in big endian format */
MSBYTEOF(x_new_state_request), /* command code msbyte */
LSBYTEOF(x_new_state_request), /* " " lsbyte */
MSBYTEOF(x_version_1_1), /* xip version msbyte */
LSBYTEOF(x_version_1_1), /* " " lsbyte */
0, 1, /* one transport supported (mlc) */
MSBYTEOF(MLC_TRANSPORT_NUMBER),
LSBYTEOF(MLC_TRANSPORT_NUMBER),
0, 1 /* transport is up and capable of link level communication */
};
DWORD AmountInThisPacket = x_new_state_request_bytes;
return PRETALWriteChannel(PrinterPointer->MlcChannelHandle,
ReplyBuffer, &AmountInThisPacket, 0);
} /* IssueNewStateRequest */
/*
Looks on the queue of data that has been read from the
printer and stuffed on the list for each channel.
The memory for the xip packet has already been allocated
so the caller just gives us a pointer and we make it
point to the xip packet that it now owns.
The caller is responsible for calling GrabSomeFree() to
free the xip packet memory that this function returns.
Returns RC_SUCCESS if a packet was found and returned.
*/
static XipTalStatus
GrabSomeFromQueue
(
XipCOLAChannelStruct *XipCOLAChannelPointer,
char **YerDataPointerPointer,
DWORD *DataLengthPointer,
xiptal_bool_t *MoreToFollowPointer
)
{
XipPendingDataStruct *PendingPointer = 0;
*YerDataPointerPointer = 0;
*DataLengthPointer = 0;
*MoreToFollowPointer = xtFALSE;
if ((XipCOLAChannelPointer == 0) ||
(XipCOLAChannelPointer->PendingDataList == 0))
{
return RC_FAILURE;
}
/* cut the first one off the list */
PendingPointer = XipCOLAChannelPointer->PendingDataList;
XipCOLAChannelPointer->PendingDataList = PendingPointer->Next;
*YerDataPointerPointer = PendingPointer->ThePacketData;
*DataLengthPointer = PendingPointer->ThePacketDataLength;
*MoreToFollowPointer = PendingPointer->MoreToFollow;
free(PendingPointer);
return RC_SUCCESS;
} /* GrabSomeFromQueue */
/*
Frees xip packets allocated and given by GrabSomeFromQueue
*/
static void
GrabSomeFree
(
char *Buffer
)
{
free(Buffer);
} /* GrabSomeFree */
/*
This waits for an x_new_state_reply from the printer.
You will notice that we really don't care what the
printer says.
*/
static XipTalStatus
WaitForNewStateReply
(
XipPrinterStruct *PrinterPointer
)
{
return RC_SUCCESS;
} /* WaitForNewStateReply */
/*
Every time we need a new XipCOLAChannelStruct, you
call this function. This should be called by
YAALOpenChannel to get a new channel.
If successful, the new structure is tacked on the appropriate list for
the given printer and service.
Returns 0 if failure occurs (out of memory).
Returns a pointer to the newly created structure if successful.
*/
static XipCOLAChannelStruct *
AddAnXipCOLAChannel
(
XipPrinterStruct *PrinterPointer,
XipServiceStruct *XipServicePointer
)
{
XipCOLAChannelStruct *XipCOLAChannelPointer =
(XipCOLAChannelStruct *)calloc(1, sizeof(XipCOLAChannelStruct));
if ((PrinterPointer == 0) ||
(XipServicePointer == 0) ||
(XipCOLAChannelPointer == 0))
{
return 0;
}
XipCOLAChannelPointer->COLAChannelHandle = AllocateCOLAChannelHandle();
XipCOLAChannelPointer->PendingDataList = 0;
/* tack it onto the service's list of COLAChannels */
XipCOLAChannelPointer->Next = XipServicePointer->XipCOLAChannelList;
XipServicePointer->XipCOLAChannelList = XipCOLAChannelPointer;
return XipCOLAChannelPointer;
} /* AddAnXipCOLAChannel */
/*
- Removes the XipCOLAChannelPointer from the list,
- Frees the memory consumed by the XipCOLAChannelStruct
- Frees any pending data packets for this channel.
*/
static void
NukeThisXipCOLAChannel
(
XipPrinterStruct *PrinterPointer,
XipServiceStruct *XipServicePointer,
XipCOLAChannelStruct *XipCOLAChannelPointer
)
{
XipCOLAChannelStruct *BeforePointer;
XipPendingDataStruct *PendingDataPointer;
if ((PrinterPointer == 0) ||
(XipServicePointer == 0) ||
(XipCOLAChannelPointer == 0))
{
return;
}
if (XipServicePointer->XipCOLAChannelList == XipCOLAChannelPointer)
{
/* it is first in the list */
XipServicePointer->XipCOLAChannelList = XipCOLAChannelPointer->Next;
free(XipCOLAChannelPointer);
return;
}
/* search the list of XipCOLAChannels for a match */
BeforePointer = XipServicePointer->XipCOLAChannelList;
while (BeforePointer != 0)
{
if (BeforePointer->Next == XipCOLAChannelPointer)
{
/*
Found the one before our item of interest.
Cut it out of the list then free the item.
*/
BeforePointer->Next = BeforePointer->Next->Next;
/*
It's been cut out of the list.
Now free any pending data packets and the
structures we use to point to these packets.
*/
while (XipCOLAChannelPointer->PendingDataList != 0)
{
PendingDataPointer = XipCOLAChannelPointer->PendingDataList;
XipCOLAChannelPointer->PendingDataList =
XipCOLAChannelPointer->PendingDataList->Next;
if (PendingDataPointer->ThePacketData != 0)
{
free(PendingDataPointer->ThePacketData);
}
free(PendingDataPointer);
}
free(XipCOLAChannelPointer);
return;
}
BeforePointer = BeforePointer->Next;
} /* while COLAChannels still exist */
} /* NukeThisXipCOLAChannel */
/*
This creates a new structure for a new service for the given printer.
Each x_bind_request from a printer should result in a call to
this function if the service does not already exist. How can this
happen you ask? If the host does a call to YAALOpenChannel for
a service that has not yet been bound by the printer, a service
gets created using this function but the service is not ready
for communication because the x_bind_request/reply pair has not
yet been executed.
If unsuccessful (out of memory) returns 0.
If successful returns a pointer to the newly created structure
and tacks it on the appropriate list for the given printer.
The service is marked as "unbound". Call BindXipService
when you get an x_bind_request from the printer and know the
service's PH.
*/
static XipServiceStruct *
AddAnXipService
(
XipPrinterStruct *PrinterPointer,
DWORD ServiceNumber
)
{
int loopster;
XipServiceStruct *XipServicePointer =
(XipServiceStruct *)calloc(1, sizeof(XipServiceStruct));
if ((PrinterPointer == 0) ||
(XipServicePointer == 0))
{
return 0;
}
XipServicePointer->ServiceState = XcUnbound;
for (loopster = 0; loopster < XIP_PACKET_PH_LENGTH; ++loopster)
{
XipServicePointer->XipPacketPH[loopster] = 0;
}
XipServicePointer->ServiceNumber = ServiceNumber;
XipServicePointer->XipCOLAChannelList = 0;
/* tack it onto the front of printer's list of services */
XipServicePointer->Next = PrinterPointer->XipServiceList;
PrinterPointer->XipServiceList = XipServicePointer;
return XipServicePointer;
} /* AddAnXipService */
/*
- removes and frees all XipCOLAChannelStructs for this service,
- removes this XipServiceStruct from the list of services for
this printer,
- frees memory used by all these structures.
*/
static void
NukeThisXipService
(
XipPrinterStruct *PrinterPointer,
XipServiceStruct *XipServicePointer
)
{
XipServiceStruct *BeforePointer = 0;
if ((PrinterPointer == 0) ||
(XipServicePointer == 0))
{
return;
}
while (XipServicePointer->XipCOLAChannelList != 0)
{
NukeThisXipCOLAChannel(PrinterPointer, XipServicePointer,
XipServicePointer->XipCOLAChannelList);
}
if (XipServicePointer == PrinterPointer->XipServiceList)
{
/* first one in the list...cut it out */
PrinterPointer->XipServiceList = PrinterPointer->XipServiceList->Next;
free(XipServicePointer);
return;
}
/* not the first one in the list */
BeforePointer = PrinterPointer->XipServiceList;
while (BeforePointer != 0)
{
if (BeforePointer->Next == XipServicePointer)
{
/* found it...cut it out of the list */
BeforePointer->Next = BeforePointer->Next->Next;
free(XipServicePointer);
return;
}
BeforePointer = BeforePointer->Next;
}
} /* NukeThisXipService */
/*
A service was previously created but was not bound
by the x_bind_request/reply yet.
When the x_bind_request/reply pair get done, you call
this function to mark the service as bound and ready
for communication.
The NewPH is the one that is sent by the printer when
it performs the x_bind_request. We must remember this
for all further xip packets sent to this service from
the host.
*/
static void
BindXipService
(
XipServiceStruct *XipServicePointer,
char *NewPH
)
{
if (XipServicePointer != 0)
{
XipServicePointer->ServiceState = XcBound;
CopyPH(NewPH, XipServicePointer->XipPacketPH);
}
} /* BindXipService */
/*
This gets called if an x_unbind is received and marks
the service as unbound and "off the air".
I simply mark the service as unbound but don't nuke the structures.
This bound/unbound flag should be checked before we
allow the host to talk to the service. If the service
is unbound, it is officially "off the air" and we
should return failure for any communication between
the host and that service.
*/
static void
UnbindXipService
(
XipServiceStruct *XipServicePointer
)
{
if (XipServicePointer != 0)
{
XipServicePointer->ServiceState = XcBound;
}
} /* UnbindXipService */
/*
- Finds the COLAChannelHandle if it exists for any service
on any printer,
- Removes it from the list of COLAChannelHandles for that service,
- Frees any memory used by this COLAChannelHandle,
- Frees any xip packets that are queued on this channel waiting
for the host to come and get 'em.
*/
static void
NukeThisCOLAChannel
(
HCHANNEL COLAChannelHandle
)
{
XipTalStatus Result;
XipPrinterStruct *PrinterPointer;
XipServiceStruct *XipServicePointer;
XipCOLAChannelStruct *XipCOLAChannelPointer;
Result = LookupCOLAChannelHandle(COLAChannelHandle,
&PrinterPointer,
&XipServicePointer,
&XipCOLAChannelPointer);
if (Result != RC_SUCCESS)
{
return;
}
NukeThisXipCOLAChannel(PrinterPointer, XipServicePointer,
XipCOLAChannelPointer);
} /* NukeThisCOLAChannel */
/*
Each time we find a new COLAPrinterHandle come our way that
is on mlc, we create a structure to manage it. Call this
function to do that.
If unsuccessful (out of memory), returns 0.
If successful, it creates a new structure for you, ties it
in the correct lists, and gives you a pointer to it.
*/
static XipPrinterStruct *
AddAPrinter
(
HPERIPHERAL COLAPrinterHandle
)
{
XipPrinterStruct *PrinterPointer =
(XipPrinterStruct *)calloc(1, sizeof(XipPrinterStruct));
if (PrinterPointer != 0)
{
PrinterPointer->COLAPrinterHandle = COLAPrinterHandle;
PrinterPointer->timeoutSec = 0;
PrinterPointer->timeoutUSec = 0;
PrinterPointer->retries = 0;
PrinterPointer->MaxTransferSize = 0;
PrinterPointer->MlcChannelHandle = 0;
PrinterPointer->XipServiceList = 0;
PrinterPointer->ContinuingXipCOLAChannel = 0;
PrinterPointer->ContinuingCOLAChannelHandle = 0;
/* tack it onto the front of the list */
PrinterPointer->Next = ListOPrinters;
ListOPrinters = PrinterPointer;
}
return PrinterPointer;
} /* AddAPrinter */
/*
- closes the mlc channel for the printer,
- removes and frees all structures for this printer
(including any services and COLAChannels).
*/
static void
NukeThisPrinter
(
XipPrinterStruct *PrinterPointer
)
{
XipPrinterStruct *BeforePointer = 0;
if (PrinterPointer == 0)
{
return;
}
if (PrinterPointer->MlcChannelHandle != 0)
{
TALCloseChannel(PrinterPointer->MlcChannelHandle);
PrinterPointer->MlcChannelHandle = 0; /* our indicator of closure */
}
while (PrinterPointer->XipServiceList != 0)
{
NukeThisXipService(PrinterPointer,
PrinterPointer->XipServiceList);
}
if (ListOPrinters == PrinterPointer)
{
/* this is the first in the list */
ListOPrinters = ListOPrinters->Next;
free(PrinterPointer);
return;
}
/* not the first one in the list */
BeforePointer = ListOPrinters;
while (BeforePointer != 0)
{
if (BeforePointer->Next == PrinterPointer)
{
/* found it...cut it out of the list */
BeforePointer->Next = BeforePointer->Next->Next;
free(PrinterPointer);
return;
}
BeforePointer = BeforePointer->Next;
}
} /* NukeThisPrinter */
/*
If there are no open COLAChannelHandles for this printer
then close the mlc connection and get rid of all data
structures for this printer.
If there are some open then do nothing.
*/
static void
NukePrinterIfAllClosed
(
XipPrinterStruct *PrinterPointer
)
{
XipServiceStruct *XipServicePointer;
xiptal_bool_t EverFoundAnActive = xtFALSE;
if (PrinterPointer == 0)
{
return;
}
XipServicePointer = PrinterPointer->XipServiceList;
while ((XipServicePointer != 0) &&
(EverFoundAnActive == xtFALSE))
{
if (XipServicePointer->XipCOLAChannelList != 0)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("NukePrinterIfAllClosed: found an active connection\n"));
#endif /* DBM_DEBUG_EROOSKI */
EverFoundAnActive = xtTRUE;
}
XipServicePointer = XipServicePointer->Next;
}
if (EverFoundAnActive == xtFALSE)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("NukePrinterIfAllClosed: no active connections...nuking\n"));
#endif /* DBM_DEBUG_EROOSKI */
NukeThisPrinter(PrinterPointer);
}
} /* NukePrinterIfAllClosed */
/*
Call this to tack on some more data that has been received
from the printer for a particular XipCOLAChannel.
This uses the XipPacket and does NOT copy it so do not
free the memory that contains the xip packet passed in here.
*/
static XipTalStatus
AddSomePendingData
(
XipCOLAChannelStruct *XipCOLAChannelPointer,
char *XipPacketDataPointer,
DWORD XipPacketDataLength,
xiptal_bool_t MoreToFollow
)
{
XipPendingDataStruct *PendingPointer = 0;
#ifdef DBM_DEBUG_EROOSKI_____________NOT
TCHAR buffster[100];
if (MoreToFollow == xtTRUE)
DbmLog(TEXT("AddSomePendingData: MoreToFollow\n"));
else
DbmLog(TEXT("AddSomePendingData: no more\n"));
_stprintf(buffster, TEXT("AddSomePendingData: data length = %d decimal\n"),
XipPacketDataLength);
DbmLog(buffster);
#endif /* DBM_DEBUG_EROOSKI */
if (XipCOLAChannelPointer == 0)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("AddSomePendingData: zero COLAChannelPointer\n"));
#endif /* DBM_DEBUG_EROOSKI */
return RC_FAILURE;
}
PendingPointer = calloc(1, sizeof(XipPendingDataStruct));
if (PendingPointer == 0)
{
/* out o' memory...bad day */
return RC_FAILURE;
}
PendingPointer->ThePacketData = XipPacketDataPointer;
PendingPointer->ThePacketDataLength = XipPacketDataLength;
PendingPointer->MoreToFollow = MoreToFollow;
PendingPointer->Next = 0;
/*
Tack it on the list for this channel.
Note: it is important that the list be ordered first in first out.
*/
if (XipCOLAChannelPointer->PendingDataList == 0)
{
XipCOLAChannelPointer->PendingDataList = PendingPointer;
}
else
{
XipPendingDataStruct *HeadPendingPointer;
HeadPendingPointer = XipCOLAChannelPointer->PendingDataList;
while (HeadPendingPointer->Next != 0)
{
HeadPendingPointer = HeadPendingPointer->Next;
}
/* HeadPendingPointer points to the last one on the list */
HeadPendingPointer->Next = PendingPointer;
}
#ifdef DBM_DEBUG_EROOSKI_____________NOT
DbmLog(TEXT("AddSomePendingData: success\n"));
#endif /* DBM_DEBUG_EROOSKI */
return RC_SUCCESS;
} /* AddSomePendingData */
/*
Call this to be given a unique number for a service which
doesn't conflict with other service numbers.
Some services don't care what their number is. This is indicated
by a zero address length in the x_bind_request sent by the printer.
In this case, the service wants a number...any number.
This scheme used here is not air-tight but will work unless
someone really changes the well known addresses around.
*/
static DWORD
GenerateServiceNumber(void)
{
return ++NextPortNumber;
} /* GenerateServiceNumber */
/*
This checks the transport number in an xip packet for correctness.
Returns xtTRUE if this is a good transport number in the packet,
returns xtFALSE otherwise.
*/
static xiptal_bool_t
CheckTransportNum
(
char *InputPacketBytePointer
)
{
int Wordski;
xiptal_bool_t GoodTransport;
Wordski = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
Wordski |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (Wordski == MLC_TRANSPORT_NUMBER)
{
GoodTransport = xtTRUE;
}
else
{
GoodTransport = xtFALSE;
}
return GoodTransport;
} /* CheckTransportNum */
/*
This converts an xip address from its network big endian format
to host native integer format.
This assumes that the AddressBytes are in big endian format
and it will start at the first byte and keep reading bytes
until it has read AddressLength bytes from the AddressBytes.
*/
static DWORD
ConvertXipAddressToNative
(
char *AddressBytesPointer,
DWORD AddressLength
)
{
unsigned int index;
DWORD ServiceNumber = 0;
for (index = 0; index < AddressLength; ++index)
{
ServiceNumber = ServiceNumber << 8;
ServiceNumber |= TONATIVE_LSBYTE(*AddressBytesPointer);
++AddressBytesPointer;
}
return ServiceNumber;
} /* ConvertXipAddressToNative */
/*
Call this when an x_new_state_reply is received from the printer.
This looks at the packet and verifies its correctness.
It does not communicate with a response packet.
Returns RC_SUCCESS if all is well.
Returns RC_FAILURE if not.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_new_state_reply
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer
)
{
int Version;
InputPacketBytePointer += 2; /* skip the command bytes */
Version = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
Version |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (Version != x_version_1_1)
{
return RC_FAILURE;
}
return RC_SUCCESS;
} /* handle_x_new_state_reply */
/*
Call this when an x_bind_request packet is received from the printer.
This looks at the bind request, allocates a unique service number
if the address length in the request is zero, and responds with an
x_bind_reply packet.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_bind_request
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer
)
{
XipTalStatus Result;
XipServiceStruct *XipServicePointer;
DWORD ServiceNumber;
DWORD AddressLength;
DWORD AmountInThisPacket;
xiptal_bool_t GoodTransport;
char ReplyBuffer[x_bind_reply_bytes] =
{
MSBYTEOF(x_bind_reply), /* command code */
LSBYTEOF(x_bind_reply),
MSBYTEOF(x_no_error), /* no error ( so far ) */
LSBYTEOF(x_no_error),
0, 0, 0, 0, /* no PH ( so far ) */
MSBYTEOF(XIP_LOCAL_ADDRESS_BYTES), /* size of address */
LSBYTEOF(XIP_LOCAL_ADDRESS_BYTES),
0, 0 /* address not assigned yet */
};
InputPacketBytePointer += 2; /* skip the command bytes */
GoodTransport = CheckTransportNum(InputPacketBytePointer);
InputPacketBytePointer += 2;
CopyPH(InputPacketBytePointer, ReplyBuffer + 4);
InputPacketBytePointer += XIP_PACKET_PH_LENGTH;
AddressLength = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
AddressLength |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (AddressLength == 0)
{
ServiceNumber = GenerateServiceNumber();
}
else
{
ServiceNumber = ConvertXipAddressToNative(InputPacketBytePointer,
AddressLength);
InputPacketBytePointer += AddressLength;
}
/*
ServiceNumber is the native representation of the
address that is being bound.
Lookup this port and see if there is already an XIP channel
by that address. If so, use it and set its state to bound.
If not, create one and set its state to bound.
*/
Result = LookupServiceNumber(PrinterPointer,
ServiceNumber,
&XipServicePointer);
if (Result != RC_SUCCESS)
{
/* the channel does not exist so create one */
XipServicePointer = AddAnXipService(PrinterPointer, ServiceNumber);
if (XipServicePointer == 0)
{
/* out o' memory */
return RC_FAILURE;
}
}
/* The service is now bound...send it the PH that we just got */
BindXipService(XipServicePointer, ReplyBuffer + 4);
if (GoodTransport == xtFALSE)
{
*(ReplyBuffer + 2) = MSBYTEOF(x_invalid_transport);
*(ReplyBuffer + 3) = LSBYTEOF(x_invalid_transport);
}
/* fill in the final address */
*(ReplyBuffer + 10) = (char)MSBYTEOF(ServiceNumber);
*(ReplyBuffer + 11) = (char)LSBYTEOF(ServiceNumber);
AmountInThisPacket = x_bind_reply_bytes;
return PRETALWriteChannel(PrinterPointer->MlcChannelHandle,
ReplyBuffer, &AmountInThisPacket, 0);
} /* handle_x_bind_request */
/*
Call this when an x_unbind is received from the printer.
This looks up the service with the address specified in the
packet and then we mark that service as "unbound" which
is the same as "off the air".
We don't nuke the memory associated with the service because
there are host programs that still think it's alive.
This way, the host programs can find as they try to communicate
that the service is gone and they'll close down themselves
one by one as they see fit.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_unbind
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer
)
{
XipTalStatus Result;
XipServiceStruct *XipServicePointer;
DWORD ServiceNumber;
DWORD AddressLength;
xiptal_bool_t GoodTransport;
InputPacketBytePointer += 2; /* skip the command bytes */
GoodTransport = CheckTransportNum(InputPacketBytePointer);
InputPacketBytePointer += 2;
if (GoodTransport == xtFALSE)
{
/* not for our transport so we are done */
return RC_SUCCESS;
}
AddressLength = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
AddressLength |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (AddressLength == 0)
{
return RC_SUCCESS;
}
else
{
ServiceNumber = ConvertXipAddressToNative(InputPacketBytePointer,
AddressLength);
InputPacketBytePointer += AddressLength;
}
/*
ServiceNumber is the native representation of the
address that is being bound.
Lookup this service and see if there is already an XIP service
by that address. If so, use it and set its state to unbound.
*/
Result = LookupServiceNumber(PrinterPointer,
ServiceNumber,
&XipServicePointer);
if (Result == RC_SUCCESS)
{
/* the channel exists so mark it as unbound */
UnbindXipService(XipServicePointer);
}
return RC_SUCCESS;
} /* handle_x_unbind */
/*
Call this when an x_get_info_request packet is receive from the printer.
This analyzes the packet and then responds to the printer with
an x_get_info_reply packet.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_get_info_request
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer
)
{
DWORD AmountInThisPacket;
xiptal_bool_t GoodTransport;
char ReplyBuffer[x_get_info_reply_bytes] =
{
/* this bad boy is in big endian format */
MSBYTEOF(x_get_info_reply), /* command code msbyte */
LSBYTEOF(x_get_info_reply), /* " " lsbyte */
MSBYTEOF(x_no_error), /* no error ( so far ) */
LSBYTEOF(x_no_error), /* no error ( so far ) */
MSBYTEOF(MLC_TRANSPORT_NUMBER),
LSBYTEOF(MLC_TRANSPORT_NUMBER),
0, 0, 0, 0, /* no PH ( so far ) */
MSBYTEOF(XIP_LOCAL_ADDRESS_BYTES),
LSBYTEOF(XIP_LOCAL_ADDRESS_BYTES),
MSBYTEOF(MLC_TSDU_BYTES),
LSBYTEOF(MLC_TSDU_BYTES),
MSBYTEOF(MLC_ADDRESS_FAMILY),
LSBYTEOF(MLC_ADDRESS_FAMILY)
};
InputPacketBytePointer += 2; /* skip the command bytes */
GoodTransport = CheckTransportNum(InputPacketBytePointer);
InputPacketBytePointer += 2;
CopyPH(InputPacketBytePointer, ReplyBuffer + 6);
InputPacketBytePointer += XIP_PACKET_PH_LENGTH;
if (GoodTransport == xtFALSE)
{
*(ReplyBuffer + 2) = MSBYTEOF(x_invalid_transport);
*(ReplyBuffer + 3) = LSBYTEOF(x_invalid_transport);
}
AmountInThisPacket = x_get_info_reply_bytes;
return PRETALWriteChannel(PrinterPointer->MlcChannelHandle,
ReplyBuffer, &AmountInThisPacket, 0);
} /* handle_x_get_info_request */
/*
This copies the data from DataPointer to a new piece of memory
that this function allocates.
DataPointer should point to the first byte of data to be copied.
DataLength should be the number of bytes to be copied (and allocated).
MoreToFollow should be set to xtTRUE if there is a x_data_out_continue
to follow this one.
*/
static XipTalStatus
handle_x_data_guts
(
XipCOLAChannelStruct *XipCOLAChannelPointer,
char *DataPointer,
DWORD DataLength,
xiptal_bool_t MoreToFollow
)
{
DWORD loopster;
char *NewPacketPointer;
NewPacketPointer = calloc(1, (size_t)DataLength);
if (NewPacketPointer == 0)
{
return RC_FAILURE;
}
/* copy original packet to its new home */
for (loopster = 0; loopster < DataLength; ++loopster)
{
NewPacketPointer[loopster] = DataPointer[loopster];
}
/*
We now know the source of the data
so queue that data on the list for that port.
*/
return AddSomePendingData(XipCOLAChannelPointer,
NewPacketPointer,
DataLength,
MoreToFollow);
} /* handle_x_data_guts */
/*
Call this when an x_data_out packet is received from the printer.
This looks at the packet, finds the service and XipCOLAChannel
that this packet is addressed to, and tacks this packet on a
list off pending packets for that XipCOLAChannel.
If the XipCOLAChannel is non-existent then just ignore it.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_data_out
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer,
DWORD AmountRead
)
{
DWORD Result;
XipServiceStruct *XipServicePointer;
DWORD ServiceNumber;
XipCOLAChannelStruct *XipCOLAChannelPointer;
HCHANNEL COLAChannelHandle;
xiptal_bool_t GoodTransport;
long int TempLong = 0;
XipPrinterStruct *BogusPrinterPointer;
XipServiceStruct *BogusXipServicePointer;
xiptal_bool_t MoreToFollow;
#ifdef DBM_DEBUG_EROOSKI
char *OrigPacket = InputPacketBytePointer;
#endif /* DBM_DEBUG_EROOSKI */
InputPacketBytePointer += 2; /* skip the command bytes */
GoodTransport = CheckTransportNum(InputPacketBytePointer);
InputPacketBytePointer += 2; /* advance to flags */
if (GoodTransport == xtFALSE)
{
/* not for us so trash it */
return RC_SUCCESS;
}
/*
Get the flag and set our boolean accordingly.
*/
Result = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
Result |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (Result == MORE_DATA_FLAG)
{
MoreToFollow = xtTRUE;
}
else
{
MoreToFollow = xtFALSE;
}
InputPacketBytePointer += 2; /* skip the address length */
/* now get the address */
ServiceNumber = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
ServiceNumber |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
Result = LookupServiceNumber(PrinterPointer,
ServiceNumber,
&XipServicePointer);
if (Result != RC_SUCCESS)
{
return RC_FAILURE;
}
/*
determine the XipCOLAChannel which should
receive this packet by looking at the destination address
*/
InputPacketBytePointer += 2; /* skip the address length */
/* now get the address */
TempLong = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
TempLong |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
COLAChannelHandle = (HCHANNEL)TempLong;
Result = LookupCOLAChannelHandle(COLAChannelHandle,
&BogusPrinterPointer,
&BogusXipServicePointer,
&XipCOLAChannelPointer);
if ((Result != RC_SUCCESS) ||
(BogusPrinterPointer != PrinterPointer) ||
(BogusXipServicePointer != XipServicePointer))
{
return RC_FAILURE;
}
/* now get the data length */
TempLong = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
TempLong |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
/* InputPacketBytePointer is now pointing to the first data byte */
Result = handle_x_data_guts(XipCOLAChannelPointer, InputPacketBytePointer,
TempLong, MoreToFollow);
if (MoreToFollow == xtTRUE)
{
PrinterPointer->ContinuingXipCOLAChannel = XipCOLAChannelPointer;
PrinterPointer->ContinuingCOLAChannelHandle = COLAChannelHandle;
}
else
{
PrinterPointer->ContinuingXipCOLAChannel = 0;
PrinterPointer->ContinuingCOLAChannelHandle = 0;
}
return Result;
} /* handle_x_data_out */
/*
Call this when an x_data_out_continue packet is received from the printer.
This uses the
PrinterPointer->ContinuingXipCOLAChannel and
PrinterPointer->ContinuingCOLAChannelHandle
fields to figure out the service and XipCOLAChannel
that this packet is addressed to, and tacks this packet on a
list off pending packets for that XipCOLAChannel.
If the XipCOLAChannel is non-existent then just ignore it.
Note: the input packet memory belongs to the caller.
Do NOT free it...that's left to the caller.
*/
static XipTalStatus
handle_x_data_out_continue
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer,
DWORD AmountRead
)
{
DWORD Result;
XipCOLAChannelStruct *XipCOLAChannelPointer;
xiptal_bool_t GoodTransport;
long int TempLong = 0;
XipPrinterStruct *BogusPrinterPointer;
XipServiceStruct *BogusXipServicePointer;
xiptal_bool_t MoreToFollow;
#ifdef DBM_DEBUG_EROOSKI__________________________NOT
DbmLog(TEXT("data_out_continue: \n"));
DataDump(InputPacketBytePointer, 10);
#endif /* DBM_DEBUG_EROOSKI */
if ((PrinterPointer == 0) ||
(PrinterPointer->ContinuingXipCOLAChannel == 0))
{
/*
Either we are brain-dead or
we didn't expect to be in the x_data_out_continue case.
*/
return RC_FAILURE;
}
InputPacketBytePointer += 2; /* skip the command bytes */
GoodTransport = CheckTransportNum(InputPacketBytePointer);
InputPacketBytePointer += 2; /* advance to flags */
if (GoodTransport == xtFALSE)
{
/* not for us so trash it */
return RC_SUCCESS;
}
/*
Get the flag and set our boolean accordingly.
*/
Result = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
Result |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
if (Result == MORE_DATA_FLAG)
{
MoreToFollow = xtTRUE;
}
else
{
MoreToFollow = xtFALSE;
}
Result = LookupCOLAChannelHandle(PrinterPointer->ContinuingCOLAChannelHandle,
&BogusPrinterPointer,
&BogusXipServicePointer,
&XipCOLAChannelPointer);
if ((Result != RC_SUCCESS) ||
(BogusPrinterPointer != PrinterPointer) ||
(XipCOLAChannelPointer != PrinterPointer->ContinuingXipCOLAChannel))
{
return RC_FAILURE;
}
/* now get the data length */
TempLong = TONATIVE_MSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
TempLong |= TONATIVE_LSBYTE(*InputPacketBytePointer);
++InputPacketBytePointer;
/* InputPacketBytePointer is now pointing to the first data byte */
Result = handle_x_data_guts(XipCOLAChannelPointer, InputPacketBytePointer,
TempLong, MoreToFollow);
if (MoreToFollow != xtTRUE)
{
PrinterPointer->ContinuingXipCOLAChannel = 0;
PrinterPointer->ContinuingCOLAChannelHandle = 0;
}
return Result;
} /* handle_x_data_out_continue */
/*
An xip packet has been sent from a printer.
The memory that contains the xip packet belongs to the caller.
We will analyze the contents of the packet.
If the packet can be acted upon immediately (like x_bind_request,
get_info_request, etc) we will respond to the printer immediately.
If the packet is an x_data_out packet, we will COPY the packet to
another buffer and attach it to a list of queued packets for the
addressed destination COLAChannel.
*/
static XipTalStatus
HandleAnXipPacket
(
XipPrinterStruct *PrinterPointer,
char *InputPacketBytePointer,
DWORD AmountRead
)
{
XipTalStatus Result;
int Wordski;
/*
See if the command is correct.
It should be a new state reply.
*/
Wordski = TONATIVE_MSBYTE(*InputPacketBytePointer);
Wordski |= TONATIVE_LSBYTE(*(InputPacketBytePointer + 1));
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT(">>>> xip packet %d from printer length = %d decimal\n"),
Wordski, AmountRead);
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
switch (Wordski)
{
case x_new_state_reply:
Result = handle_x_new_state_reply(PrinterPointer,
InputPacketBytePointer);
break;
case x_bind_request:
Result = handle_x_bind_request(PrinterPointer,
InputPacketBytePointer);
break;
case x_get_info_request:
Result = handle_x_get_info_request(PrinterPointer,
InputPacketBytePointer);
break;
case x_unbind:
Result = handle_x_unbind(PrinterPointer,
InputPacketBytePointer);
break;
case x_data_out:
Result = handle_x_data_out(PrinterPointer,
InputPacketBytePointer,
AmountRead);
break;
case x_data_out_continue:
Result = handle_x_data_out_continue(PrinterPointer,
InputPacketBytePointer,
AmountRead);
break;
default:
Result = RC_FAILURE;
break;
} /* switch (Wordski) */
return Result;
} /* HandleAnXipPacket */
/*
Does one TALReadChannel and if that fails or if the read is
zero size then return failure.
If the read succeeds and the size is non-zero then act
upon the received xip packet. This action may include writing back to
the printer a response xip packet.
Doing a TALPollChannels is the caller's responsibility.
Note: the Buffer belongs to the caller. The caller allocates it
and the caller frees it.
BufferSize is the size of the buffer in bytes.
*/
static XipTalStatus
ReadAndHandleAnXipPacket
(
XipPrinterStruct *PrinterPointer,
char *Buffer,
DWORD BufferSize
)
{
XipTalStatus Result;
DWORD AmountRead = 0;
/*
Read once from the mlc channel and handle
whatever comes our way.
Handle only one packet at a time.
*/
if ((PrinterPointer == 0) ||
(PrinterPointer->MlcChannelHandle == 0))
{
return RC_FAILURE;
}
AmountRead = BufferSize;
Result = TALReadChannel(PrinterPointer->MlcChannelHandle,
Buffer,
&AmountRead,
0);
if ((Result == RC_SUCCESS) && (AmountRead != 0))
{
Result = HandleAnXipPacket(PrinterPointer, Buffer, AmountRead);
}
else
{
Result = RC_FAILURE;
}
return Result;
} /* ReadAndHandleAnXipPacket */
/*
For all printer that we know about, attempt to read from
each of them. If any read is successful then respond with
a response xip packet if appropriate and then attempt another
read.
Loop until no read on any printer succeeds.
*/
static void
HandleQueuedXipPackets(void)
{
XipPrinterStruct *PrinterPointer;
DWORD Result;
char *Buffer;
PrinterPointer = ListOPrinters;
Buffer = calloc(1, MLC_BUFFER_SIZE);
if (Buffer == 0)
{
return;
}
while (PrinterPointer != 0)
{
do
{
Result = ReadAndHandleAnXipPacket(PrinterPointer,
Buffer,
MLC_BUFFER_SIZE);
} while (Result == RC_SUCCESS);
PrinterPointer = PrinterPointer->Next;
} /* for all printers */
free(Buffer);
} /* HandleQueuedXipPackets */
#ifdef LETS_DO_CALLBACKS
/*
This is the callback that is given to MLC when
we initially do the MLC open channel.
When the MLC watchdog gets action, it will call
this entry point.
*/
DLL_EXPORT (LONG) CALLBACK
XipTALPrinterEntryPoint
(
HWND Param1,
UINT ReasonCalled,
WPARAM XipChannelDisguisedAsAWord,
LPARAM Param4
)
{
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
DbmLog(TEXT("***** Actually in callback!!!!!!\n"));
TRACE0(TEXT("***** Actually in callback!!!!!!\n"));
_stprintf(buffster, TEXT("callback: Param1 = %d decimal\n"), Param1);
DbmLog(buffster);
_stprintf(buffster, TEXT("callback: ReasonCalled = %d decimal\n"), ReasonCalled);
DbmLog(buffster);
_stprintf(buffster, TEXT("callback: XipChannelDisguisedAsAWord = %d decimal\n"), XipChannelDisguisedAsAWord);
DbmLog(buffster);
_stprintf(buffster, TEXT("callback: Param4 = %d decimal\n"), Param4);
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
HandleQueuedXipPackets();
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("***** Leaving Callback\n"));
TRACE0(TEXT("***** Leaving Callback\n"));
#endif /* DBM_DEBUG_EROOSKI */
return 0;
} /* XipTALPrinterEntryPoint */
#endif /* LETS_DO_CALLBACKS */
/*
This opens the lone TAL mlc channel for the printer.
There is one mlc channel per printer.
*/
static DWORD
OpenMlcChannel
(
XipPrinterStruct *PrinterPointer
)
{
MLCOpenParams MlcOpenParams;
WORD wBufferSize = MLC_BUFFER_SIZE;
BYTE bStatusLevel = 0xFF; /* no clue what this status is! */
DWORD Result;
MlcOpenParams.iMLCResult = 0; /* no clue dbm DBM */
MlcOpenParams.bType = STREAM_TYPE_CHANNEL;
MlcOpenParams.lpwBufferSize = &wBufferSize;
MlcOpenParams.hWnd = 0; /* important! needs to be null! */
MlcOpenParams.wMessage = 0; /* think this goes in bit bucket */
/*
The only parameter that we can pass to the callback
is wParam. It gets passed as parameter #3 when a
packet comes in from mlc.
Let's make it a good one since it's our only one!
I want it to be the XipChannelHandle if I can because
this can be used to find anything.
*/
MlcOpenParams.wParam = 0;
#ifdef LETS_DO_CALLBACKS
MlcOpenParams.lpCallBackFunc = XipTALPrinterEntryPoint;
#else
MlcOpenParams.lpCallBackFunc = 0;
#endif
MlcOpenParams.lpbStatusLevel = &bStatusLevel; /* no clue */
Result = TALOpenChannel(PrinterPointer->COLAPrinterHandle,
10,
CHANNEL_CONNECTION,
&MlcOpenParams,
&(PrinterPointer->MlcChannelHandle));
if ((Result != RC_SUCCESS) || (PrinterPointer->MlcChannelHandle == 0))
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("TALOpenChannel failed\n"));
#endif /* DBM_DEBUG_EROOSKI */
PrinterPointer->MlcChannelHandle = 0; /* important that this be zero */
return RC_FAILURE;
}
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("TALOpenChannel: returned size = %d decimal\n"),
wBufferSize);
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
PrinterPointer->MaxTransferSize = wBufferSize;
return RC_SUCCESS;
} /* OpenMlcChannel */
/*
Check to see if the service is bound and ready for communication.
If so, return success immediately.
If not, spin on TALReadChannel until the service binds (by way
of an x_bind_request/reply pair) or the time expires.
Return RC_SUCCESS if the service is bound or gets bound within
the allowed time.
Return RC_FAILURE otherwise.
*/
static XipTalStatus
WaitForServiceToBind
(
XipPrinterStruct *PrinterPointer,
XipServiceStruct *XipServicePointer
)
{
XipTalStatus Result;
TimeoutType TimeoutValue;
InitTimeoutStuff(TimeoutValue);
Result = RC_SUCCESS;
while ((XipServicePointer->ServiceState == XcUnbound) &&
(NotTimedOutYet(TimeoutValue)))
{
TALPollChannels(PrinterPointer->MlcChannelHandle);
#ifdef LETS_DO_CALLBACKS
DaversYield(YIELD_NOW, NULL);
#else
WindowsYield(YIELD_NOW, NULL);
HandleQueuedXipPackets();
#endif
} /* while ((XipServicePointer->ServiceState == XcUnbound) ... */
if (XipServicePointer->ServiceState == XcUnbound)
{
Result = RC_FAILURE; /* timed out */
}
return Result;
} /* WaitForServiceToBind */
DLL_EXPORT(DWORD) CALLING_CONVEN
YAALTransportMaxPacket(HPERIPHERAL hPeripheral)
{
if (MLC_SUPPORTED(hPeripheral))
{
/*
For mlc let's get the size up there for performance.
The printer (elkhorn) has only 2 buffers for xip's mlc channel.
Because of this, if we exceed 2 xip writes in a row, we
incur all kinds if big time performance hits.
Therefore, I'm keeping this return value smaller than it
could be. It could be up around 1280 or more but that
doesn't work due to the 2 buffer stuff.
I'm making it (576 * 2 - some stuff) because 576 is the
packet size that the printer returns in the TALOpenChannel
call for mlc.
*/
return (576 * 2) -
X_DATA_IN_HEADER_LENGTH - X_DATA_IN_CONTINUE_HEADER_LENGTH;
}
/*
This is very important!
Old IPX systems cannot handle large packet sizes.
The max set by COLA TAL is DAT_SIZ bytes for this reason.
If we exceed TAL's limit, we get data corruption on reads!
*/
return DAT_SIZ;
} /* YAALTransportMaxPacket */
/*
When a host program wishes to communicate with a service on a
printer, it calls this function.
The handle that is received is unique. No other host program
will receive this handle even if the same service is communicating
with other host programs simultaneously.
In order to keep from really goofing things up, it is required
to close this channel using YAALCloseChannel when the channel
is no longer required.
Call this with printers that talk mlc or any other transport.
This checks the transport type and will route the call to
the appropriate transport.
Calling this function when the transport is mlc will cause
multiple xip packets to be sent and received if this is the
first connection over mlc.
*/
DLL_EXPORT(DWORD) CALLING_CONVEN
YAALOpenChannel
(
HPERIPHERAL COLAPrinterHandle,
DWORD ServiceNumber,
DWORD COLAChannelType,
LPVOID OptionsPointer,
LPHCHANNEL COLAChannelHandlePointer
)
{
DWORD Result;
XipPrinterStruct *PrinterPointer;
XipServiceStruct *XipServicePointer;
XipCOLAChannelStruct *XipCOLAChannelPointer;
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("====YAALOpenChannel\n"));
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
if (!MLC_SUPPORTED(COLAPrinterHandle))
{
return TALOpenChannel(COLAPrinterHandle,
ServiceNumber,
COLAChannelType,
OptionsPointer,
COLAChannelHandlePointer);
}
/*
We know we're working with an MLC connection
*/
if (!BeenInitialized)
{
Result = XipTALInit();
if (Result != RC_SUCCESS)
{
return Result;
}
}
/*
Before we do anything else, lets grab all the queued
xip packets and act upon them.
*/
HandleQueuedXipPackets();
Result = LookupCOLAPrinterHandle(COLAPrinterHandle,
&PrinterPointer);
if (Result != RC_SUCCESS)
{
PrinterPointer = AddAPrinter(COLAPrinterHandle);
if (PrinterPointer == 0)
{
/* out of memory...ughh */
return RC_FAILURE;
}
Result = OpenMlcChannel(PrinterPointer);
if (Result != RC_SUCCESS)
{
return Result;
}
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
/* From here on out we close the channel on error */
Result = IssueNewStateRequest(PrinterPointer);
if (Result != RC_SUCCESS)
{
NukeThisPrinter(PrinterPointer);
return Result;
}
Result = WaitForNewStateReply(PrinterPointer);
if (Result != RC_SUCCESS)
{
NukeThisPrinter(PrinterPointer);
return Result;
}
} /* COLAPrinterHandle didn't exist */
/* COLAPrinterHandle now exists...check for the service */
Result = LookupServiceNumber(PrinterPointer,
ServiceNumber,
&XipServicePointer);
if (Result != RC_SUCCESS)
{
XipServicePointer = AddAnXipService(PrinterPointer,
ServiceNumber);
if (XipServicePointer == 0)
{
/* out o' memory */
return RC_FAILURE;
}
}
/* Service exists...wait for binding */
Result = WaitForServiceToBind(PrinterPointer,
XipServicePointer);
if (Result != RC_SUCCESS)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("WaitForBinding failed\n"));
DbmLog(TEXT("Leaving YAALOpenChannel: failure\n"));
DbmLog(TEXT("===================================\n"));
#endif /* DBM_DEBUG_EROOSKI */
return Result;
}
/* Service is bound...let's give 'em a handle to it */
XipCOLAChannelPointer = AddAnXipCOLAChannel(PrinterPointer,
XipServicePointer);
if (XipCOLAChannelPointer == 0)
{
/* out o' memory */
return RC_FAILURE;
}
*COLAChannelHandlePointer = (HCHANNEL)
XipCOLAChannelPointer->COLAChannelHandle;
return RC_SUCCESS;
} /* YAALOpenChannel */
/*
Call this to close down a channel that was openned with YAALOpenChannel.
Call this for all transports. The transport type is checked and
the call is routed to the appropriate TAL transport.
*/
DLL_EXPORT(DWORD) CALLING_CONVEN
YAALCloseChannel
(
HCHANNEL COLAChannelHandle
)
{
DWORD Result;
XipPrinterStruct *PrinterPointer;
XipServiceStruct *XipServicePointer;
XipCOLAChannelStruct *XipCOLAChannelPointer;
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("====YAALCloseChannel\n"));
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
if (!BeenInitialized)
{
Result = XipTALInit();
if (Result != RC_SUCCESS)
{
return Result;
}
}
/*
Before we do anything else, lets grab all the queued
xip packets and act upon them.
*/
HandleQueuedXipPackets();
Result = LookupCOLAChannelHandle(COLAChannelHandle,
&PrinterPointer,
&XipServicePointer,
&XipCOLAChannelPointer);
if (Result == RC_SUCCESS)
{
NukeThisCOLAChannel(COLAChannelHandle);
}
else
{
/* it's not one of ours so hopefully it's one of theirs */
return TALCloseChannel(COLAChannelHandle);
}
return RC_SUCCESS;
} /* YAALCloseChannel */
/*
Call this when the applet is unloading and we are closing up shop.
If there are no active channels open for any service on the printer,
the mlc connection is closed.
If you are not talking mlc then the call is routed to the
appropriate TAL transport.
*/
DLL_EXPORT(void) CALLING_CONVEN
YAALNukePrinter
(
HPERIPHERAL COLAPrinterHandle
)
{
XipPrinterStruct *PrinterPointer;
if (!BeenInitialized)
{
return; /* now that's what ya call nuked! */
}
/*
Before we do anything else, lets grab all the queued
xip packets and act upon them.
*/
HandleQueuedXipPackets();
if (RC_SUCCESS == LookupCOLAPrinterHandle(COLAPrinterHandle,
&PrinterPointer))
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("YAALNukePrinter: off for NukePrinterIfAllClosed\n"));
#endif /* DBM_DEBUG_EROOSKI */
NukePrinterIfAllClosed(PrinterPointer);
}
} /* YAALNukePrinter */
/*
Call this to read any data that has been sent by the printer
to you.
*/
DLL_EXPORT(DWORD) CALLING_CONVEN
YAALReadChannel
(
HCHANNEL COLAChannelHandle,
LPVOID VoidBuffPointer,
LPDWORD inlenPointer,
LPVOID options
)
{
unsigned int index;
char *SourcePointer, *DestPointer;
DWORD Result;
XipPrinterStruct *PrinterPointer;
XipServiceStruct *XipServicePointer;
XipCOLAChannelStruct *XipCOLAChannelPointer;
char *XipPacketDataPointer;
DWORD XipPacketDataLength;
TimeoutType TimeoutValue;
char *Buffer;
DWORD TimeWeStartedLooping;
xiptal_bool_t MoreToFollow;
DWORD SumOfDataLengths = 0;
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("====YAALReadChannel\n"));
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
if (!BeenInitialized)
{
Result = XipTALInit();
if (Result != RC_SUCCESS)
{
return Result;
}
}
Result = LookupCOLAChannelHandle(COLAChannelHandle,
&PrinterPointer,
&XipServicePointer,
&XipCOLAChannelPointer);
if (Result != RC_SUCCESS)
{
/* it's not one of ours...hopefully it's one of theirs */
return TALReadChannel(COLAChannelHandle,
VoidBuffPointer,
inlenPointer,
options);
}
/*
Before we do anything else, lets grab all the queued
xip packets and act upon them.
*/
HandleQueuedXipPackets();
if ((PrinterPointer == 0) ||
(PrinterPointer->MlcChannelHandle == 0))
{
return RC_FAILURE;
}
/* Service exists...wait for binding */
Result = WaitForServiceToBind(PrinterPointer,
XipServicePointer);
if (Result != RC_SUCCESS)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("WaitForBinding failed\n"));
DbmLog(TEXT("Leaving YAALReadChannel: failure\n"));
DbmLog(TEXT("===================================\n"));
#endif /* DBM_DEBUG_EROOSKI */
return Result;
}
Buffer = calloc(1, MLC_BUFFER_SIZE);
if (Buffer == 0)
{
return RC_FAILURE;
}
InitTimeoutStuff(TimeoutValue);
/*
To boost performance, we will spend a little time
just brute force reading the channel and if that
fails, we will let the callback do its thing.
*/
TimeWeStartedLooping = GetTickCount();
do
{
Result = ReadAndHandleAnXipPacket(PrinterPointer,
Buffer,
MLC_BUFFER_SIZE);
if (Result == RC_SUCCESS)
{
Result = GrabSomeFromQueue(XipCOLAChannelPointer,
&XipPacketDataPointer,
&XipPacketDataLength,
&MoreToFollow);
}
else
{
TALPollChannels(PrinterPointer->MlcChannelHandle);
}
} while (((GetTickCount() - TimeWeStartedLooping) < 500) &&
((Result != RC_SUCCESS) ||
(XipPacketDataLength == 0) ||
(XipPacketDataPointer == 0)));
free(Buffer);
/*
We reached this point either
- because we had a successful read with non-zero packet length
and our result is RC_SUCCESS and we have the xip packet
all ready to copy out to the caller, or
- because we exceeded our 500 milliseconds and result
will not be RC_SUCCESS
*/
DestPointer = (char *)VoidBuffPointer;
SumOfDataLengths = 0;
do
{
while ((NotTimedOutYet(TimeoutValue)) &&
((Result != RC_SUCCESS) ||
(XipPacketDataLength == 0) ||
(XipPacketDataPointer == 0)))
{
#ifdef LETS_DO_CALLBACKS
DaversYield(YIELD_NOW, NULL);
#else
WindowsYield(YIELD_NOW, NULL);
HandleQueuedXipPackets();
#endif
Result = GrabSomeFromQueue(XipCOLAChannelPointer,
&XipPacketDataPointer,
&XipPacketDataLength,
&MoreToFollow);
if (Result != RC_SUCCESS)
{
TALPollChannels(PrinterPointer->MlcChannelHandle);
}
}
if ((Result != RC_SUCCESS) ||
(XipPacketDataLength == 0) ||
(XipPacketDataPointer == 0))
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("YAALReadChannel: failure due to timeout\n"));
#endif /* DBM_DEBUG_EROOSKI */
return RC_FAILURE;
}
/*
Copy the data to the caller's buffer.
*/
SourcePointer = XipPacketDataPointer;
for (index = 0;
(((index + SumOfDataLengths) < *inlenPointer) &&
(index < XipPacketDataLength));
++index)
{
*DestPointer = *SourcePointer;
++SourcePointer;
++DestPointer;
}
SumOfDataLengths += index;
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("YAALReadChannel: copied %d\n"), index);
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
if ((MoreToFollow == xtTRUE) && (Result == RC_SUCCESS))
{
/* give 'em a full timeout to get the next packet */
InitTimeoutStuff(TimeoutValue);
Result = RC_FAILURE; /* <<< important to make while loop work */
}
}
while ((MoreToFollow == xtTRUE) && (SumOfDataLengths < *inlenPointer));
*inlenPointer = SumOfDataLengths;
GrabSomeFree(XipPacketDataPointer);
#ifdef DBM_DEBUG_EROOSKI_____________________NOT
DbmLog(TEXT("YAALReadChannel: success\n"));
#endif /* DBM_DEBUG_EROOSKI */
return RC_SUCCESS;
} /* YAALReadChannel */
/*
This allocates data for an xip data in packet.
*/
static char *
AllocateXipDataInPacket
(
DWORD length
)
{
return (char *)calloc((size_t)1, (size_t)length + 24);
} /* AllocateXipDataInPacket */
/*
This frees the memory that was allocated using AllocateXipDataInPacket.
*/
static void
FreeXipDataInPacket
(
char * XipPacketPointer
)
{
free(XipPacketPointer);
} /* FreeXipDataInPacket */
/*
Call this to wrap a data packet in xip as a data_out packet.
We put the header on the front of the xip packet
and then copy the data from the data buffer
to the packet buffer.
Return the number of bytes that were placed in the packet.
The DataPointer passed in is copied to the XipPacketPointer
so the caller may free the DataPointer after this call.
*/
static DWORD
FormatADataIn
(
XipServiceStruct *XipServicePointer,
XipCOLAChannelStruct *XipCOLAChannelPointer,
char *XipPacketPointer,
char *DataPointer,
DWORD DataInThisPacket,
DWORD MaxTransferSize,
DWORD *PacketSizePointer
)
{
unsigned int index;
xiptal_bool_t MoreToFollow;
*PacketSizePointer = 0;
if (XipPacketPointer == 0)
{
return 0;
}
if (MaxTransferSize < X_DATA_IN_HEADER_LENGTH)
{
return 0;
}
if (DataInThisPacket > MaxTransferSize - X_DATA_IN_HEADER_LENGTH)
{
/*
exceeded the packet size so we'll cut it up
into an x_data_in and one or more x_data_in_continue
x_data_in must have even number of bytes in this case
so round our amount down to an even number.
*/
DataInThisPacket = MaxTransferSize - X_DATA_IN_HEADER_LENGTH;
DataInThisPacket = DataInThisPacket - (DataInThisPacket % 2);
MoreToFollow = xtTRUE;
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("FormatADataIn: MoreToFollow\n"));
#endif /* DBM_DEBUG_EROOSKI */
}
else
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("FormatADataIn: none to follow\n"));
#endif /* DBM_DEBUG_EROOSKI */
MoreToFollow = xtFALSE;
}
*(XipPacketPointer + 0) = MSBYTEOF(x_data_in); /* command code */
*(XipPacketPointer + 1) = LSBYTEOF(x_data_in);
*(XipPacketPointer + 2) = MSBYTEOF(MLC_TRANSPORT_NUMBER);
*(XipPacketPointer + 3) = LSBYTEOF(MLC_TRANSPORT_NUMBER);
if (xtTRUE == MoreToFollow)
{
*(XipPacketPointer + 4) = MSBYTEOF(x_more_data);
*(XipPacketPointer + 5) = LSBYTEOF(x_more_data);
}
else
{
*(XipPacketPointer + 4) = MSBYTEOF(x_end_of_data);
*(XipPacketPointer + 5) = LSBYTEOF(x_end_of_data);
}
CopyPH(XipServicePointer->XipPacketPH,
XipPacketPointer + 6);
/* destination address */
*(XipPacketPointer + 10) = MSBYTEOF(XIP_LOCAL_ADDRESS_BYTES);
*(XipPacketPointer + 11) = LSBYTEOF(XIP_LOCAL_ADDRESS_BYTES);
*(XipPacketPointer + 12) = (char)
MSBYTEOF(XipServicePointer->ServiceNumber);
*(XipPacketPointer + 13) = (char)
LSBYTEOF(XipServicePointer->ServiceNumber);
/* source address */
*(XipPacketPointer + 14) = MSBYTEOF(XIP_LOCAL_ADDRESS_BYTES);
*(XipPacketPointer + 15) = LSBYTEOF(XIP_LOCAL_ADDRESS_BYTES);
*(XipPacketPointer + 16) = (char)
MSBYTEOF(XipCOLAChannelPointer->
COLAChannelHandle);
*(XipPacketPointer + 17) = (char)
LSBYTEOF(XipCOLAChannelPointer->
COLAChannelHandle);
*(XipPacketPointer + 18) = (char)MSBYTEOF(DataInThisPacket);
*(XipPacketPointer + 19) = (char)LSBYTEOF(DataInThisPacket);
XipPacketPointer += X_DATA_IN_HEADER_LENGTH; /* first data byte */
for (index = 0; index < DataInThisPacket; ++index)
{
*XipPacketPointer = *DataPointer;
++DataPointer;
++XipPacketPointer;
}
*PacketSizePointer = DataInThisPacket + X_DATA_IN_HEADER_LENGTH;
return DataInThisPacket;
} /* FormatADataIn */
static DWORD
FormatADataInC
(
XipServiceStruct *XipServicePointer,
XipCOLAChannelStruct *XipCOLAChannelPointer,
char *XipPacketPointer,
char *DataPointer,
DWORD DataInThisPacket,
DWORD MaxTransferSize,
DWORD *PacketSizePointer
)
{
unsigned int index;
xiptal_bool_t MoreToFollow;
*PacketSizePointer = 0;
if (XipPacketPointer == 0)
{
return 0;
}
if (MaxTransferSize < X_DATA_IN_CONTINUE_HEADER_LENGTH)
{
return 0;
}
if (DataInThisPacket > (MaxTransferSize - X_DATA_IN_CONTINUE_HEADER_LENGTH))
{
/*
exceeded the packet size so we'll cut it up
into another x_data_in_continue
x_data_in must have even number of bytes in this case
so round our amount down to an even number.
*/
DataInThisPacket = MaxTransferSize - X_DATA_IN_CONTINUE_HEADER_LENGTH;
DataInThisPacket = DataInThisPacket - (DataInThisPacket % 2);
MoreToFollow = xtTRUE;
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("FormatADataInC: MoreToFollow\n"));
#endif /* DBM_DEBUG_EROOSKI */
}
else
{
MoreToFollow = xtFALSE;
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("FormatADataInC: none to follow\n"));
#endif /* DBM_DEBUG_EROOSKI */
}
*(XipPacketPointer + 0) = MSBYTEOF(x_data_in_continue); /* command code */
*(XipPacketPointer + 1) = LSBYTEOF(x_data_in_continue);
*(XipPacketPointer + 2) = MSBYTEOF(MLC_TRANSPORT_NUMBER);
*(XipPacketPointer + 3) = LSBYTEOF(MLC_TRANSPORT_NUMBER);
if (xtTRUE == MoreToFollow)
{
*(XipPacketPointer + 4) = MSBYTEOF(x_more_data);
*(XipPacketPointer + 5) = LSBYTEOF(x_more_data);
}
else
{
*(XipPacketPointer + 4) = MSBYTEOF(x_end_of_data);
*(XipPacketPointer + 5) = LSBYTEOF(x_end_of_data);
}
CopyPH(XipServicePointer->XipPacketPH,
XipPacketPointer + 6);
*(XipPacketPointer + 10) = (char)MSBYTEOF(DataInThisPacket);
*(XipPacketPointer + 11) = (char)LSBYTEOF(DataInThisPacket);
XipPacketPointer += X_DATA_IN_CONTINUE_HEADER_LENGTH; /* first data byte */
for (index = 0; index < DataInThisPacket; ++index)
{
*XipPacketPointer = *DataPointer;
++DataPointer;
++XipPacketPointer;
}
*PacketSizePointer = DataInThisPacket + X_DATA_IN_CONTINUE_HEADER_LENGTH;
return DataInThisPacket;
} /* FormatADataInC */
/*
Call this to write data to a service on any printer on any transport.
The COLAChannelHandle is inspected and if it is not on mlc, the
call is routed to TAL for further handling.
It it is on mlc then we wrap it in xip and then send it to the printer.
*/
DLL_EXPORT(DWORD) CALLING_CONVEN
YAALWriteChannel
(
HCHANNEL COLAChannelHandle,
LPVOID VoidBuffPointer,
LPDWORD outlenPointer,
LPVOID options
)
{
char *BuffPointer = (char *)VoidBuffPointer;
DWORD Result;
DWORD TotalDataSent;
DWORD DataInThisPacket, PacketSize;
char *XipPacketPointer;
xiptal_bool_t DoADataInContinue = xtFALSE;
XipPrinterStruct *PrinterPointer;
XipServiceStruct *XipServicePointer;
XipCOLAChannelStruct *XipCOLAChannelPointer;
#ifdef DBM_DEBUG_EROOSKI
{
TCHAR buffster[100];
_stprintf(buffster, TEXT("====YAALWriteChannel: size = %d decimal\n"),
*outlenPointer);
DbmLog(buffster);
}
#endif /* DBM_DEBUG_EROOSKI */
if (!BeenInitialized)
{
Result = XipTALInit();
if (Result != RC_SUCCESS)
{
return Result;
}
}
Result = LookupCOLAChannelHandle(COLAChannelHandle,
&PrinterPointer,
&XipServicePointer,
&XipCOLAChannelPointer);
if (Result != RC_SUCCESS)
{
/* it's not one of ours...hopefully it's one of theirs */
return TALWriteChannel(COLAChannelHandle,
VoidBuffPointer,
outlenPointer,
options);
}
/*
Before we do anything else, lets grab all the queued
xip packets and act upon them.
*/
HandleQueuedXipPackets();
/*
Cut the write into xip data_in
and xip data_in_continue packets
and loop through them one at a time.
*/
/* Service exists...wait for binding */
Result = WaitForServiceToBind(PrinterPointer,
XipServicePointer);
if (Result != RC_SUCCESS)
{
#ifdef DBM_DEBUG_EROOSKI
DbmLog(TEXT("WaitForBinding failed\n"));
DbmLog(TEXT("Leaving YAALWriteChannel: failure\n"));
DbmLog(TEXT("===================================\n"));
#endif /* DBM_DEBUG_EROOSKI */
return Result;
}
XipPacketPointer = AllocateXipDataInPacket(*outlenPointer);
if (XipPacketPointer == 0)
{
return RC_FAILURE;
}
TotalDataSent = 0;
DoADataInContinue = xtFALSE;
while ((TotalDataSent < *outlenPointer) &&
(Result == RC_SUCCESS))
{
if (DoADataInContinue == xtFALSE)
{
DoADataInContinue = xtTRUE;
DataInThisPacket = FormatADataIn(XipServicePointer,
XipCOLAChannelPointer,
XipPacketPointer,
&(BuffPointer[TotalDataSent]),
(*outlenPointer - TotalDataSent),
PrinterPointer->MaxTransferSize,
&PacketSize);
}
else
{
DataInThisPacket = FormatADataInC(XipServicePointer,
XipCOLAChannelPointer,
XipPacketPointer,
&(BuffPointer[TotalDataSent]),
(*outlenPointer - TotalDataSent),
PrinterPointer->MaxTransferSize,
&PacketSize);
}
TotalDataSent += DataInThisPacket;
/*
Do actual mlc write via mlc tal call
*/
Result = PRETALWriteChannel(PrinterPointer->MlcChannelHandle,
(LPVOID)XipPacketPointer,
&PacketSize,
options);
}
FreeXipDataInPacket(XipPacketPointer);
return Result;
} /* YAALWriteChannel */