]> pd.if.org Git - mmurtl/blob - ossource/parallel.c
autocommit for file dated 2003-12-29 17:36:54
[mmurtl] / ossource / parallel.c
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
4 \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
10 */\r
11 \r
12 #define U32 unsigned long\r
13 #define S32 long\r
14 #define U16 unsigned int\r
15 #define S16 int\r
16 #define U8 unsigned char\r
17 #define S8 char\r
18 #define TRUE   1\r
19 #define FALSE  0\r
20 \r
21 #include "parallel.h"\r
22 \r
23 /* MMURTL OS Prototypes */\r
24 \r
25 extern far U32 AllocExch(U32 *pExchRet);\r
26 \r
27 extern far U32 InitDevDr(U32 dDevNum,\r
28                                           S8  *pDCBs,\r
29                                                   U32 nDevices,\r
30                                                   U32 dfReplace);\r
31 \r
32 \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
49 \r
50 extern far SpawnTask(S8  *pEntry,\r
51                              U32 dPriority,\r
52                      U32 fDebug,\r
53                      S8  *pStack,\r
54                              U32 fOSCode);\r
55 \r
56 extern far long GetJobNum(long *pJobNumRet);\r
57 \r
58 /* local prototypes. These will be called form the device driver interface. */\r
59 \r
60 static U32 lptdev_stat(U32  dDevice,\r
61                        S8  *pStatRet,\r
62                        U32 dStatusMax,\r
63                        U32 *pdStatusRet);\r
64 \r
65 static S32 lptdev_init(U32  dDevice,\r
66                        S8  *pInitData,\r
67                        U32  sdInitData);\r
68 \r
69 static U32 lptdev_op(U32 dDevice,\r
70                      U32 dOpNum,\r
71                      U32 dLBA,\r
72                      U32 dnBlocks,\r
73                      U8  *pData);\r
74 \r
75 \r
76 /* The following definitions are used to identify, set\r
77    and reset signal line conditions and functions.\r
78 */\r
79 \r
80 #define SSENDBUF 4096       /* 4K Send Buf */\r
81 \r
82 unsigned char SendBuf[SSENDBUF];\r
83 \r
84 static U32  xmit_timeout = 100; /* 10ms intervals - 1 second */\r
85 \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
92 \r
93 static U8   control_byte = 0;\r
94 \r
95  /* Control, Data & Status Registers for port */\r
96 \r
97 static U16      DAT;        /* Data output register */\r
98 static U16      STA;        /* Status Register */\r
99 static U16      STC;        /* Status/Control Register */\r
100 \r
101 static U32 lptStk[200];  /* 800 byte stack for this task */\r
102 static U32 lptStkTop;\r
103 \r
104 /* Complete description of Register bits follows:\r
105 \r
106     DAT -- Bits 0-7 of data output pins\r
107 \r
108                 7 6 5 4 3 2 1 0\r
109                  \_\_\_\_\_\_\_\__ Data out Bits\r
110 \r
111     STA -- Status Register\r
112                 1 1 0 1 1 1 1 1\r
113                 7 6 5 4 3 2 1 0\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
120 \r
121 \r
122     STC -- Status/Control Register\r
123 \r
124                 7 6 5 4 3 2 1 0\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
131 \r
132 */\r
133 \r
134 \r
135 /* Record for 64 byte status and init record */\r
136 /* This structure is peculiar to the lpt driver */\r
137 \r
138 #define sStatus 64\r
139 \r
140 static struct statRecL lptstat;\r
141 static struct statRecL *pPS;\r
142 \r
143 static struct dcbtype\r
144 {\r
145     S8   Name[12];\r
146     S8   sbName;\r
147     S8   type;\r
148     S16  nBPB;\r
149     U32  last_erc;\r
150     U32  nBlocks;\r
151     S8  *pDevOp;\r
152     S8  *pDevInit;\r
153     S8  *pDevSt;\r
154     S8   fDevReent;\r
155     S8   fSingleUser;\r
156     S16  wJob;\r
157     U32  OS1;\r
158     U32  OS2;\r
159     U32  OS3;\r
160     U32  OS4;\r
161     U32  OS5;\r
162     U32  OS6;\r
163     };\r
164 \r
165 static struct dcbtype lptdcb;           /* One parallel port */\r
166 \r
167 \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
175 \r
176 static void lpt_task(void)\r
177 {\r
178         while (1)\r
179         {\r
180                 /* Get lpt status every half second even if no data\r
181                 is in buffer for those that may want it.\r
182                 */\r
183 \r
184                 control_byte = InByte(STA);  /* Get status Byte from STA */\r
185                 lptstat.status = control_byte;\r
186 \r
187                 if (cSendBuf)\r
188                 {\r
189             burstcount = 10;\r
190 \r
191                         while ((cSendBuf) && (burstcount--))\r
192                         {\r
193                                 /* see if port is busy. If so, sleep 20ms, else send burst */\r
194 \r
195                                 control_byte = InByte(STA);  /* Get status Byte from STA */\r
196                                 lptstat.status = control_byte;\r
197 \r
198                                 if (control_byte & LPTBUSY)\r
199                             {\r
200 #asm\r
201         CLI\r
202 #endasm\r
203                             OutByte(SendBuf[tail_send], DAT);  /* Send the byte */\r
204                             if (++tail_send == sSendBuf)\r
205                                         tail_send = 0;\r
206                                 --cSendBuf;\r
207 #asm\r
208         STI\r
209 #endasm\r
210                                 OutByte(0x0d, STC); /* Strobe High */\r
211                                 OutByte(0x0c, STC); /* Strobe Low */\r
212                                 }\r
213                             else\r
214                                         Sleep(2);\r
215                         }\r
216                         Sleep(1);  /* eliminate busyloop... */\r
217                 }\r
218                 else\r
219                         Sleep(30);      /* sleep for a .3 seconds */\r
220         }\r
221 }\r
222 \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
227 \r
228 U32  lpt_setup(void)\r
229 {\r
230 U32  erc;\r
231 \r
232   /* first we set up the DCB in anticipation of calling InitDevDr */\r
233 \r
234     lptdcb.Name[0]  = 'L';\r
235     lptdcb.Name[1]  = 'P';\r
236     lptdcb.Name[2]  = 'T';\r
237     lptdcb.sbName   = 3;\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
244 \r
245     /* Set default lpt params in stat records */\r
246 \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
251     sSendBuf = 4096;\r
252 \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
256 \r
257         cSendBuf = 0;\r
258         head_send = 0;\r
259         tail_send = 0;\r
260 \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
264 \r
265         erc = SpawnTask( &lpt_task, 19, 0, &lptStkTop, 1);\r
266         if (erc)\r
267                 return(erc);\r
268 \r
269     return(erc = InitDevDr(3, &lptdcb, 1, 1));\r
270 }\r
271 \r
272 /********************************************/\r
273 static long WriteByteL(unsigned char b)\r
274 {\r
275 \r
276 U32 erc, counter;\r
277 U8 *pXBuf;\r
278 \r
279         erc = 0;\r
280         counter = lptstat.XTimeOut;             /* set up for timeout */\r
281 \r
282         while (cSendBuf == sSendBuf)\r
283         {\r
284                 Sleep(1);\r
285                 counter--;\r
286                 if  (!counter)\r
287                         return (ErcXmitTimeoutL);               /* never got sent */\r
288         }\r
289 \r
290 #asm\r
291         CLI\r
292 #endasm\r
293 \r
294     SendBuf[head_send] = b;\r
295     if (++head_send == sSendBuf)\r
296          head_send  = 0;\r
297         ++cSendBuf;                             /* one more in buf */\r
298 \r
299 #asm\r
300         STI\r
301 #endasm\r
302         return (erc);\r
303 }\r
304 \r
305 \r
306 /********************************************/\r
307 static long WriteRecordL(unsigned char *pSendData,\r
308                          unsigned int cbSendData)\r
309 {\r
310 int erc;\r
311 \r
312         erc = 0;\r
313     while ((cbSendData) && (!erc))\r
314     {\r
315                 erc = WriteByteL(*pSendData++);\r
316                 --cbSendData;\r
317         }\r
318         return (erc);\r
319 }\r
320 \r
321 /********************************************\r
322  This allocates a buffer for use driver use.\r
323 *********************************************/\r
324 \r
325 static U32  OpenLPT(void)\r
326 \r
327 {\r
328 U32  erc, Job;\r
329 U16  port_base;\r
330 U8   c;\r
331 \r
332         GetJobNum(&Job);\r
333 \r
334         if (lptstat.lptJob)\r
335         {\r
336                 if (Job != lptstat.lptJob)\r
337                         return(ErcChannelOpenL);        /* differnet job */\r
338                 else\r
339                         return(0); /* same job - already open */\r
340         }\r
341 \r
342         lptstat.lptJob = Job;\r
343 \r
344         /* Set up buffer variables for this job */\r
345 \r
346         if (!cSendBuf)\r
347         {\r
348                 cSendBuf = 0;\r
349                 head_send = 0;\r
350                 tail_send = 0;\r
351         }\r
352         port_base = lptstat.IOBase;\r
353 \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
357 \r
358         return (0);\r
359 }\r
360 \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
367 \r
368 static int  CloseLPT (int fAbort)\r
369 {\r
370 U32 erc, Job;\r
371 \r
372         GetJobNum(&Job);\r
373 \r
374         if (lptstat.lptJob)\r
375         {\r
376                 if (Job != lptstat.lptJob)\r
377                         return(ErcNotOwnerL);   /* differnet job */\r
378                 else\r
379                         return(0); /* same job - already open */\r
380         }\r
381         else\r
382                 return(ErcNotOpenL);    /* Ports not open! */\r
383 \r
384         if (fAbort)\r
385         {\r
386                 cSendBuf = 0;\r
387                 head_send = 0;\r
388                 tail_send = 0;\r
389         }\r
390 \r
391         lptstat.lptJob = 0;\r
392         return(0);\r
393 }\r
394 \r
395 /***************************************************************************\r
396 Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS\r
397 ****************************************************************************/\r
398 \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
403 and 6=1.\r
404 *******************************************/\r
405 \r
406 static U32 lptdev_op(U32 dDevice,\r
407                     U32 dOpNum,\r
408                     U32 dLBA,\r
409                     U32 dnBlocks,\r
410                     U8  *pData)\r
411 {\r
412 U32 erc;\r
413 U32 Job;\r
414 U8 c;\r
415 \r
416         GetJobNum(&Job);\r
417 \r
418         if ((!lptstat.lptJob) && (dOpNum != CmdOpenL))\r
419                 return(ErcNotOpenL);\r
420 \r
421         if (lptstat.lptJob)\r
422         {\r
423                 if ((lptstat.lptJob != Job) &&\r
424                         (Job != 1))\r
425                         return(ErcNotOwnerL);\r
426         }\r
427 \r
428         erc = 0;                /* default error */\r
429 \r
430         switch(dOpNum)\r
431         {\r
432                 case(0):\r
433                         break;                          /* Null Command */\r
434                 case CmdWriteB:\r
435                         erc = WriteByteL(*pData);\r
436                         break;\r
437                 case CmdWriteRec:\r
438                         erc = WriteRecordL(pData, dnBlocks);\r
439                         break;\r
440                 case CmdSetXTO:\r
441                         lptstat.XTimeOut = dLBA;                /* 10ms intervals */\r
442                         break;\r
443                 case CmdOpenL:\r
444                         erc =  OpenLPT();\r
445                         break;\r
446                 case CmdCloseL:\r
447                         erc =  CloseLPT(0);\r
448                         break;\r
449                 case CmdCloseLU:\r
450                         erc =  CloseLPT(1);\r
451                         break;\r
452                 default:\r
453                         erc = ErcBadOpNum;              /* default error */\r
454                         break;\r
455         }\r
456 \r
457         lptstat.LastErc = erc;\r
458         return(erc);\r
459 }\r
460 \r
461 \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
467 \r
468 static U32 lptdev_stat(U32  dDevice,\r
469                           S8  *pStatRet,\r
470                           U32 dStatusMax,\r
471                           U32 *pdStatusRet)\r
472 {\r
473 U32 i;\r
474 \r
475         if (dStatusMax > 64)\r
476                 i = 64;\r
477         else\r
478             i = dStatusMax;\r
479 \r
480     lptstat.BufCnt = cSendBuf;\r
481 \r
482         CopyData(&lptstat, pStatRet, i);                /* copy the status data */\r
483         *pdStatusRet = dStatusMax;              /* give em the size returned */\r
484         return(0);\r
485 }\r
486 \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
494 the port is open.\r
495 *******************************************/\r
496 \r
497 static S32 lptdev_init(U32  dDevice,\r
498                          S8  *pInitData,\r
499                          U32  sdInitData)\r
500 \r
501 {\r
502 U32  erc, Xbufsize,     XTO, job;\r
503 U16  port_base;\r
504 U8   IRQNUM;\r
505 \r
506         erc = 0;\r
507 \r
508         GetJobNum(&job);\r
509         if ((lptstat.lptJob) && (lptstat.lptJob != job))\r
510                 return(ErcNotOwnerL);   /* Port is in use, and not by you! */\r
511 \r
512         if (sdInitData < 40)\r
513                 return(ErcBadInitSizeL);\r
514 \r
515         pPS = pInitData;\r
516 \r
517         /* Get the callers new params into local vars */\r
518 \r
519         XTO       = pPS->XTimeOut;      /* Non Volatile */\r
520         port_base = pPS->IOBase;\r
521 \r
522         /* Volatile params can not change while port is open. */\r
523 \r
524         if (lptstat.lptJob)             /* Port is in use */\r
525         {\r
526         if (lptstat.IOBase != port_base)\r
527                 erc = ErcChannelOpenL;\r
528         }\r
529 \r
530         /* Non Volatile params can be set whether or not the\r
531            channel is open. */\r
532 \r
533     if (!XTO)  XTO = 100;\r
534         lptstat.XTimeOut = XTO;\r
535 \r
536     if (!port_base)\r
537                 return (ErcBadIOBaseL);\r
538 \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
543 \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
546         */\r
547 \r
548         if ( ((lptstat.lptJob) && (!cSendBuf)) || (!lptstat.lptJob) )\r
549         {\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
553         }\r
554 \r
555         erc = 0;\r
556         return(erc);\r
557 }\r