--- /dev/null
+/* Centronics Parallel Device Driver (1 channel v1.0). */\r
+/* Copyright 1991,1992,1993,1994 R.A. Burgess */\r
+/* ALL RIGHTS RESERVED */\r
+\r
+/* This driver is NOT interrupt driven because documentation\r
+ on interrupt usage with the parallel I/O device is sketchy at best..\r
+ We compensate for this by creating a separate task that is a\r
+ loop which statuses and continues to try to send all the data\r
+ without eating too much processor bandwidth.\r
+*/\r
+\r
+#define U32 unsigned long\r
+#define S32 long\r
+#define U16 unsigned int\r
+#define S16 int\r
+#define U8 unsigned char\r
+#define S8 char\r
+#define TRUE 1\r
+#define FALSE 0\r
+\r
+#include "parallel.h"\r
+\r
+/* MMURTL OS Prototypes */\r
+\r
+extern far U32 AllocExch(U32 *pExchRet);\r
+\r
+extern far U32 InitDevDr(U32 dDevNum,\r
+ S8 *pDCBs,\r
+ U32 nDevices,\r
+ U32 dfReplace);\r
+\r
+\r
+extern far U32 UnMaskIRQ(U32 IRQNum);\r
+extern far U32 MaskIRQ(U32 IRQNum);\r
+extern far U32 SetIRQVector(U32 IRQNum, S8 *pIRQ);\r
+extern far U32 EndOfIRQ(U32 IRQNum);\r
+extern far U32 SendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 ISendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 WaitMsg(U32 Exch, S8 *pMsgRet);\r
+extern far U32 CheckMsg(U32 Exch, S8 *pMsgRet);\r
+extern far U32 GetTimerTick(U32 *pTickRet);\r
+extern far U32 Alarm(U32 Exch, U32 count);\r
+extern far U32 KillAlarm(U32 Exch);\r
+extern far U32 Sleep(U32 count);\r
+extern far void MicroDelay(U32 us15count);\r
+extern far void OutByte(U8 Byte, U16 wPort);\r
+extern far U8 InByte(U16 wPort);\r
+extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
+\r
+extern far SpawnTask(S8 *pEntry,\r
+ U32 dPriority,\r
+ U32 fDebug,\r
+ S8 *pStack,\r
+ U32 fOSCode);\r
+\r
+extern far long GetJobNum(long *pJobNumRet);\r
+\r
+/* local prototypes. These will be called form the device driver interface. */\r
+\r
+static U32 lptdev_stat(U32 dDevice,\r
+ S8 *pStatRet,\r
+ U32 dStatusMax,\r
+ U32 *pdStatusRet);\r
+\r
+static S32 lptdev_init(U32 dDevice,\r
+ S8 *pInitData,\r
+ U32 sdInitData);\r
+\r
+static U32 lptdev_op(U32 dDevice,\r
+ U32 dOpNum,\r
+ U32 dLBA,\r
+ U32 dnBlocks,\r
+ U8 *pData);\r
+\r
+\r
+/* The following definitions are used to identify, set\r
+ and reset signal line conditions and functions.\r
+*/\r
+\r
+#define SSENDBUF 4096 /* 4K Send Buf */\r
+\r
+unsigned char SendBuf[SSENDBUF];\r
+\r
+static U32 xmit_timeout = 100; /* 10ms intervals - 1 second */\r
+\r
+static U32 head_send; /* Next char to send */\r
+static U32 tail_send; /* Where next char goes in buf */\r
+static U32 cSendBuf; /* Count of bytes in buf */\r
+static U32 sSendBuf; /* Size of buffer (allocated) */\r
+static U32 burstcount; /* for burst of chars to lpt */\r
+static U32 strobecount; /* tries to strobe the char out */\r
+\r
+static U8 control_byte = 0;\r
+\r
+ /* Control, Data & Status Registers for port */\r
+\r
+static U16 DAT; /* Data output register */\r
+static U16 STA; /* Status Register */\r
+static U16 STC; /* Status/Control Register */\r
+\r
+static U32 lptStk[200]; /* 800 byte stack for this task */\r
+static U32 lptStkTop;\r
+\r
+/* Complete description of Register bits follows:\r
+\r
+ DAT -- Bits 0-7 of data output pins\r
+\r
+ 7 6 5 4 3 2 1 0\r
+ \_\_\_\_\_\_\_\__ Data out Bits\r
+\r
+ STA -- Status Register\r
+ 1 1 0 1 1 1 1 1\r
+ 7 6 5 4 3 2 1 0\r
+ | | | | | \_\_\_ Not Used\r
+ | | | | \_______ Error Status (Input (P15)\r
+ | | | \_________ Select Status (Input P13)\r
+ | | \___________ Out of Paper Status (Input P12)\r
+ | \_____________ Acknoledge Status (Input P10)\r
+ \_______________ Busy Status (Input P11)\r
+\r
+\r
+ STC -- Status/Control Register\r
+\r
+ 7 6 5 4 3 2 1 0\r
+ | | | | | | | \_ Strobe Inverted (Input/Output P1)\r
+ | | | | | | \___ AutoFeed Inverted (Input/Output P14)\r
+ | | | | | \_____ Initialize (Input/Output P16)\r
+ | | | | \_______ Select Inverted (Input/Output P17)\r
+ | | | \_________ Status IRQ Enable (Input)\r
+ \_\_\___________ Not Used\r
+\r
+*/\r
+\r
+\r
+/* Record for 64 byte status and init record */\r
+/* This structure is peculiar to the lpt driver */\r
+\r
+#define sStatus 64\r
+\r
+static struct statRecL lptstat;\r
+static struct statRecL *pPS;\r
+\r
+static struct dcbtype\r
+{\r
+ S8 Name[12];\r
+ S8 sbName;\r
+ S8 type;\r
+ S16 nBPB;\r
+ U32 last_erc;\r
+ U32 nBlocks;\r
+ S8 *pDevOp;\r
+ S8 *pDevInit;\r
+ S8 *pDevSt;\r
+ S8 fDevReent;\r
+ S8 fSingleUser;\r
+ S16 wJob;\r
+ U32 OS1;\r
+ U32 OS2;\r
+ U32 OS3;\r
+ U32 OS4;\r
+ U32 OS5;\r
+ U32 OS6;\r
+ };\r
+\r
+static struct dcbtype lptdcb; /* One parallel port */\r
+\r
+\r
+/********************************************************************\r
+ This small function becomes a thread (task) to feel the printer.\r
+ It checks to see if there is data in the buffer, and if so it\r
+ sends the dat out in bursts of characters. It also properly sets\r
+ the status byte in the lptstat block so proper errors can be\r
+ returned to callers of the device driver.\r
+**********************************************************************/\r
+\r
+static void lpt_task(void)\r
+{\r
+ while (1)\r
+ {\r
+ /* Get lpt status every half second even if no data\r
+ is in buffer for those that may want it.\r
+ */\r
+\r
+ control_byte = InByte(STA); /* Get status Byte from STA */\r
+ lptstat.status = control_byte;\r
+\r
+ if (cSendBuf)\r
+ {\r
+ burstcount = 10;\r
+\r
+ while ((cSendBuf) && (burstcount--))\r
+ {\r
+ /* see if port is busy. If so, sleep 20ms, else send burst */\r
+\r
+ control_byte = InByte(STA); /* Get status Byte from STA */\r
+ lptstat.status = control_byte;\r
+\r
+ if (control_byte & LPTBUSY)\r
+ {\r
+#asm\r
+ CLI\r
+#endasm\r
+ OutByte(SendBuf[tail_send], DAT); /* Send the byte */\r
+ if (++tail_send == sSendBuf)\r
+ tail_send = 0;\r
+ --cSendBuf;\r
+#asm\r
+ STI\r
+#endasm\r
+ OutByte(0x0d, STC); /* Strobe High */\r
+ OutByte(0x0c, STC); /* Strobe Low */\r
+ }\r
+ else\r
+ Sleep(2);\r
+ }\r
+ Sleep(1); /* eliminate busyloop... */\r
+ }\r
+ else\r
+ Sleep(30); /* sleep for a .3 seconds */\r
+ }\r
+}\r
+\r
+/*********************************************************\r
+ This is called ONCE to initialize the 1 default\r
+ lpt channel with the OS device driver interface.\r
+*********************************************************/\r
+\r
+U32 lpt_setup(void)\r
+{\r
+U32 erc;\r
+\r
+ /* first we set up the DCB in anticipation of calling InitDevDr */\r
+\r
+ lptdcb.Name[0] = 'L';\r
+ lptdcb.Name[1] = 'P';\r
+ lptdcb.Name[2] = 'T';\r
+ lptdcb.sbName = 3;\r
+ lptdcb.type = 2; /* Sequential */\r
+ lptdcb.nBPB = 1; /* 1 byte per block */\r
+ lptdcb.nBlocks = 0; /* 0 for Sequential devices */\r
+ lptdcb.pDevOp = &lptdev_op;\r
+ lptdcb.pDevInit = &lptdev_init;\r
+ lptdcb.pDevSt = &lptdev_stat;\r
+\r
+ /* Set default lpt params in stat records */\r
+\r
+ lptstat.XTimeOut = 100; /* 1 second */\r
+ lptstat.IOBase = 0x378;\r
+ lptstat.IRQNum = 7; /* Not used right now */\r
+ lptstat.XBufSize = 4096;\r
+ sSendBuf = 4096;\r
+\r
+ DAT = lptstat.IOBase; /* Data output register */\r
+ STA = lptstat.IOBase +1; /* Status Register */\r
+ STC = lptstat.IOBase +2; /* Status/Control Register */\r
+\r
+ cSendBuf = 0;\r
+ head_send = 0;\r
+ tail_send = 0;\r
+\r
+ OutByte(0x08, STC); /* Reset (Init) Line Low */\r
+ MicroDelay(100); /* 1500us ought to do it */\r
+ OutByte(0x0C, STC); /* No ints, No AutoLF, Init High */\r
+\r
+ erc = SpawnTask( &lpt_task, 19, 0, &lptStkTop, 1);\r
+ if (erc)\r
+ return(erc);\r
+\r
+ return(erc = InitDevDr(3, &lptdcb, 1, 1));\r
+}\r
+\r
+/********************************************/\r
+static long WriteByteL(unsigned char b)\r
+{\r
+\r
+U32 erc, counter;\r
+U8 *pXBuf;\r
+\r
+ erc = 0;\r
+ counter = lptstat.XTimeOut; /* set up for timeout */\r
+\r
+ while (cSendBuf == sSendBuf)\r
+ {\r
+ Sleep(1);\r
+ counter--;\r
+ if (!counter)\r
+ return (ErcXmitTimeoutL); /* never got sent */\r
+ }\r
+\r
+#asm\r
+ CLI\r
+#endasm\r
+\r
+ SendBuf[head_send] = b;\r
+ if (++head_send == sSendBuf)\r
+ head_send = 0;\r
+ ++cSendBuf; /* one more in buf */\r
+\r
+#asm\r
+ STI\r
+#endasm\r
+ return (erc);\r
+}\r
+\r
+\r
+/********************************************/\r
+static long WriteRecordL(unsigned char *pSendData,\r
+ unsigned int cbSendData)\r
+{\r
+int erc;\r
+\r
+ erc = 0;\r
+ while ((cbSendData) && (!erc))\r
+ {\r
+ erc = WriteByteL(*pSendData++);\r
+ --cbSendData;\r
+ }\r
+ return (erc);\r
+}\r
+\r
+/********************************************\r
+ This allocates a buffer for use driver use.\r
+*********************************************/\r
+\r
+static U32 OpenLPT(void)\r
+\r
+{\r
+U32 erc, Job;\r
+U16 port_base;\r
+U8 c;\r
+\r
+ GetJobNum(&Job);\r
+\r
+ if (lptstat.lptJob)\r
+ {\r
+ if (Job != lptstat.lptJob)\r
+ return(ErcChannelOpenL); /* differnet job */\r
+ else\r
+ return(0); /* same job - already open */\r
+ }\r
+\r
+ lptstat.lptJob = Job;\r
+\r
+ /* Set up buffer variables for this job */\r
+\r
+ if (!cSendBuf)\r
+ {\r
+ cSendBuf = 0;\r
+ head_send = 0;\r
+ tail_send = 0;\r
+ }\r
+ port_base = lptstat.IOBase;\r
+\r
+ DAT = port_base; /* Data output register */\r
+ STA = port_base +1; /* Status Register */\r
+ STC = port_base +2; /* Status/Control Register */\r
+\r
+ return (0);\r
+}\r
+\r
+/********************************************\r
+ This closes the port, sets the owner to 0\r
+ and deallocates the buffers. If there is\r
+ still data to send, this diables ints,\r
+ kills the buffer, then closes it.\r
+********************************************/\r
+\r
+static int CloseLPT (int fAbort)\r
+{\r
+U32 erc, Job;\r
+\r
+ GetJobNum(&Job);\r
+\r
+ if (lptstat.lptJob)\r
+ {\r
+ if (Job != lptstat.lptJob)\r
+ return(ErcNotOwnerL); /* differnet job */\r
+ else\r
+ return(0); /* same job - already open */\r
+ }\r
+ else\r
+ return(ErcNotOpenL); /* Ports not open! */\r
+\r
+ if (fAbort)\r
+ {\r
+ cSendBuf = 0;\r
+ head_send = 0;\r
+ tail_send = 0;\r
+ }\r
+\r
+ lptstat.lptJob = 0;\r
+ return(0);\r
+}\r
+\r
+/***************************************************************************\r
+Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS\r
+****************************************************************************/\r
+\r
+/******************************************\r
+Called for all device operations. This\r
+assigns physical device from logical number\r
+that outside callers use. For RS-232, 5=0\r
+and 6=1.\r
+*******************************************/\r
+\r
+static U32 lptdev_op(U32 dDevice,\r
+ U32 dOpNum,\r
+ U32 dLBA,\r
+ U32 dnBlocks,\r
+ U8 *pData)\r
+{\r
+U32 erc;\r
+U32 Job;\r
+U8 c;\r
+\r
+ GetJobNum(&Job);\r
+\r
+ if ((!lptstat.lptJob) && (dOpNum != CmdOpenL))\r
+ return(ErcNotOpenL);\r
+\r
+ if (lptstat.lptJob)\r
+ {\r
+ if ((lptstat.lptJob != Job) &&\r
+ (Job != 1))\r
+ return(ErcNotOwnerL);\r
+ }\r
+\r
+ erc = 0; /* default error */\r
+\r
+ switch(dOpNum)\r
+ {\r
+ case(0):\r
+ break; /* Null Command */\r
+ case CmdWriteB:\r
+ erc = WriteByteL(*pData);\r
+ break;\r
+ case CmdWriteRec:\r
+ erc = WriteRecordL(pData, dnBlocks);\r
+ break;\r
+ case CmdSetXTO:\r
+ lptstat.XTimeOut = dLBA; /* 10ms intervals */\r
+ break;\r
+ case CmdOpenL:\r
+ erc = OpenLPT();\r
+ break;\r
+ case CmdCloseL:\r
+ erc = CloseLPT(0);\r
+ break;\r
+ case CmdCloseLU:\r
+ erc = CloseLPT(1);\r
+ break;\r
+ default:\r
+ erc = ErcBadOpNum; /* default error */\r
+ break;\r
+ }\r
+\r
+ lptstat.LastErc = erc;\r
+ return(erc);\r
+}\r
+\r
+\r
+/******************************************\r
+Called for status report on lpt channel.\r
+Returns 64 byte block for channel specified.\r
+This is called by the PUBLIC call DeviceStat\r
+*******************************************/\r
+\r
+static U32 lptdev_stat(U32 dDevice,\r
+ S8 *pStatRet,\r
+ U32 dStatusMax,\r
+ U32 *pdStatusRet)\r
+{\r
+U32 i;\r
+\r
+ if (dStatusMax > 64)\r
+ i = 64;\r
+ else\r
+ i = dStatusMax;\r
+\r
+ lptstat.BufCnt = cSendBuf;\r
+\r
+ CopyData(&lptstat, pStatRet, i); /* copy the status data */\r
+ *pdStatusRet = dStatusMax; /* give em the size returned */\r
+ return(0);\r
+}\r
+\r
+/******************************************\r
+Called to set parameters for the lpt\r
+channel prior to opening or while in use.\r
+If an invalid value is passed in, all params\r
+remain the same as before.\r
+This is called by the PUBLIC call DeviceInit.\r
+Only the timeout value may be changed while\r
+the port is open.\r
+*******************************************/\r
+\r
+static S32 lptdev_init(U32 dDevice,\r
+ S8 *pInitData,\r
+ U32 sdInitData)\r
+\r
+{\r
+U32 erc, Xbufsize, XTO, job;\r
+U16 port_base;\r
+U8 IRQNUM;\r
+\r
+ erc = 0;\r
+\r
+ GetJobNum(&job);\r
+ if ((lptstat.lptJob) && (lptstat.lptJob != job))\r
+ return(ErcNotOwnerL); /* Port is in use, and not by you! */\r
+\r
+ if (sdInitData < 40)\r
+ return(ErcBadInitSizeL);\r
+\r
+ pPS = pInitData;\r
+\r
+ /* Get the callers new params into local vars */\r
+\r
+ XTO = pPS->XTimeOut; /* Non Volatile */\r
+ port_base = pPS->IOBase;\r
+\r
+ /* Volatile params can not change while port is open. */\r
+\r
+ if (lptstat.lptJob) /* Port is in use */\r
+ {\r
+ if (lptstat.IOBase != port_base)\r
+ erc = ErcChannelOpenL;\r
+ }\r
+\r
+ /* Non Volatile params can be set whether or not the\r
+ channel is open. */\r
+\r
+ if (!XTO) XTO = 100;\r
+ lptstat.XTimeOut = XTO;\r
+\r
+ if (!port_base)\r
+ return (ErcBadIOBaseL);\r
+\r
+ lptstat.IOBase = port_base;\r
+ DAT = lptstat.IOBase; /* Data output register */\r
+ STA = lptstat.IOBase +1; /* Status Register */\r
+ STC = lptstat.IOBase +2; /* Status/Control Register */\r
+\r
+ /* If in use and no data in buf, or no one is using\r
+ lpt channel then do a HARD reset on it.\r
+ */\r
+\r
+ if ( ((lptstat.lptJob) && (!cSendBuf)) || (!lptstat.lptJob) )\r
+ {\r
+ OutByte(0x08, STC); /* Reset Line Low */\r
+ MicroDelay(100); /* 1.5 ms ought to do it */\r
+ OutByte(0x0C, STC); /* No ints, No AutoLF, Init High */\r
+ }\r
+\r
+ erc = 0;\r
+ return(erc);\r
+}\r