OpenCBM
PortAccessNt4.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 2004-2006 Spiro Trikaliotis
8  * Copyright 1998 Wolfgang Moser (http://d81.de)
9  *
10  * This file is heavily based on LPTDTC by Wolfgang Moser.
11  */
12 
22 #include <initguid.h>
23 #include <wdm.h>
24 #include "cbm_driver.h"
25 
26 
27 
33 #define AdvancedEPPTests 1
34 
36 enum lptMode
37 {
38  lptN_A = 0,
39  lptSPP = 1,
40  lptPS2 = 2,
41  lptEPP = 3,
42  lptECP = 4,
43  lptEPPc = 5,
44 };
45 
47 enum ecpMode
48 {
49  ecpNOECR = 0,
51  ecpSTNDRD = 1,
52  ecpBYTE = 2,
53  ecpSTDFIFO = 3,
54  ecpECPFIFO = 4,
55  ecpEPP = 5,
56  ecpRESVRD = 6,
57  ecpFIFOTST = 7,
58  ecpCONFIG = 8
59 };
60 
61 // set the Bidirectional-Flag to output (normal mode)
62 static void
63 BIDIoutp(PUCHAR port)
64 {
65  WRITE_PORT_UCHAR(port+2,0xC4);
66 }
67 
68 // set the Bidirectional-Flag to input (reverse mode)
69 static void
70 BIDIinp(PUCHAR port)
71 {
72  WRITE_PORT_UCHAR(port+2,0xE4);
73 }
74 
75 // clear the Timeout-Flag
76 static void
77 EPPclear(PUCHAR port)
78 {
79  unsigned char val=READ_PORT_UCHAR(port+1);
80 
81  // Reset Timeout-Flag by reading
82  WRITE_PORT_UCHAR(port+1,val|0x01); // or by writing 0
83  WRITE_PORT_UCHAR(port+1,val&0xFE); // or by writing 1 to it
84 }
85 
86 //----------------------------------------------------------------
87 // Port Detection Routines
88 //----------------------------------------------------------------
89 
90 // ECP port detection
91 static enum lptMode
92 ECPdetect(PUCHAR port)
93 {
94  enum lptMode ret = lptN_A;
95 
96  unsigned char ECR = READ_PORT_UCHAR(port+0x402)&~0x07;
97  do
98  {
99  WRITE_PORT_UCHAR(port+0x402,0x34);
100  WRITE_PORT_UCHAR(port+2,0xC6);
101  if(READ_PORT_UCHAR(port+0x402)!=0x35){
102  ECR=0xC4; // There's no ECR,
103  break;
104  }
105 
106  WRITE_PORT_UCHAR(port+0x402,0x35);
107  WRITE_PORT_UCHAR(port+0x402,0xd4);
108  READ_PORT_UCHAR(port+0x400);
109  WRITE_PORT_UCHAR(port+0x400,0xAA);
110  if(READ_PORT_UCHAR(port+0x400)!=0xAA)
111  break;
112 
113  WRITE_PORT_UCHAR(port+0x400,0x55);
114  if(READ_PORT_UCHAR(port+0x400)!=0x55)
115  break;
116 
117  ret=lptECP;
118 
119  } while(0);
120 
121  WRITE_PORT_UCHAR(port+0x402,0x35);
122  WRITE_PORT_UCHAR(port+0x402,ECR);
123  return ret;
124 }
125 
126 // EPP port detection without Control port initialisation
127 static enum lptMode
128 EPPdWOC(PUCHAR port)
129 {
130  do
131  {
132  EPPclear(port);
133  WRITE_PORT_UCHAR(port+3,0xAA);
134  EPPclear(port);
135  if(READ_PORT_UCHAR(port+3)!=0xAA)
136  break;
137 
138  EPPclear(port);
139  WRITE_PORT_UCHAR(port+3,0x55);
140  EPPclear(port);
141 
142  if(READ_PORT_UCHAR(port+3)!=0x55)
143  break;
144 
145  return lptEPP;
146 
147  } while(0);
148 
149  EPPclear(port);
150  WRITE_PORT_UCHAR(port+3,0x00);
151  READ_PORT_UCHAR(port+3);
152 
153  if(!(READ_PORT_UCHAR(port+1)&0x01))
154  return lptN_A;
155 
156  EPPclear(port);
157  if(READ_PORT_UCHAR(port+1)&0x01)
158  return lptN_A;
159 
160  return lptEPP;
161 }
162 
163 // set the Bidirectional-Flag to input (reverse mode) and block
164 // the DataStrobe, AddressStrobe and Write line manually, so
165 // that the EPP can't send any automatic handshake signal
166 static enum lptMode
167 EPPdetect(PUCHAR port)
168 {
169  WRITE_PORT_UCHAR(port+2, (UCHAR) 0xEF);
170  return EPPdWOC(port);
171 }
172 
173 // Parallel Printer Port detection (SPP or PS/2)
174 static enum lptMode
175 PPPdetect(PUCHAR port)
176 {
177  BIDIoutp(port);
178 
179  WRITE_PORT_UCHAR(port,0xAA);
180  if(READ_PORT_UCHAR(port)!=0xAA)
181  return lptN_A;
182 
183  WRITE_PORT_UCHAR(port,0x55);
184  if(READ_PORT_UCHAR(port)!=0x55)
185  return lptN_A;
186 
187  BIDIinp(port);
188 
189  WRITE_PORT_UCHAR(port,0xAA);
190  if(READ_PORT_UCHAR(port)!=0xAA)
191  return lptPS2;
192 
193  WRITE_PORT_UCHAR(port,0x55);
194  if(READ_PORT_UCHAR(port)!=0x55)
195  return lptPS2;
196 
197  return lptSPP;
198 }
199 
200 // perform a parallel printer port reset
201 static void
202 ResetLPT(PUCHAR port)
203 {
204  int i;
205 
206  // since a port read/write command is delayed by ISA bus waitstates to
207  // 1,6 ęs, we can use it for a simple system independent delay routine.
208 
209  // But it would be much better to program one of the system
210  // timers to delay 16 micro seconds
211  for(i=10;i>0;i--)
212  WRITE_PORT_UCHAR(port+2,0xC0);
213 
214  BIDIoutp(port);
215 }
216 
217 //----------------------------------------------------------------
218 // Port Mode Resolving Routines
219 //----------------------------------------------------------------
220 
221 #if AdvancedEPPTests
222 
230 static enum lptMode AdvEPP(PUCHAR port){
231  unsigned char EPPctrl=0;
232 
233  // check all control words to free up the EPP
234  do
235  {
236  EPPctrl |= 0x04; // don't do a reset
237  // write the special Control word, for freeing up the EPP
238  WRITE_PORT_UCHAR(port+2,EPPctrl);
239 
240  if (EPPdWOC(port)!=lptN_A)
241  return lptEPPc;
242 
243  EPPctrl++;
244  } while(EPPctrl);
245 
246  return lptN_A;
247 }
248 
249 // Resolving the special Control-Word to enable an EPP
250 static char *
251 EPPcontrol(PUCHAR port){
252  int ret;
253  static char ctrlWord[8];
254  unsigned int done0[8], done1[8];
255  unsigned char i,EPPctrl, OldCtrl,mask;
256 
257  for (i=0;i<8;i++)
258  done0[i]=done1[i]=0;
259 
260  OldCtrl = READ_PORT_UCHAR(port+2)&0x1f;
261  EPPctrl = 0; // check all control words to free up the EPP
262 
263  do
264  {
265  EPPctrl|=0x04; // don't do a reset
266  // write the special Control word, for freeing up the EPP
267  WRITE_PORT_UCHAR(port+2,EPPctrl);
268  if(EPPdWOC(port)!=lptN_A)
269  {
270  // EPP is enabled with this control word
271  // return EPPctrl;
272  for(i=0,mask=0x80;i<8;i++,mask>>=1)
273  {
274  if(EPPctrl&mask)
275  done1[i]++;
276  else
277  done0[i]++;
278  }
279  }
280  EPPctrl++;
281  } while(EPPctrl);
282 
283  WRITE_PORT_UCHAR(port+2,OldCtrl);
284 
285  for (i=0;i<8;i++)
286  {
287  if(done0[i]==done1[i])
288  {
289  if(!done0[i]) ctrlWord[i]='!'; // Control-Word could not found
290  else ctrlWord[i]='X'; // This Bit cares nobody
291  }
292  else if(!done0[i]) ctrlWord[i]='1'; // This Bit must be 1
293  else if(!done1[i]) ctrlWord[i]='0'; // This Bit must be 0
294  else ctrlWord[i]='?'; // This Bit depends on other Bits
295  }
296  return ctrlWord;
297 }
298 #else
299 static char *
300 EPPcontrol(PUCHAR)
301 {
302  return "XX0X0100";
303 }
304 #endif
305 
306 static enum lptMode
307 LPTmode(PUCHAR port)
308 {
309  enum lptMode ret=lptN_A;
310 
311  // check for valid portaddresses (LPT 1-6)
312  // valid port addresses only at 0x200, 0x204, 0x208, ..., 0x3fc
313 
314  if((((ULONG_PTR)port) & ~0x1fc)!=0x200)
315  return lptN_A;
316 
317  // if(!(port&0x07)){
318  // test for ECP/EPP only at 0/8-bases
319 
320  ResetLPT(port);
321  // ECP test doesn't touch any data registers, so no reset is needed
322 
323  do
324  {
325  // tests for an ECP
326  if((ret = ECPdetect(port)) != lptN_A)
327  break;
328 
329  // perform a reset to prevent printers from printing unusable stuff
330  ResetLPT(port);
331 
332  // tests for an EPP
333  if ((ret = EPPdetect(port)) != lptN_A)
334  break;
335 
336 #if AdvancedEPPTests
337  // tests for an EPP with different control words
338  if ((ret = AdvEPP(port)) != lptN_A)
339  break;
340 #endif
341  // tests for a SPP or PS/2
342  if ((ret = PPPdetect(port)) != lptN_A)
343  break;
344 
345  } while(0);
346  BIDIoutp(port);
347  return ret;
348 }
349 
350 static enum ecpMode
351 ECPmode(PUCHAR port)
352 {
353  if (LPTmode(port) != lptECP)
354  return ecpNOECR;
355 
356  return (enum ecpMode)(ecpSTNDRD + (READ_PORT_UCHAR(port+0x402)>>5));
357 }
358 
359 static enum ecpMode
360 SetECPmode(PUCHAR port, enum ecpMode mode)
361 {
362  unsigned char oldvalue;
363 
364  if (LPTmode(port) != lptECP)
365  return ecpNOECR;
366 
367  oldvalue = READ_PORT_UCHAR(port+0x402);
368 
369  WRITE_PORT_UCHAR(port+0x402, (oldvalue & 0x3f) | ((mode - ecpSTNDRD) << 5));
370 
371  return (enum ecpMode)(ecpSTNDRD + (oldvalue>>5));
372 }
373 
374 //----------------------------------------------------------------
375 // Some helper functions
376 //----------------------------------------------------------------
377 
378 static int
379 preprprt(PUCHAR port)
380 {
381  enum lptMode mode;
382  const char *modes[6]={"N/A","SPP","PS/2","EPP","ECP","EPPc"};
383 
384  FUNC_ENTER();
385 
386  mode=LPTmode(port);
387  DBG_PRINT((DBG_PREFIX "at 0x%03X, %s", port, modes[mode]));
388 
389  FUNC_LEAVE_INT(mode);
390 }
391 
392 static int
393 prport(PUCHAR port)
394 {
395  const char *ecpM[9]={
396  "no ECR found",
397  "Standard Mode",
398  "Byte Mode",
399  "Parallel Port FIFO Mode",
400  "ECP FIFO Mode",
401  "EPP Mode",
402  "Reserved",
403  "FIFO Test Mode",
404  "Configuration Mode"
405  };
406 
407  int ret;
408 
409  FUNC_ENTER();
410 
411  ret=1;
412 
413  switch (preprprt(port))
414  {
415  case lptN_A:
416  ret=0;
417  break;
418  case lptEPPc:
419  DBG_PRINT((DBG_PREFIX ", EPP-Enable-Timeout-Bit-Detection-Control-Word:"
420  " %s", EPPcontrol(port)));
421  break;
422 
423  case lptECP:
424  DBG_PRINT((DBG_PREFIX ", ECP-Mode: %s", ecpM[ECPmode(port)]));
425  }
426  DBG_PRINT((DBG_PREFIX ""));
427 
428  FUNC_LEAVE_INT(ret);
429 }
430 
431 
444 NTSTATUS
445 ParPortSetModeNt4(PDEVICE_EXTENSION Pdx)
446 {
447  NTSTATUS ntStatus = STATUS_SUCCESS;
448  enum lptMode lptmode = Pdx->HandleEcpEppMyself & 0xFF;
449  enum ecpMode oldecpmode = ecpNOECR;
450 
451  FUNC_ENTER();
452 
453  if (lptmode == lptN_A)
454  {
455  DBG_PRINT((DBG_PREFIX "Trying to find out mode of 0x%04x", Pdx->ParPortPortAddress));
456  lptmode = LPTmode(Pdx->ParPortPortAddress);
457  prport(Pdx->ParPortPortAddress);
458  }
459 
460  if (lptmode == lptECP)
461  {
462  oldecpmode = ECPmode(Pdx->ParPortPortAddress);
463 
464  DBG_PRINT((DBG_PREFIX "Setting ECP mode to BYTE mode"));
465  SetECPmode(Pdx->ParPortPortAddress, ecpBYTE);
466 
467  DBG_PRINT((DBG_PREFIX "Checking if mode has changed:"));
468  prport(Pdx->ParPortPortAddress);
469  }
470 
471  if (lptmode > lptSPP)
472  {
473  DBG_PRINT((DBG_PREFIX "Writing the value 0xE4 to 0x%04x", Pdx->ParPortPortAddress+2));
474  WRITE_PORT_UCHAR(Pdx->ParPortPortAddress+2,0xE4);
475  }
476 
477  Pdx->HandleEcpEppMyself = (lptmode & 0xFF) | ((oldecpmode & 0xFF) << 8);
478 
479  FUNC_LEAVE_NTSTATUS(ntStatus);
480 }
481 
495 NTSTATUS
496 ParPortUnsetModeNt4(PDEVICE_EXTENSION Pdx)
497 {
498  NTSTATUS ntStatus = STATUS_SUCCESS;
499  enum lptMode lptmode = Pdx->HandleEcpEppMyself & 0xFF;
500  enum ecpMode oldecpmode = (Pdx->HandleEcpEppMyself & 0xFF00) >> 8;
501 
502  FUNC_ENTER();
503 
504  if (lptmode == lptECP)
505  {
506  DBG_PRINT((DBG_PREFIX "Setting ECP mode back to standard mode"));
507  SetECPmode(Pdx->ParPortPortAddress, oldecpmode);
508 
509  DBG_PRINT((DBG_PREFIX "Checking if mode has changed:"));
510  prport(Pdx->ParPortPortAddress);
511  }
512 
513  FUNC_LEAVE_NTSTATUS(ntStatus);
514 }
515 
516 extern VOID
517 cbmiec_udelay(IN ULONG howlong); // howlong in ms!
518 
531 NTSTATUS
532 ParPortSetMode(PDEVICE_EXTENSION Pdx)
533 {
534  NTSTATUS ntStatus = STATUS_LOGON_FAILURE; // this is just a dummy failure value to allow falling into NT4 processing
535 
536  FUNC_ENTER();
537 
538  if (!Pdx->HandleEcpEppMyself)
539  {
540  // First, try to set the mode via WDM functions.
541  // Remember: Even if we install the NT4 driver, we might be running on 2000 or up.
542  ntStatus = ParPortSetModeWdm(Pdx);
543  }
544 
545  if (Pdx->HandleEcpEppMyself || ((ntStatus == STATUS_INVALID_PARAMETER) && Pdx->IsNT4))
546  {
547  ntStatus = ParPortSetModeNt4(Pdx);
548  cbmiec_udelay(1000 * 10);
549  }
550 
551  FUNC_LEAVE_NTSTATUS(ntStatus);
552 }
553 
567 NTSTATUS
568 ParPortUnsetMode(PDEVICE_EXTENSION Pdx)
569 {
570  NTSTATUS ntStatus;
571 
572  FUNC_ENTER();
573 
574  if (Pdx->HandleEcpEppMyself == 0)
575  {
576  ntStatus = ParPortUnsetModeWdm(Pdx);
577  }
578  else
579  {
580  ntStatus = ParPortUnsetModeNt4(Pdx);
581  }
582 
583  FUNC_LEAVE_NTSTATUS(ntStatus);
584 }
ecpMode
Definition: PortAccessNt4.c:47
lptMode
Definition: PortAccessNt4.c:36
#define FUNC_LEAVE_INT(_xxx)
Definition: debug.h:358
NTSTATUS ParPortUnsetMode(PDEVICE_EXTENSION Pdx)
Unset the operational mode of the parallel port.
VOID cbmiec_udelay(IN ULONG howlong)
Wait for a timeout.
Definition: libiec/util.c:66
NTSTATUS ParPortSetModeWdm(PDEVICE_EXTENSION Pdx)
Set the operational mode of the parallel port, WDM Version.
Definition: PortAccess.c:461
#define WRITE_PORT_UCHAR(_x_, _y_)
WRITE_PORT_UCHAR replacement for debugging.
Definition: i_iec.h:221
NTSTATUS ParPortSetMode(PDEVICE_EXTENSION Pdx)
Set the operational mode of the parallel port.
#define FUNC_ENTER()
Definition: debug.h:347
Definitions for the opencbm driver.
#define DBG_PREFIX
Definition: debug.h:320
NTSTATUS ParPortSetModeNt4(PDEVICE_EXTENSION Pdx)
Set the operational mode of the parallel port, NT4 version.
NTSTATUS ParPortUnsetModeNt4(PDEVICE_EXTENSION Pdx)
Unset the operational mode of the parallel port, NT4 version.
NTSTATUS ParPortUnsetModeWdm(PDEVICE_EXTENSION Pdx)
Unset the operational mode of the parallel port, WDM Version.
Definition: PortAccess.c:549
#define DBG_PRINT(_xxx)
Definition: debug.h:403
#define READ_PORT_UCHAR(_x_)
READ_PORT_UCHAR replacement for debugging.
Definition: i_iec.h:211