1 /* Centronics Parallel Device Driver (1 channel v1.0). */
\r
2 /* Copyright 1991,1992,1993,1994 R.A. Burgess */
\r
3 /* ALL RIGHTS RESERVED */
\r
5 /* This driver is NOT interrupt driven because documentation
\r
6 on interrupt usage with the parallel I/O device is sketchy at best..
\r
7 We compensate for this by creating a separate task that is a
\r
8 loop which statuses and continues to try to send all the data
\r
9 without eating too much processor bandwidth.
\r
12 #define U32 unsigned long
\r
14 #define U16 unsigned int
\r
16 #define U8 unsigned char
\r
21 #include "parallel.h"
\r
23 /* MMURTL OS Prototypes */
\r
25 extern far U32 AllocExch(U32 *pExchRet);
\r
27 extern far U32 InitDevDr(U32 dDevNum,
\r
33 extern far U32 UnMaskIRQ(U32 IRQNum);
\r
34 extern far U32 MaskIRQ(U32 IRQNum);
\r
35 extern far U32 SetIRQVector(U32 IRQNum, S8 *pIRQ);
\r
36 extern far U32 EndOfIRQ(U32 IRQNum);
\r
37 extern far U32 SendMsg(U32 Exch, U32 msg1, U32 msg2);
\r
38 extern far U32 ISendMsg(U32 Exch, U32 msg1, U32 msg2);
\r
39 extern far U32 WaitMsg(U32 Exch, S8 *pMsgRet);
\r
40 extern far U32 CheckMsg(U32 Exch, S8 *pMsgRet);
\r
41 extern far U32 GetTimerTick(U32 *pTickRet);
\r
42 extern far U32 Alarm(U32 Exch, U32 count);
\r
43 extern far U32 KillAlarm(U32 Exch);
\r
44 extern far U32 Sleep(U32 count);
\r
45 extern far void MicroDelay(U32 us15count);
\r
46 extern far void OutByte(U8 Byte, U16 wPort);
\r
47 extern far U8 InByte(U16 wPort);
\r
48 extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);
\r
50 extern far SpawnTask(S8 *pEntry,
\r
56 extern far long GetJobNum(long *pJobNumRet);
\r
58 /* local prototypes. These will be called form the device driver interface. */
\r
60 static U32 lptdev_stat(U32 dDevice,
\r
65 static S32 lptdev_init(U32 dDevice,
\r
69 static U32 lptdev_op(U32 dDevice,
\r
76 /* The following definitions are used to identify, set
\r
77 and reset signal line conditions and functions.
\r
80 #define SSENDBUF 4096 /* 4K Send Buf */
\r
82 unsigned char SendBuf[SSENDBUF];
\r
84 static U32 xmit_timeout = 100; /* 10ms intervals - 1 second */
\r
86 static U32 head_send; /* Next char to send */
\r
87 static U32 tail_send; /* Where next char goes in buf */
\r
88 static U32 cSendBuf; /* Count of bytes in buf */
\r
89 static U32 sSendBuf; /* Size of buffer (allocated) */
\r
90 static U32 burstcount; /* for burst of chars to lpt */
\r
91 static U32 strobecount; /* tries to strobe the char out */
\r
93 static U8 control_byte = 0;
\r
95 /* Control, Data & Status Registers for port */
\r
97 static U16 DAT; /* Data output register */
\r
98 static U16 STA; /* Status Register */
\r
99 static U16 STC; /* Status/Control Register */
\r
101 static U32 lptStk[200]; /* 800 byte stack for this task */
\r
102 static U32 lptStkTop;
\r
104 /* Complete description of Register bits follows:
\r
106 DAT -- Bits 0-7 of data output pins
\r
109 \_\_\_\_\_\_\_\__ Data out Bits
\r
111 STA -- Status Register
\r
114 | | | | | \_\_\_ Not Used
\r
115 | | | | \_______ Error Status (Input (P15)
\r
116 | | | \_________ Select Status (Input P13)
\r
117 | | \___________ Out of Paper Status (Input P12)
\r
118 | \_____________ Acknoledge Status (Input P10)
\r
119 \_______________ Busy Status (Input P11)
\r
122 STC -- Status/Control Register
\r
125 | | | | | | | \_ Strobe Inverted (Input/Output P1)
\r
126 | | | | | | \___ AutoFeed Inverted (Input/Output P14)
\r
127 | | | | | \_____ Initialize (Input/Output P16)
\r
128 | | | | \_______ Select Inverted (Input/Output P17)
\r
129 | | | \_________ Status IRQ Enable (Input)
\r
130 \_\_\___________ Not Used
\r
135 /* Record for 64 byte status and init record */
\r
136 /* This structure is peculiar to the lpt driver */
\r
140 static struct statRecL lptstat;
\r
141 static struct statRecL *pPS;
\r
143 static struct dcbtype
\r
165 static struct dcbtype lptdcb; /* One parallel port */
\r
168 /********************************************************************
\r
169 This small function becomes a thread (task) to feel the printer.
\r
170 It checks to see if there is data in the buffer, and if so it
\r
171 sends the dat out in bursts of characters. It also properly sets
\r
172 the status byte in the lptstat block so proper errors can be
\r
173 returned to callers of the device driver.
\r
174 **********************************************************************/
\r
176 static void lpt_task(void)
\r
180 /* Get lpt status every half second even if no data
\r
181 is in buffer for those that may want it.
\r
184 control_byte = InByte(STA); /* Get status Byte from STA */
\r
185 lptstat.status = control_byte;
\r
191 while ((cSendBuf) && (burstcount--))
\r
193 /* see if port is busy. If so, sleep 20ms, else send burst */
\r
195 control_byte = InByte(STA); /* Get status Byte from STA */
\r
196 lptstat.status = control_byte;
\r
198 if (control_byte & LPTBUSY)
\r
203 OutByte(SendBuf[tail_send], DAT); /* Send the byte */
\r
204 if (++tail_send == sSendBuf)
\r
210 OutByte(0x0d, STC); /* Strobe High */
\r
211 OutByte(0x0c, STC); /* Strobe Low */
\r
216 Sleep(1); /* eliminate busyloop... */
\r
219 Sleep(30); /* sleep for a .3 seconds */
\r
223 /*********************************************************
\r
224 This is called ONCE to initialize the 1 default
\r
225 lpt channel with the OS device driver interface.
\r
226 *********************************************************/
\r
228 U32 lpt_setup(void)
\r
232 /* first we set up the DCB in anticipation of calling InitDevDr */
\r
234 lptdcb.Name[0] = 'L';
\r
235 lptdcb.Name[1] = 'P';
\r
236 lptdcb.Name[2] = 'T';
\r
238 lptdcb.type = 2; /* Sequential */
\r
239 lptdcb.nBPB = 1; /* 1 byte per block */
\r
240 lptdcb.nBlocks = 0; /* 0 for Sequential devices */
\r
241 lptdcb.pDevOp = &lptdev_op;
\r
242 lptdcb.pDevInit = &lptdev_init;
\r
243 lptdcb.pDevSt = &lptdev_stat;
\r
245 /* Set default lpt params in stat records */
\r
247 lptstat.XTimeOut = 100; /* 1 second */
\r
248 lptstat.IOBase = 0x378;
\r
249 lptstat.IRQNum = 7; /* Not used right now */
\r
250 lptstat.XBufSize = 4096;
\r
253 DAT = lptstat.IOBase; /* Data output register */
\r
254 STA = lptstat.IOBase +1; /* Status Register */
\r
255 STC = lptstat.IOBase +2; /* Status/Control Register */
\r
261 OutByte(0x08, STC); /* Reset (Init) Line Low */
\r
262 MicroDelay(100); /* 1500us ought to do it */
\r
263 OutByte(0x0C, STC); /* No ints, No AutoLF, Init High */
\r
265 erc = SpawnTask( &lpt_task, 19, 0, &lptStkTop, 1);
\r
269 return(erc = InitDevDr(3, &lptdcb, 1, 1));
\r
272 /********************************************/
\r
273 static long WriteByteL(unsigned char b)
\r
280 counter = lptstat.XTimeOut; /* set up for timeout */
\r
282 while (cSendBuf == sSendBuf)
\r
287 return (ErcXmitTimeoutL); /* never got sent */
\r
294 SendBuf[head_send] = b;
\r
295 if (++head_send == sSendBuf)
\r
297 ++cSendBuf; /* one more in buf */
\r
306 /********************************************/
\r
307 static long WriteRecordL(unsigned char *pSendData,
\r
308 unsigned int cbSendData)
\r
313 while ((cbSendData) && (!erc))
\r
315 erc = WriteByteL(*pSendData++);
\r
321 /********************************************
\r
322 This allocates a buffer for use driver use.
\r
323 *********************************************/
\r
325 static U32 OpenLPT(void)
\r
334 if (lptstat.lptJob)
\r
336 if (Job != lptstat.lptJob)
\r
337 return(ErcChannelOpenL); /* differnet job */
\r
339 return(0); /* same job - already open */
\r
342 lptstat.lptJob = Job;
\r
344 /* Set up buffer variables for this job */
\r
352 port_base = lptstat.IOBase;
\r
354 DAT = port_base; /* Data output register */
\r
355 STA = port_base +1; /* Status Register */
\r
356 STC = port_base +2; /* Status/Control Register */
\r
361 /********************************************
\r
362 This closes the port, sets the owner to 0
\r
363 and deallocates the buffers. If there is
\r
364 still data to send, this diables ints,
\r
365 kills the buffer, then closes it.
\r
366 ********************************************/
\r
368 static int CloseLPT (int fAbort)
\r
374 if (lptstat.lptJob)
\r
376 if (Job != lptstat.lptJob)
\r
377 return(ErcNotOwnerL); /* differnet job */
\r
379 return(0); /* same job - already open */
\r
382 return(ErcNotOpenL); /* Ports not open! */
\r
391 lptstat.lptJob = 0;
\r
395 /***************************************************************************
\r
396 Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS
\r
397 ****************************************************************************/
\r
399 /******************************************
\r
400 Called for all device operations. This
\r
401 assigns physical device from logical number
\r
402 that outside callers use. For RS-232, 5=0
\r
404 *******************************************/
\r
406 static U32 lptdev_op(U32 dDevice,
\r
418 if ((!lptstat.lptJob) && (dOpNum != CmdOpenL))
\r
419 return(ErcNotOpenL);
\r
421 if (lptstat.lptJob)
\r
423 if ((lptstat.lptJob != Job) &&
\r
425 return(ErcNotOwnerL);
\r
428 erc = 0; /* default error */
\r
433 break; /* Null Command */
\r
435 erc = WriteByteL(*pData);
\r
438 erc = WriteRecordL(pData, dnBlocks);
\r
441 lptstat.XTimeOut = dLBA; /* 10ms intervals */
\r
453 erc = ErcBadOpNum; /* default error */
\r
457 lptstat.LastErc = erc;
\r
462 /******************************************
\r
463 Called for status report on lpt channel.
\r
464 Returns 64 byte block for channel specified.
\r
465 This is called by the PUBLIC call DeviceStat
\r
466 *******************************************/
\r
468 static U32 lptdev_stat(U32 dDevice,
\r
475 if (dStatusMax > 64)
\r
480 lptstat.BufCnt = cSendBuf;
\r
482 CopyData(&lptstat, pStatRet, i); /* copy the status data */
\r
483 *pdStatusRet = dStatusMax; /* give em the size returned */
\r
487 /******************************************
\r
488 Called to set parameters for the lpt
\r
489 channel prior to opening or while in use.
\r
490 If an invalid value is passed in, all params
\r
491 remain the same as before.
\r
492 This is called by the PUBLIC call DeviceInit.
\r
493 Only the timeout value may be changed while
\r
495 *******************************************/
\r
497 static S32 lptdev_init(U32 dDevice,
\r
502 U32 erc, Xbufsize, XTO, job;
\r
509 if ((lptstat.lptJob) && (lptstat.lptJob != job))
\r
510 return(ErcNotOwnerL); /* Port is in use, and not by you! */
\r
512 if (sdInitData < 40)
\r
513 return(ErcBadInitSizeL);
\r
517 /* Get the callers new params into local vars */
\r
519 XTO = pPS->XTimeOut; /* Non Volatile */
\r
520 port_base = pPS->IOBase;
\r
522 /* Volatile params can not change while port is open. */
\r
524 if (lptstat.lptJob) /* Port is in use */
\r
526 if (lptstat.IOBase != port_base)
\r
527 erc = ErcChannelOpenL;
\r
530 /* Non Volatile params can be set whether or not the
\r
531 channel is open. */
\r
533 if (!XTO) XTO = 100;
\r
534 lptstat.XTimeOut = XTO;
\r
537 return (ErcBadIOBaseL);
\r
539 lptstat.IOBase = port_base;
\r
540 DAT = lptstat.IOBase; /* Data output register */
\r
541 STA = lptstat.IOBase +1; /* Status Register */
\r
542 STC = lptstat.IOBase +2; /* Status/Control Register */
\r
544 /* If in use and no data in buf, or no one is using
\r
545 lpt channel then do a HARD reset on it.
\r
548 if ( ((lptstat.lptJob) && (!cSendBuf)) || (!lptstat.lptJob) )
\r
550 OutByte(0x08, STC); /* Reset Line Low */
\r
551 MicroDelay(100); /* 1.5 ms ought to do it */
\r
552 OutByte(0x0C, STC); /* No ints, No AutoLF, Init High */
\r