1 #include <windows.h>
2 #include <winioctl.h>
3 #include <stdio.h>
4 #include <stdlib.h> // strtoul()
5 #include <tchar.h> // _tcsdup()
6 EXTERN_C{
7 #include <hidsdi.h>
8 #include <hidpi.h>
9 }
10 #include <setupapi.h>
11 #include "usb2lpt.h"
12 #include "Redir.h"
13 #include "inpout32.h"
14
15 #include <conio.h>
16 #ifdef UNICODE
17 # define TS "S"
18 #else
19 # define TS "s"
20 #endif
21
22 // This source code relies on tabsize == 8
23
24 /*****************************
25 * True dynamic entry points *
26 *****************************/
27
28 #ifdef _M_IX86
29 // This header seems to require _stdcall (/Gz) compiler option!
30 #include <nt_vdd.h>
31
32 // Dynamic entries ensure compatibility to Windows NT 4.0 and possibly NT 3.51
33 struct _setupapi setupapi;
34 const char setupapi_names[]=
35 "setupapi\0"
36 "SetupDiEnumDeviceInterfaces\0" // Win2k++, Win98++
37 "SetupDiGetDeviceInterfaceDetailA\0";
38
39 struct _hid hid;
40 const char hid_names[]=
41 "hid\0" // Win2k++, Win98++
42 "HidD_GetHidGuid\0"
43 "HidD_GetPreparsedData\0"
44 "HidD_GetFeature\0"
45 "HidD_SetFeature\0"
46 "HidD_GetAttributes\0"
47 "HidP_GetCaps\0";
48
49 struct _krnl krnl;
50 const char krnl_names[]=
51 "kernel32\0"
52 "CreateRemoteThread\0"
53 "OpenThread\0"
54 "CancelIo\0" // Win2k++
55 "AddVectoredExceptionHandler\0"
56 "RemoveVectoredExceptionHandler\0"
57 "IsWow64Process\0" // WinXP++ 64bit
58 "Wow64DisableWow64FsRedirection\0"
59 "Wow64RevertWow64FsRedirection\0";
60
61 static struct _vdm{
62 HINSTANCE hLib;
63 BOOL (WINAPI*InstallIOHook)(HANDLE,WORD,PVDD_IO_PORTRANGE,PVDD_IO_HANDLERS);
64 VOID (WINAPI*DeInstallIOHook)(HANDLE,WORD,PVDD_IO_PORTRANGE);
65 }vdm;
66 static const char vdm_names[]=
67 "\0"
68 "VDDInstallIOHook\0"
69 "VDDDeInstallIOHook\0";
70
71 bool _fastcall dynaload(HMODULE &hLib,const char*e) {
72 if (hLib==INVALID_HANDLE_VALUE) return false; // already failed
73 if (!hLib && !(hLib=LoadLibraryA(e))) {
74 hLib--; // don't try again
75 return false; // failed
76 }
77 FARPROC *proc=(FARPROC*)&hLib;
78 bool ret=true;
79 while (++proc,*(e+=strlen(e)+1)) {
80 if (!*proc && !(*proc=GetProcAddress(hLib,e))) ret=false;
81 }
82 return ret; // true when all entry points are OK
83 }
84
85 static bool wdmHooked;
86 #endif
87
88 /*****************
89 * Debug Console *
90 *****************/
91
92 REDIR RedirInfo[9]; // 0..2: Automatic, for standard addresses, 3..9: Via OpenLpt()
93 struct _con{
94 HANDLE hOut;
95 CONSOLE_SCREEN_BUFFER_INFO csbi;
96 void Init();
97 void SetColor(BYTE c) const;
98 void SetGreenOrRed(BOOL) const;
99 void SetNormal() const;
100 }con;
101
102 void _con::Init() {
103 AllocConsole();
104 hOut=GetStdHandle(STD_OUTPUT_HANDLE);
105 GetConsoleScreenBufferInfo(hOut,&csbi);
106 }
107
108 // Sets given foreground color for lightgray text color, with high intensity.
109 // For other text colors, distinct foregrounds are produced.
110 void _con::SetColor(BYTE c) const{
111 SetConsoleTextAttribute(hOut,csbi.wAttributes
112 ^(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY)
113 ^c);
114 }
115
116 void _con::SetGreenOrRed(BOOL green) const{
117 SetColor(green?FOREGROUND_GREEN:FOREGROUND_RED);
118 }
119
120 void _con::SetNormal() const{
121 SetConsoleTextAttribute(hOut,csbi.wAttributes);
122 }
123
124 // returns a bit array, each of three bits for a changed LPT entry in BIOS data area 40:08 .. 40:10
125 // See Ralf Brown Interrupt List : Memory for meanings.
126 // Free slots (i.e. a non-available LPT2) are not checked to be free (zero).
127 // The BIOS data area is used by WGif12NT.exe, at least.
128 static char EnsureBiosDataArea() {
129 HANDLE h;
130 WORD*bios=(WORD*)MapPhysToLin((void*)0x408,0x0A,&h);
131 if (!bios) return -1;
132 char ret=0;
133 for (int i=0; i<3; i++) {
134 if (RedirInfo[i].where==1) {
135 if (bios[i]!=LOWORD(RedirInfo[i].addr)) {
136 bios[i]=LOWORD(RedirInfo[i].addr);
137 ret|=1<<i;
138 }
139 if (bios[4]>>14<i+1) { // Ensure that "Installed hardware" reflects the maximum parallel port number
140 bios[4]=bios[4]&0x3FFF|(i+1)<<14;
141 ret|=1<<i;
142 }
143 }
144 }
145 UnmapPhysicalMemory(h,bios);
146 return ret;
147 }
148
149 static void StartType(int typ) {
150 typ=setstarttype(typ);
151 static const char *starttypes[]={"BOOT","SYSTEM","AUTO","DEMAND","DISABLED"};
152 if ((unsigned)typ>SERVICE_DISABLED) {
153 con.SetColor(FOREGROUND_RED);
154 _cprintf("Error code %d getting/setting StartType value!",typ);
155 }else{
156 con.SetColor(FOREGROUND_GREEN|FOREGROUND_BLUE);
157 _cprintf("StartType = SERVICE_%s%s",starttypes[typ],typ!=SERVICE_DISABLED?"_START":"");
158 }
159 con.SetNormal();
160 _cprintf("\n\n");
161 }
162
163 static void ShowAssignment() {
164 int e=elemof(RedirInfo)-1;
165 for (;e>2;e--) if (RedirInfo[e].where) break;
166 for (int i=0; i<=e; i++) {
167 _cprintf("LPT%d: ",i+1);
168 switch (RedirInfo[i].where) {
169 case 0: _cprintf("free"); break;
170 case 1: _cprintf("SPP=0x%X, ECP=0x%X",LOWORD(RedirInfo[i].addr),HIWORD(RedirInfo[i].addr)); break;
171 case 2: _cprintf("USB2LPT (native, \\\\.\\%" TS ")",RedirInfo[i].name); break;
172 case 3: _cprintf("USB2LPT (HID, %.50" TS "...)",RedirInfo[i].name); break;
173 case 4: _cprintf("USB->Prn (%.50" TS "...)",RedirInfo[i].name); break;
174 // case 5: _cprintf("FT232 BitBang mode",RedirInfo[i].name); break;
175 // case 6: _cprintf("V-USB PowerSwitch",RedirInfo[i].name); break;
176 // case 7: _cprintf("UsbLotIo",RedirInfo[i].name); break;
177 default: _cprintf("unknown (%d)",RedirInfo[i].where);
178 }
179 _cprintf("\n");
180 }
181 BOOL ok=IsInpOutDriverOpen();
182 con.SetGreenOrRed(ok);
183 _cprintf("\nKernel-mode driver is %s\n\n",sysver<0?"not necessary.":ok?"loaded.":"NOT loaded!");
184 #ifdef _M_IX86
185 BOOL w64=IsXP64Bit();
186 if (sysver>=0 && !w64) {
187 con.SetGreenOrRed(wdmHooked);
188 _cprintf("DOS box & Win16 LPT redirection is %s\n\n",wdmHooked?"active.":"NOT active!");
189 }
190 #endif
191 if (ok) { // requires Win9x or loaded driver
192 char result=EnsureBiosDataArea();
193 con.SetGreenOrRed(!result);
194 if (!result) {
195 _cprintf("BIOS data area is OK.");
196 #ifdef _M_IX86
197 if (!w64) _cprintf(" (Not visible to DOS boxes.)");
198 #endif
199 _cprintf("\n");
200 }else if (result<0) _cprintf("BIOS data area NOT accessible!\n");
201 else for(int i=0; i<3; i++) {
202 if (result&1<<i) _cprintf("BIOS data area corrected for LPT%d, set to 0x%X.\n",i+1,LOWORD(RedirInfo[i].addr));
203 }
204 _cprintf("\n");
205 }
206 con.SetNormal();
207 if (sysver>=0) StartType(-1); // here: GetStartType
208 }
209
210 static bool VAL(char*&s, int&v) {
211 char*e;
212 while (*s==' ') s++; // skip leading spaces
213 v=strtoul(s,&e,16); // try to convert
214 if (e==s) return false; // Not a valid number (e.g. ';')
215 s=e; // Next start
216 if (*s==',') s++; // Skip one comma
217 return true;
218 }
219
220 static bool ProcessLine(HQUEUE &q, char *s) {
221 LARGE_INTEGER pf,tic,toc;
222 QueryPerformanceFrequency(&pf);
223 for (char c;c=*s++;) {
224 if (c<'?') continue;
225 int a=0,b=0;
226 if (isupper(c)) c=tolower(c);
227 switch (c) { // one-parameter commands
228 case 'o':
229 case 'i':
230 case 'l':
231 case 'd':
232 case 's': if (!VAL(s,a)) goto noparam;
233 }
234 switch (c) { // two-parameter commands
235 case 'o': if (!VAL(s,b)) goto noparam;
236 }
237 int i,l;
238 BYTE buf[256];
239 switch (c) {
240 case 'o': if (q) LptOut(q,a,b); else Out32((WORD)a,(BYTE)b); break;
241 case 'i': if (q) {if (!LptIn(q,a)) goto failed;} else {b=Inp32((WORD)a); _cprintf("%X\n",b);} break;
242 case 'l': if (q) _cprintf("Queue already open!\n"); else q=LptOpen(a,0); if (!q) _cprintf("Open failed!\n"); break;
243 case 'c': if (q) {LptClose(q); q=0;} else goto notopen; break;
244 case 'd': if (q) LptDelay(q,a); else goto notopen; break;
245 case 'u':
246 case 'e': if (q) {
247 for(i=0; i<sizeof buf; i++) {
248 if (!VAL(s,b)) break;
249 buf[i]=b;
250 }
251 QueryPerformanceCounter(&tic);
252 l=LptInOut(q,buf,i,buf,sizeof buf);
253 goto outbuf;
254 }goto notopen;
255 case 'f': if (q) {
256 QueryPerformanceCounter(&tic);
257 l=LptFlush(q,buf,sizeof buf);
258 outbuf:
259 QueryPerformanceCounter(&toc);
260 if (l<0) failed: _cprintf("Command `%c' failed!\n",c);
261 else if (l) {for (i=0; i<l; i++) _cprintf("%X ",buf[i]);_cprintf("\n");}
262 _cprintf("Execution time: %u us\n",MulDiv(toc.LowPart-tic.LowPart,1000000,pf.LowPart));
263 }else notopen: _cprintf("Not in queue mode!\n");
264 break;
265 case 'a': ShowAssignment(); break;
266 case 'q':
267 case 'x': return false; // let exit command-line processor
268 case 's': StartType(a); break;
269 case '?':
270 case 'h':
271 _cprintf(
272 "### InpOut32 debug console - all parameters are hexadecimal\n"
273 " ## Direct mode ## ## Queue mode ##\n"
274 "o a b OUT byte to address Enqueue OUT byte to offset\n"
275 "i a IN from address, emit byte Enqueue IN from offset\n"
276 "l n Open Queue for LPT n+1 -\n"
277 "d us - Enqueue delay in microseconds\n"
278 "e ... - Execute microcode, emit IN results\n"
279 "f - Flush Queue, emit IN results\n"
280 "c - Close Queue\n"
281 "a Show LPT assignment\n"
282 "%s"
283 "h,? This help\n"
284 "x,q Exit debug console\n\n",
285 sysver>=0?"s n Set service start type, n=2: AUTO_START, n=3: DEMAND_START\n":""); break;
286 case 0: noparam: _cprintf("Missing parameter for `%c'!\n",c); break;
287 default: _cprintf("Unknown command `%c'!\n",c);
288 }
289 }
290 return true; // let continue command-line processing
291 }
292
293 void InfoA (HWND Wnd, HINSTANCE, char *cmdLine, int) {
294 HQUEUE q=0;
295 con.Init();
296 ShowAssignment();
297 char buf[128];
298 buf[0]=126;
299 buf[1]=(char)strlen(cmdLine);
300 strncpy(buf+2,cmdLine,126); // prepare for editing
301 if (buf[1]) {
302 con.SetColor(FOREGROUND_RED|FOREGROUND_GREEN);
303 _cprintf("-%s\n",cmdLine);
304 con.SetNormal();
305 }
306 else _cprintf("Enter `q' for exiting.\n");
307 while (ProcessLine(q, buf+2)) {
308 con.SetColor(FOREGROUND_RED|FOREGROUND_GREEN);
309 // FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); doesn't help for the two-"-"-problem!
310 _cprintf("-"); // prompt (two loops for Win7/8-64, unknown reason)
311 _cgets(buf); // get line-editable command
312 con.SetNormal();
313 }
314 if (q) {_cprintf("Queue cleanup.\n"); LptClose(q);}
315 }
316
317 void InfoW(HWND Wnd, HINSTANCE hInst, PCWSTR cmdLine, int nCmdShow) {
318 char buf[128];
319 WideCharToMultiByte(CP_ACP,0,cmdLine,-1,buf,elemof(buf),NULL,NULL);
320 InfoA(Wnd,hInst,buf,nCmdShow);
321 }
322
323 /***************************************************************
324 ** LPT address redirector, fine for PCI and PCIexpress cards **
325 ***************************************************************/
326
327 WORD SppDef[3]={0x378,0x278,0x3BC};
328
329 // Change 0x378 to LOWORD(RedirInfo[0].addr), 0x778 to HIWORD(...), etc.
330 // 0x3BC (LPT3) is not exprected to have EPP/ECP registers, as this would break
331 // compatibility to some software accessing 0x3C0 (graphics card) or 0x7BC (unknown)
332 void patch(WORD &addr) {
333 for (int i=0;; i++) {
334 REDIR *info=RedirInfo+i;
335 if ((WORD)(addr-SppDef[i])<(i<2?8:4) && info->where==1 /*&& LOWORD(info->addr)*/) {
336 addr+=LOWORD(info->addr)-SppDef[i];
337 break;
338 }
339 if (i==2) break; // No ECP for 0x3BC, abort loop here
340 if ((WORD)(addr-SppDef[i]-0x400)<4 && info->where==1 && HIWORD(info->addr)) {
341 addr+=HIWORD(info->addr)-SppDef[i]-0x400;
342 break;
343 }
344 }
345 }
346
347 #ifdef _M_IX86
348 /**************************************************************
349 ** Redirection for DOS programs (i.e. NTVDM: DOS and Win16) **
350 **************************************************************/
351
352 /* DOS+Win16 programs will also get redirected port addresses, therefore,
353 * a DOS program doesn't need to know the PCI card's base address.
354 * Simply use 0x378 (378h, &H378, $378) for LPT1, etc.
355 * Redirection also occurs to USB->ParallelPrinter and USB2LPT adapters!
356 * Moreover, USB ports doesn't need administrative privilege for access.
357 * Note that ntvdm.exe only exists for x86 platform, not for amd64.
358 * If a 64-bit DosBox emulator behaves like ntvdm.exe (i.e. has same
359 * entry points), the #ifdef above should be removed.
360 *
361 * BUGBUG: ntvdm.exe seems to _always_ redirect access to the well-known
362 * LPT addresses (378, 278, 3BC) to printer somehow WITHOUT ANY WORKAROUND.
363 * (Except patching ntvdm.exe, of-course.)
364 * Therefore, this DLL creates an alias address range which is commonly
365 * free. This feature is only useful for programs where you can
366 * select a nonstandard port base address.
367 * To assist DOS+Win16 programs to fetch that aliases,
368 * this DLL patches the well-known BIOS data area for the ntvdm process.
369 */
370 VDD_IO_PORTRANGE portranges[6];
371 WORD nRanges;
372
373 static int setup_portranges() {
374 portranges[0].First=0x100; // Additional feature 141012:
375 portranges[0].Last=0x10A; // Alias port adresses for LPT1..LPT3
376 int k=1;
377 for(int i=0;; i++) {
378 REDIR *info=RedirInfo+i;
379 if (info->where) {
380 portranges[k].First=SppDef[i]-1;
381 portranges[k].Last=SppDef[i]+(i<2?7:3);// with / without EPP data
382 k++;
383 }
384 if (i==2) break;
385 if (info->where) {
386 portranges[k].First=SppDef[i]+0x400;
387 portranges[k].Last=SppDef[i]+0x407; // with ECP (and USB2LPT direction registers)
388 k++;
389 }
390 }
391 nRanges=(WORD)k;
392 return k;
393 }
394
395 static void WINAPI VddInbHandler(WORD addr,BYTE*data) {
396 if ((addr&~0xF)==0x100) { // ersatzweises LPT1-LPT3 für DOS-Box auf 100 (LPT1), 104 (LPT2), 108 (LPT3)
397 addr=(addr&3)+SppDef[addr>>2&3];
398 }
399 *data=Inp32(addr);
400 }
401
402 static void WINAPI VddOutbHandler(WORD addr, BYTE data) {
403 if ((addr&~0xF)==0x100) { // als Workaround für fix gecapture Druckerportadressen
404 addr=(addr&3)+SppDef[addr>>2&3];// (DataIO 2700 Programmiergerät)
405 }
406 Out32(addr,data);
407 }
408
409 void VddInit() {
410 static const VDD_IO_HANDLERS IOHandler={
411 VddInbHandler,NULL,NULL,NULL,
412 VddOutbHandler,NULL,NULL,NULL
413 };
414 vdm.hLib=GetModuleHandle(NULL); // get calling process (ntvdm.exe, or any third-party program that exports VDDInstallIoHook() etc.)
415 if (dynaload(vdm.hLib,vdm_names) // if not ntvdm.exe, this will fail because entries won't exist
416 && setup_portranges()
417 && vdm.InstallIOHook(hInstance,nRanges,portranges,(PVDD_IO_HANDLERS)&IOHandler)) {
418 _asm{
419 push es
420 push edi
421 mov di,0x40
422 mov es,di // access BIOS data area (of current process??) - still valid in Win32
423 mov edi,8
424 push 3
425 pop ecx
426 xor eax,eax
427 inc ah // 0x100
428 l1: stosw // patch 3 words
429 add eax,4
430 loop l1
431 pop edi
432 pop es
433 }
434 #ifdef _DEBUG
435 MessageBeep(MB_OK);
436 }else{
437 MessageBox(0,T("inpout32.dll: Failed to install IO Hook!"),NULL,MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
438 #endif
439 }
440 }
441
442 void VddDone() {
443 if (nRanges) vdm.DeInstallIOHook(hInstance,nRanges,portranges);
444 // MessageBeep(MB_ICONSTOP);
445 }
446
447 // Installs globally for all DOS programs, which is mostly intended
448 // This procedure requires administrative privileges on Vista or newer,
449 // otherwise, the registry access is redirected to a wrong place.
450 // (And RegOpenKeyEx() should fail silently in this case.)
451 void InstallVdmHook() {
452 TCHAR self[MAX_PATH],c;
453 GetModuleFileName(hInstance,self,elemof(self));
454 c=self[3];
455 self[3]=0;
456 if (GetDriveType(self)!=DRIVE_FIXED) return; // ab XP?
457 self[3]=c;
458 HKEY hKey;
459 if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,T("System\\CurrentControlSet\\Control\\VirtualDeviceDrivers"),0,
460 KEY_QUERY_VALUE|KEY_SET_VALUE,&hKey)) {
461 TCHAR *selfname=_tcsrchr(self,'\\')+1; // "inpout32.dll" or "inpoutx64.dll" - or somehow renamed
462 TCHAR buf[1500],*p=buf,*q,*r,*e;
463 DWORD len=sizeof(buf);
464 if (!RegQueryValueEx(hKey,T("VDD"),NULL,NULL,(PBYTE)buf,&len)) {
465 e=buf+len/sizeof(TCHAR); // behind double-zero
466 for (p=buf; *p; p=q) { // traverse each entry
467 if (!lstrcmpi(p,self)) {wdmHooked=true; goto raus;} // already installed, do nothing
468 q=p+_tcslen(p)+1; // next entry
469 r=_tcsrchr(p,'\\'); // filename portion
470 if (!r) r=_tcsrchr(p,'/'); // possibly UNIX style here
471 if (r) r++; else r=p; // with no path, take entire entry as file name
472 if (!lstrcmpi(r,selfname)) { // this DLL on another place?
473 memmove(p,q,(BYTE*)e-(BYTE*)q); // Remove this entry! (Move trailing data to lower addresses)
474 e-=q-p; // Adjust end address
475 q=p; // Adjust next-entry address
476 continue;
477 }
478 }
479 }
480 lstrcpyn(p,self,buf+elemof(buf)-p-1); // Append
481 e=p+_tcslen(p)+1;
482 *e++=0; // now double-terminated
483 if (!RegSetValueEx(hKey,T("VDD"),0,REG_MULTI_SZ,(PBYTE)buf,(e-buf)*sizeof(TCHAR))) wdmHooked=true;
484 raus:
485 RegCloseKey(hKey);
486 }
487 }
488 #endif
489
490 // Dummy entry! DLL initialization is the workhorse.
491 BOOL WINAPI VDDInitialize(HANDLE DllHandle,ULONG Reason,PCONTEXT Context) {
492 return TRUE;
493 }
494
495 /*****************************************************
496 ** Automated full redirector for Inp32() / Out32() **
497 *****************************************************/
498
499 // Fill residual free slots of RedirInfo[]
500 static bool InsertDevice(BYTE where, PTSTR name) {
501 for (int i=0; i<elemof(RedirInfo); i++) {
502 if (!RedirInfo[i].where) { // found free slot
503 RedirInfo[i].where=where;
504 RedirInfo[i].name=_tcsdup(name);
505 return true;
506 }
507 }
508 return false; // couldn't insert
509 }
510
511 static bool AddUsb2Lpt(PTSTR name) {
512 // As FindDevices will enumerate all HID devices here
513 // (at least non-mice, non-keyboards, and non-joysticks)
514 // filter for USB2LPT devices here
515 bool ret=true; // let continue enumeration
516 HANDLE h=CreateFile(name,0,0,NULL,OPEN_EXISTING,0,NULL);
517 if (h!=INVALID_HANDLE_VALUE) {
518 HIDD_ATTRIBUTES attr;
519 if (HidD_(GetAttributes(h,&attr))) {
520 if (attr.VendorID==0x16C0 // VOTI (5824)
521 && (WORD)(attr.ProductID-1715)<5) { // siphec -> h#s
522 ret=InsertDevice(3,name);
523 }
524 }
525 CloseHandle(h);
526 }
527 return ret;
528 }
529
530 static bool AddUsbPrn(PTSTR name) {
531 // Take any USB->ParallelPrinter adapter
532 return InsertDevice(4,name);
533 }
534
535 static void FindDevices(const GUID*guid, bool(*FilterProc)(PTSTR)) {
536 HDEVINFO devs;
537 devs=SetupDiGetClassDevs(guid,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
538 if (devs!=INVALID_HANDLE_VALUE) {
539 SP_DEVICE_INTERFACE_DATA devinterface;
540 devinterface.cbSize=sizeof(SP_DEVICE_INTERFACE_DATA);
541 for (int i=0; SetupDi(EnumDeviceInterfaces(devs,NULL,guid,i,&devinterface)); i++) {
542 SP_DEVINFO_DATA devinfo;
543 struct{
544 SP_DEVICE_INTERFACE_DETAIL_DATA id;
545 TCHAR space[MAX_PATH];
546 }id;
547 devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
548 id.id.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
549 if (SetupDi(GetDeviceInterfaceDetail(devs, &devinterface, &id.id, sizeof(id), NULL, &devinfo))) {
550 if (!FilterProc(id.id.DevicePath)) break;
551 }
552 }
553 SetupDiDestroyDeviceInfoList(devs);
554 }
555 }
556
557 void InitRedirector(void) {
558 // Traverse true parallel ports and USB2LPT devices first
559 DWORD a[9];
560 memset(a,0,sizeof(a));
561 LptGetAddr(a,elemof(a)); // Collect true LPT ports
562 for (int n=0; n<9; n++) {
563 if (a[n]) {
564 RedirInfo[n].where=1;
565 RedirInfo[n].addr=a[n];
566 continue;
567 }
568 TCHAR s[16];
569 _sntprintf(s,elemof(s),T("LPT%d"),n+1);
570 HANDLE hDev=CreateFile(s,0,0,NULL,OPEN_EXISTING,0,NULL);
571 if (hDev!=INVALID_HANDLE_VALUE) {
572 DWORD e,bw; // Check whether it's a USB2LPT
573 if (DeviceIoControl(hDev,IOCTL_VLPT_GetLastError,NULL,0,&e,sizeof(e),&bw,NULL)) {
574 RedirInfo[n].where=2;
575 RedirInfo[n].name=_tcsdup(s);
576 }
577 CloseHandle(hDev);
578 }
579 }
580 // Fill gaps with available USB2LPT (HID) and USB->PRN adapters
581 if (!dynaload(setupapi.hLib,setupapi_names)) return;
582 if (!dynaload(hid.hLib,hid_names)) return;
583 dynaload(krnl.hLib,krnl_names); // load CancelIo() - not available on Win95
584 // Typically, a PCI card defaults to LPT3, so a USB->PRN adapter can be used as LPT2 here
585 GUID guid;
586 HidD_(GetHidGuid(&guid));
587 FindDevices(&guid,AddUsb2Lpt);
588 static const GUID GUID_DEVINTERFACE_USBPRINT = {
589 0x28d78fad,0x5a12,0x11D1,0xae,0x5b,0x00,0x00,0xf8,0x03,0xa8,0xc2};
590 FindDevices(&GUID_DEVINTERFACE_USBPRINT,AddUsbPrn);
591 }
592
593 static QUEUE* RedirQueue[3];
594
595 static QUEUE* RedirCandidate(WORD addr, BYTE &offset) {
596 for (int i=0; i<3; i++) {
597 if (RedirInfo[i].where>=2) {
598 WORD diff=addr-SppDef[i];
599 if ((WORD)(diff&~0x400)<8) {
600 if (!RedirQueue[i]) RedirQueue[i]=LptOpen(i,0);
601 if (!RedirQueue[i]) return false;
602 offset=(BYTE)diff | diff>>7&8;
603 return RedirQueue[i];
604 }
605 }
606 }
607 return NULL;
608 }
609
610 bool RedirOut(WORD addr, BYTE data) {
611 BYTE ucode[2];
612 QUEUE *q=RedirCandidate(addr,ucode[0]);
613 if (!q) return false;
614 ucode[1]=data;
615 LptInOut(q,ucode,2,NULL,0); // no result checking here
616 return true;
617 }
618
619 bool RedirIn(WORD addr, BYTE &data) {
620 QUEUE *q=RedirCandidate(addr,data);
621 if (!q) return false;
622 data|=0x10; // IN instruction
623 LptInOut(q,&data,1,&data,1);
624 return true;
625 }
626
627 BOOL ExitPortTalk() {
628 for (int i=0; i<elemof(RedirQueue); i++) {
629 LptClose(RedirQueue[i]);
630 RedirQueue[i]=0;
631 }
632 return TRUE;
633 }
634
Detected encoding: UTF-8 | 0
|