OpenCBM
i_opencbm.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 1999-2001 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8  * Copyright 2001-2004, 2007-2009, 2011 Spiro Trikaliotis
9  *
10  * Parts are Copyright
11  * Jouko Valta <jopi(at)stekt(dot)oulu(dot)fi>
12  * Andreas Boose <boose(at)linux(dot)rz(dot)fh-hannover(dot)de>
13 */
14 
26 #include <windows.h>
27 #include <windowsx.h>
28 
30 #define DBG_USERMODE
31 
33 #ifndef DBG_PROGNAME
34  #define DBG_PROGNAME "OPENCBM-XA1541.DLL"
35 #endif // #ifndef DBG_PROGNAME
36 
37 #include "debug.h"
38 
39 #include <winioctl.h>
40 #include "cbmioctl.h"
41 
42 #include <stdlib.h>
43 #include <stddef.h>
44 
45 #include "i_opencbm.h"
46 
47 #include "version.h"
48 
49 #define OPENCBM_PLUGIN
51 #include "archlib.h"
52 
53 
54 /*-------------------------------------------------------------------*/
55 /*--------- REGISTRY FUNCTIONS --------------------------------------*/
56 
70 static int
71 cbm_get_default_port(VOID)
72 {
73  DWORD ret;
74  HKEY regKey;
75 
76  FUNC_ENTER();
77 
78  DBG_PPORT((DBG_PREFIX "cbm_get_default_port()"));
79 
80  // Open a registry key to HKLM<%REGKEY_SERVICE%>
81 
82  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
84  0,
85  KEY_QUERY_VALUE,
86  &regKey)
87  )
88  {
89  DBG_WARN((DBG_PREFIX "RegOpenKeyEx() failed!"));
90  FUNC_LEAVE_BOOL(FALSE);
91  }
92 
93  // now, get the number of the port to use
94 
95  if (RegGetDWORD(regKey, CBM_REGKEY_SERVICE_DEFAULTLPT, &ret) != ERROR_SUCCESS)
96  {
98  " value, setting 0."));
99  ret = 0;
100  }
101 
102  // We're done, close the registry handle.
103 
104  RegCloseKey(regKey);
105 
106  DBG_PPORT((DBG_PREFIX "RETURN: cbm_get_default_port() == %u", ret));
107 
108  FUNC_LEAVE_INT(ret);
109 }
110 
111 /*-------------------------------------------------------------------*/
112 /*--------- ASYNCHRONOUS IO FUNCTIONS -------------------------------*/
113 
117 static HANDLE CancelEvent = NULL;
118 static HANDLE CancelCallbackEvent = NULL;
119 static ULONG InCancellableState = 0;
120 
128 VOID
130 {
131  FUNC_ENTER();
132 
133  //
134  // Create the events for prematurely cancelling I/O request
135  //
136 
137  CancelEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
138  DBG_ASSERT(CancelEvent != NULL);
139 
140  CancelCallbackEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
141  DBG_ASSERT(CancelCallbackEvent != NULL);
142 
143  FUNC_LEAVE();
144 }
145 
157 VOID
159 {
160  FUNC_ENTER();
161 
162  //
163  // Delete the event which is used for prematurely
164  // cancelling I/O request
165  //
166  if (CancelEvent != NULL)
167  CloseHandle(CancelEvent);
168 
169  FUNC_LEAVE();
170 }
171 
178 VOID
180 {
181  FUNC_ENTER();
182 
183  if (InterlockedExchange(&InCancellableState, 0) != 0)
184  {
185  //
186  // signal the event which is used for prematurely
187  // cancelling I/O request
188  //
189 
190  SetEvent(CancelEvent);
191 
192  //
193  // Wait to be signalled that the current I/O request
194  // has been cancelled.
195  //
196 
197  WaitForSingleObject(CancelCallbackEvent, INFINITE);
198  }
199 
200  FUNC_LEAVE();
201 }
202 
216 VOID
217 WaitForIoCompletionConstruct(LPOVERLAPPED Overlapped)
218 {
219  FUNC_ENTER();
220 
221  memset(Overlapped, 0, sizeof(*Overlapped));
222  Overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
223 
224  DBG_ASSERT(Overlapped->hEvent != NULL);
225 
226  FUNC_LEAVE();
227 }
228 
257 BOOL
258 WaitForIoCompletion(BOOL Result, CBM_FILE HandleDevice, LPOVERLAPPED Overlapped, DWORD *BytesTransferred)
259 {
260  BOOL result = Result;
261  HANDLE handleList[2] = { CancelEvent, Overlapped->hEvent };
262 
263  FUNC_ENTER();
264 
265  DBG_ASSERT(Overlapped != NULL);
266  DBG_ASSERT(BytesTransferred != NULL);
267 
268  if (!Result)
269  {
270  // deal with the error code
271  switch (GetLastError())
272  {
273  case ERROR_IO_PENDING:
274  {
275  int tmp;
276 
277  //
278  // Make sure WaitForIoCompletionCancelAll() knows it has to signal us
279  //
280 
281  tmp = InterlockedExchange(&InCancellableState, 1);
282  DBG_ASSERT(tmp == 0);
283 
284  //
285  // wait for the operation to finish
286  //
287 
288  if (WaitForMultipleObjects(2, handleList, FALSE, INFINITE) == WAIT_OBJECT_0)
289  {
290  CancelIo(HandleDevice);
291 
292  // we are told to cancel this event
293  *BytesTransferred = 0;
294  result = FALSE;
295  SetEvent(CancelCallbackEvent);
296  }
297  else
298  {
299  //
300  // WaitForIoCompletionCancelAll() does not need to alert us anymore
301  //
302 
303  if (InterlockedExchange(&InCancellableState, 0) == 0)
304  {
305  //
306  // In case we were signalled, make sure
307  // WaitForIoCompletionCancelAll() does not hang
308  //
309 
310  SetEvent(CancelCallbackEvent);
311  }
312 
313  // check on the results of the asynchronous read
314  result = GetOverlappedResult(HandleDevice, Overlapped,
315  BytesTransferred, FALSE) ;
316  }
317  break;
318  }
319  }
320  }
321 
322  CloseHandle(Overlapped->hEvent);
323 
324  FUNC_LEAVE_BOOL(result ? TRUE : FALSE);
325 }
326 
327 /*-------------------------------------------------------------------*/
328 /*--------- OPENCBM ARCH FUNCTIONS ----------------------------------*/
329 
346 const char * CBMAPIDECL
347 opencbm_plugin_get_driver_name(const char * const Port)
348 {
350  static char driverName[] = "\\\\.\\opencbm0";
351  char *ret;
352 
353  int portNumber = 0;
354 
355  FUNC_ENTER();
356 
357  ret = NULL;
358 
359  if (Port != NULL)
360  {
361  portNumber = strtoul(Port, NULL, 10);
362  }
363 
368  if (portNumber <= 10)
369  {
370  if (portNumber == 0)
371  {
372  // PortNumber 0 has special meaning: Find out the default value
373 
374  portNumber = cbm_get_default_port();
375  }
376 
377  driverName[strlen(driverName)-1] = (portNumber ? portNumber-1 : 0) + '0';
378  ret = driverName;
379  }
380 
381  FUNC_LEAVE_STRING(ret);
382 }
383 
404 int CBMAPIDECL
405 opencbm_plugin_driver_open(CBM_FILE *HandleDevice, const char * const Port)
406 {
407  const char *driverName;
408 
409  FUNC_ENTER();
410 
411  // Get the name of the driver to be opened
412 
413  driverName = opencbm_plugin_get_driver_name(Port);
414 
415  if (driverName == NULL)
416  {
417  // there was a problem, thus, fail this call!
418 
419  *HandleDevice = INVALID_HANDLE_VALUE;
420  }
421  else
422  {
423  // Open the device
424 
425  *HandleDevice = CreateFile(driverName,
426  GENERIC_READ | GENERIC_WRITE, // we need read and write access
427  0,
428  NULL,
429  OPEN_EXISTING,
430  FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
431  NULL);
432  }
433 
434  DBG_ASSERT(*HandleDevice != INVALID_HANDLE_VALUE);
435 
436  FUNC_LEAVE_INT(*HandleDevice == INVALID_HANDLE_VALUE);
437 }
438 
453 void CBMAPIDECL
455 {
456  FUNC_ENTER();
457 
458  DBG_ASSERT(HandleDevice != 0);
459 
460  CloseHandle(HandleDevice);
461 
462  FUNC_LEAVE();
463 }
464 
505 BOOL
506 cbm_ioctl(IN CBM_FILE HandleDevice, IN DWORD ControlCode,
507 #if DBG
508  IN char *TextControlCode,
509 #endif // #if DBG
510  IN PVOID InBuffer, IN ULONG InBufferSize, OUT PVOID OutBuffer, IN ULONG OutBufferSize)
511 {
512  OVERLAPPED overlapped;
513  DWORD dwBytesReturned;
514 
515  BOOL returnValue;
516 
517  FUNC_ENTER();
518 
519  WaitForIoCompletionConstruct(&overlapped);
520 
521  // Perform the IOCTL
522 
523  returnValue = DeviceIoControl(HandleDevice, ControlCode, InBuffer, InBufferSize,
524  OutBuffer, OutBufferSize, &dwBytesReturned, &overlapped);
525 
526  returnValue = WaitForIoCompletion(returnValue, HandleDevice, &overlapped, &dwBytesReturned);
527 
528  // If an error occurred, output it (in the DEBUG version of this file
529 
530  if (!returnValue)
531  {
532  DBG_ERROR((DBG_PREFIX "%s: Error code = %u", TextControlCode, GetLastError()));
533  }
534  else
535  {
536  // Check if the number of bytes returned equals the wanted number
537 
538  if (dwBytesReturned != OutBufferSize)
539  {
540  DBG_WARN((DBG_PREFIX "%s: OutBufferSize = %u, but dwBytesReturned = %u",
541  TextControlCode, OutBufferSize, dwBytesReturned));
542  }
543  }
544 
545  FUNC_LEAVE_BOOL(returnValue);
546 }
547 
548 /*-------------------------------------------------------------------*/
549 /*--------- DEVICE DRIVER HANDLING FUNCTIONS -----------------------*/
550 
551 
563 BOOL
565 {
566  SC_HANDLE schManager;
567  SC_HANDLE schService;
568  DWORD err;
569  BOOL ret;
570 
571  FUNC_ENTER();
572 
573  ret = TRUE;
574 
575  schManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
576 
577  if (schManager == NULL)
578  {
579  DBG_ERROR((DBG_PREFIX "Could not open service control manager!"));
580  FUNC_LEAVE_BOOL(FALSE);
581  }
582 
583  schService = OpenService(schManager, OPENCBM_DRIVERNAME, SERVICE_ALL_ACCESS);
584 
585  if (schService == NULL)
586  {
587  DBG_ERROR((DBG_PREFIX "OpenService (0x%02x)", GetLastError()));
588  FUNC_LEAVE_BOOL(FALSE);
589  }
590 
591  ret = StartService(schService, 0, NULL);
592 
593  if (ret)
594  {
595  DBG_SUCCESS((DBG_PREFIX "StartService"));
596  }
597  else
598  {
599  err = GetLastError();
600 
601  if (err == ERROR_SERVICE_ALREADY_RUNNING)
602  {
603  /* If the service is already running, don't treat it
604  * as an error, but as a warning, and report success!
605  */
606 
607  DBG_WARN((DBG_PREFIX "StartService, (ERROR_SERVICE_ALREADY_RUNNING)"));
608 
609  ret = TRUE;
610  }
611  else
612  {
613  DBG_ERROR((DBG_PREFIX "StartService (0x%02x)", err));
614  }
615  }
616 
617  CloseServiceHandle(schService);
618 
619  // Close the SCM: We don't need it anymore
620 
621  CloseServiceHandle(schManager);
622 
623  FUNC_LEAVE_BOOL(ret);
624 }
625 
626 
638 BOOL
640 {
641  SERVICE_STATUS serviceStatus;
642  SC_HANDLE schManager;
643  SC_HANDLE schService;
644  BOOL ret;
645 
646  FUNC_ENTER();
647 
648  schManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
649 
650  schService = OpenService(schManager, OPENCBM_DRIVERNAME, SERVICE_ALL_ACCESS);
651 
652  if (schManager == NULL)
653  {
654  DBG_ERROR((DBG_PREFIX "Could not open service control manager!"));
655  FUNC_LEAVE_BOOL(FALSE);
656  }
657 
658  if (schService == NULL)
659  {
660  DBG_ERROR((DBG_PREFIX "OpenService (0x%02x)", GetLastError()));
661  FUNC_LEAVE_BOOL(FALSE);
662  }
663 
664  ret = ControlService(schService, SERVICE_CONTROL_STOP, &serviceStatus);
665 
666  if (ret)
667  {
668  DBG_SUCCESS((DBG_PREFIX "ControlService"));
669  }
670  else
671  {
672  DBG_ERROR((DBG_PREFIX "ControlService (0x%02x)", GetLastError()));
673  }
674 
675  CloseServiceHandle(schService);
676 
677  // Close the SCM: We don't need it anymore
678 
679  CloseServiceHandle(schManager);
680 
681  FUNC_LEAVE_BOOL(ret);
682 }
683 
703 BOOL
704 cbm_driver_install(OUT PULONG Buffer, IN ULONG BufferLen)
705 {
706  PCBMT_I_INSTALL_OUT outBuffer;
707  CBM_FILE HandleDevice;
708 
709  FUNC_ENTER();
710 
711  DBG_ASSERT(Buffer != NULL);
712  DBG_ASSERT(BufferLen >= sizeof(ULONG));
713 
714  outBuffer = (PCBMT_I_INSTALL_OUT) Buffer;
715 
716  DBG_ASSERT(outBuffer != NULL);
717 
718  if (opencbm_plugin_driver_open(&HandleDevice, 0) == 0)
719  {
721  cbm_ioctl(HandleDevice, CBMCTRL(I_INSTALL), NULL, 0, Buffer, BufferLen);
722  opencbm_plugin_driver_close(HandleDevice);
723  }
724  else
725  {
727  DBG_ERROR((DBG_PREFIX "Driver could not be openend"));
728  }
729 
730  // if there is room in the given buffer, set the dll version
731 
732  if (BufferLen >= sizeof(outBuffer->DllVersion) + offsetof(CBMT_I_INSTALL_OUT, DllVersion))
733  {
734  outBuffer->DllVersion =
735  CBMT_I_INSTALL_OUT_MAKE_VERSION(OPENCBM_VERSION_MAJOR, OPENCBM_VERSION_MINOR,
736  OPENCBM_VERSION_SUBMINOR, OPENCBM_VERSION_DEVEL);
737  }
738 
739  // if there is even room for the dll version extension, set the dll version extension
740 
741  if (BufferLen >= sizeof(outBuffer->DllVersionEx) + offsetof(CBMT_I_INSTALL_OUT, DllVersionEx))
742  {
743  outBuffer->DllVersionEx =
744  CBMT_I_INSTALL_OUT_MAKE_VERSION_EX(OPENCBM_VERSION_PATCHLEVEL);
745  }
746 
749  && (outBuffer->ErrorFlags != CBM_I_DRIVER_INSTALL_0_FAILED)
750  ) ? FALSE : TRUE);
751 }
752 
762 BOOL
764 {
765  DWORD ret;
766  BOOL automaticStart;
767  HKEY regKey;
768 
769  FUNC_ENTER();
770 
771  automaticStart = FALSE;
772 
773  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
775  0,
776  KEY_QUERY_VALUE,
777  &regKey)
778  )
779  {
780  DBG_WARN((DBG_PREFIX "RegOpenKeyEx() failed!"));
781  FUNC_LEAVE_BOOL(FALSE);
782  }
783 
784  // now, get the number of the port to use
785 
786  if (RegGetDWORD(regKey, "Start", &ret) != ERROR_SUCCESS)
787  {
788  DBG_ERROR((DBG_PREFIX "No " CBM_REGKEY_SERVICE "\\Start value!"));
789  }
790  else
791  {
792  if (ret == SERVICE_AUTO_START)
793  {
794  automaticStart = TRUE;
795  }
796  }
797 
798  // We're done, close the registry handle.
799 
800  RegCloseKey(regKey);
801 
802  FUNC_LEAVE_BOOL(automaticStart);
803 }
#define CBM_I_DRIVER_INSTALL_0_FAILED
CBMCTRL_I_INSTALL: The driver could not be opened.
Definition: cbmioctl.h:352
void CBMAPIDECL opencbm_plugin_driver_close(CBM_FILE HandleDevice)
Closes the driver.
Definition: i_opencbm.c:454
const char *CBMAPIDECL opencbm_plugin_get_driver_name(const char *const Port)
Get the name of the driver for a specific parallel port.
Definition: i_opencbm.c:347
int CBMAPIDECL opencbm_plugin_driver_open(CBM_FILE *HandleDevice, const char *const Port)
Opens the driver.
Definition: i_opencbm.c:405
#define DBG_WARN(_xxx)
Definition: debug.h:395
#define FUNC_LEAVE_INT(_xxx)
Definition: debug.h:358
VOID WaitForIoCompletionCancelAll(VOID)
Cancel any running WaitForIoCompletion()
Definition: i_opencbm.c:179
Define makros for debugging purposes.
VOID WaitForIoCompletionInit(VOID)
Initialize WaitForIoCompletion()
Definition: i_opencbm.c:129
#define CBMAPIDECL
Definition: opencbm.h:85
#define CBM_I_DRIVER_INSTALL_0_IOCTL_FAILED
CBMCTRL_I_INSTALL: The IOCTL failed.
Definition: cbmioctl.h:354
#define CBM_REGKEY_SERVICE
Definition: cbmioctl.h:38
#define CBMCTRL(_x_)
Definition: i_opencbm.h:37
LONG RegGetDWORD(IN HKEY RegKey, IN char *SubKey, OUT LPDWORD Value)
Get a DWORD value from the registry.
Definition: registry.c:54
#define OPENCBM_DRIVERNAME
Definition: cbmioctl.h:59
Define the IOCTL codes for the opencbm driver.
#define CBM_REGKEY_SERVICE_DEFAULTLPT
Definition: cbmioctl.h:42
#define FUNC_LEAVE()
Definition: debug.h:349
BOOL cbm_driver_install(OUT PULONG Buffer, IN ULONG BufferLen)
Complete driver installation, "direct version".
Definition: i_opencbm.c:704
#define CBM_FILE
Definition: opencbm.h:87
#define CBMT_I_INSTALL_OUT_MAKE_VERSION(_x, _y, _z, _w)
Definition: cbmioctl.h:155
#define DBG_ASSERT(_xxx)
Definition: debug.h:401
#define FUNC_LEAVE_BOOL(_xxx)
Definition: debug.h:354
#define CBMT_I_INSTALL_OUT_MAKE_VERSION_EX(_w)
Definition: cbmioctl.h:163
#define DBG_ERROR(_xxx)
Definition: debug.h:397
#define DBG_PPORT(_xxx)
Definition: debug.h:391
#define DBG_SUCCESS(_xxx)
Definition: debug.h:393
VOID WaitForIoCompletionConstruct(LPOVERLAPPED Overlapped)
Boilerplate code for asynchronous I/O requests.
Definition: i_opencbm.c:217
Defining OpenCBM version.
BOOL cbm_ioctl(IN CBM_FILE HandleDevice, IN DWORD ControlCode, IN char *TextControlCode, IN PVOID InBuffer, IN ULONG InBufferSize, OUT PVOID OutBuffer, IN ULONG OutBufferSize)
Perform an ioctl on the driver.
Definition: i_opencbm.c:506
#define FUNC_ENTER()
Definition: debug.h:347
#define DBG_PREFIX
Definition: debug.h:320
#define FUNC_LEAVE_STRING(_xxx)
Definition: debug.h:368
BOOL cbm_driver_stop(VOID)
Stop a device driver.
Definition: i_opencbm.c:639
BOOL cbm_driver_start(VOID)
Start a device driver.
Definition: i_opencbm.c:564
Internal API for opencbm installation.
VOID WaitForIoCompletionDeinit(VOID)
Uninitialize WaitForIoCompletion()
Definition: i_opencbm.c:158
BOOL WaitForIoCompletion(BOOL Result, CBM_FILE HandleDevice, LPOVERLAPPED Overlapped, DWORD *BytesTransferred)
Wait for the completion of an I/O operation.
Definition: i_opencbm.c:258
BOOL IsDriverStartedAutomatically(VOID)
Is the driver started automatically?
Definition: i_opencbm.c:763