Source file: /~heha/hs/dlo/DLO_UsbPrn.zip/src/DLO_UsbPrn.c

/* Discolitez plugin, for controlling eight channels via
 * a regular usb printer adapter
 * Requires following connections, besides such an adapter:
 * - Connection of /STB (1) and /ACK (10) for handshake
 * - Connection of BSY (11) with GND (22)
 * - Connection of PE (12) with GND (23)
 * - Connection of /ERR (15) with /INI (16) supplying HIGH level
 * - Catching data with a 8-channel flip-flop (e.g. 74HC574),
 *   CLOCK = STB (1), Ucc = /INI (16), GND = /OE = GND (21)
 * Some light controllers may have these connections built-in,
 * for emulating a regular printer.
 *
 * This plugin uses the first USB-Printer adapter found by default.
 * It takes the number of the adapter from DLL file name,
 * e.g. "DLO_UsbPrn 1.dll" uses the second UsbPrn found.
 *
 * USB->Printer adapters may behave slightly differently!
 * no-name - Prolific PL-2305H: /SELECT=L, /AF=H, ONLINE not checked
 * LogiLink - WCH CH340S: /SELECT=toggling, /AF=toggling, ONLINE not checked
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
#include <shlwapi.h>
#include <usb.h>
#include <usbiodef.h>
#include <usbprint.h>
#include <setupapi.h>
#include <devguid.h>
#include <wdmguid.h>
#include <winioctl.h>


/* This define is required so that the GUID_DEVINTERFACE_USBPRINT variable is
 * declared an initialised as a static locally, since windows does not include it in any
 * of its libraries
 */
static const GUID GUID_DEVINTERFACE_USBPRINT = {
  0x28d78fad,0x5a12,0x11D1,0xae,0x5b,0x00,0x00,0xf8,0x03,0xa8,0xc2};

static HANDLE OpenUsbPort(int n, DWORD flags) {
 HANDLE ret = 0;
 HDEVINFO devs = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USBPRINT,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 if (devs != INVALID_HANDLE_VALUE) {
  SP_DEVICE_INTERFACE_DATA devinterface;
  devinterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  if (SetupDiEnumDeviceInterfaces(devs, NULL, &GUID_DEVINTERFACE_USBPRINT, n, &devinterface)) {
   SP_DEVINFO_DATA devinfo;
   struct{
    SP_DEVICE_INTERFACE_DETAIL_DATA id;
    TCHAR space[MAX_PATH];
   }id;
   devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
   id.id.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
   if (SetupDiGetDeviceInterfaceDetail(devs, &devinterface, &id.id, sizeof(id), NULL, &devinfo)) {
    ret = CreateFile(id.id.DevicePath,GENERIC_WRITE,FILE_SHARE_READ,
      NULL,OPEN_EXISTING,flags,NULL);
    if (ret == INVALID_HANDLE_VALUE) ret = 0;
   }
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
 return ret;
}

static HANDLE usbHandle;
static BYTE PortState;
static int DevNum;	// zero-based device number
static UINT TimerId;
static HINSTANCE hInst;
static OVERLAPPED o;


// Retrieves the DLL file name into buffer and returns a pointer
// to the first ' ' or the ".dll" portion of the file name.
// Returns NULL when failing.
static PSTR GetNumberPointer(PSTR p, size_t cch) {
 PSTR q;
 if (!GetModuleFileName(hInst,p,cch)) return NULL;
 p=PathFindFileName(p);
 if (!p) return p;
 q=StrPBrk(p," #");
 if (!q) q=PathFindExtension(p);
 return q;
}

static void OutPortState(BYTE State) {
 DWORD bw;
 if (!usbHandle) return;
 if (!WriteFile(usbHandle,&State,1,&bw,&o)
 && (GetLastError()!=ERROR_IO_PENDING
 || WaitForSingleObject(o.hEvent,10))) {
#ifdef _DEBUG
  char s[128];
  bw=GetLastError();
  wnsprintf(s,sizeof(s),"USB-LPT error %d\n",bw);
  OutputDebugString(s);
#endif
  CancelIo(usbHandle);
  if (!DeviceIoControl(usbHandle,IOCTL_USBPRINT_SOFT_RESET,NULL,0,NULL,0,&bw,&o)
  && (GetLastError()!=ERROR_IO_PENDING
  || WaitForSingleObject(o.hEvent,10))) {
   CancelIo(usbHandle);
  }
 }
}

// Let a timer procedure actualize the output,
// reduce load due to too many kernel<->user mode transitions
static void CALLBACK TimerProc(UINT TimerId, UINT uMsg, DWORD dwUser,
  DWORD dw1, DWORD dw2) {
 static BYTE NewState;
 if (NewState!=PortState) OutPortState(NewState=PortState);
}

/**********************
 * exported functions *
 **********************/
BYTE _declspec(dllexport) _cdecl DLO_getnch(void) {
 return 8;
}

LPSTR _declspec(dllexport) _cdecl DLO_getname(void) {
 static char buf[32];
 wnsprintf(buf,sizeof(buf),"USB->Printer adapter #%d",DevNum);
 return buf;
}

void _declspec(dllexport) _cdecl DLO_datain(BYTE ch, BYTE val) {
 BYTE mask = 1<<(ch-1);	// The bit to be set or reset
 if (val) {		// Set bit?
  if (PortState&mask) return;	// Already set, do nothing
 }else{			// Reset bit?
  if (~PortState&mask) return;	// Already reset, do nothing
 }
 PortState ^= mask;	// Swap bit, the timer procedure will output this value
}

void _declspec(dllexport) _cdecl DLO_init(void) {
 if ((usbHandle=OpenUsbPort(DevNum,FILE_FLAG_OVERLAPPED))) {
  o.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
  timeBeginPeriod(10);	// Let MMSYSTEM create the necessary thread
  TimerId=timeSetEvent(10,10,TimerProc,0,TIME_PERIODIC);
 }
}

void _declspec(dllexport) _cdecl DLO_done(void) {
 if (!usbHandle) return;
 timeKillEvent(TimerId);
 timeEndPeriod(10);
 CloseHandle(o.hEvent);
 CloseHandle(usbHandle);
 usbHandle = 0;
}

static BOOL CALLBACK ConfigDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
// Show current state of Usb2Prn found
   CheckDlgButton(Wnd,11,usbHandle?BST_CHECKED:BST_UNCHECKED);
   SetDlgItemInt(Wnd,12,DevNum,FALSE);
  }return TRUE;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 1:
   case 2: EndDialog(Wnd,wParam); break;
   case 10: {	// re-open the Usb2Prn (in case of some disconnection meanwhile)
    int OldNum=DevNum;
    DLO_done();
    DevNum=GetDlgItemInt(Wnd,12,NULL,FALSE);
    DLO_init();
    CheckDlgButton(Wnd,11,usbHandle?BST_CHECKED:BST_UNCHECKED);
    if (usbHandle && DevNum!=OldNum) {
     char NewName[MAX_PATH], *p;
     p=GetNumberPointer(NewName,sizeof(NewName));
     if (p) {
      char DllName[MAX_PATH];
      lstrcpy(DllName,NewName);
      wnsprintf(p,NewName+MAX_PATH-p," %d.dll",DevNum);
      MoveFileEx(DllName,NewName,MOVEFILE_REPLACE_EXISTING);
     }
    }
   }break;
  }break;
 }
 return FALSE;
}

void _declspec(dllexport) _cdecl DLO_about(void) {
 DialogBox(hInst,MAKEINTRESOURCE(100),0,ConfigDlgProc);
}

/********************
 * main entry point *
 ********************/
void CALLBACK _DllMainCRTStartup(HINSTANCE hInstDll,DWORD fdwReason,LPVOID lpReserved) {
 if (fdwReason==DLL_PROCESS_ATTACH) {
  char DllName[MAX_PATH], *p;
  hInst = hInstDll;	// Where the (dialog) resource comes from - and the file name
  p=GetNumberPointer(DllName,sizeof(DllName));
  if (p) DevNum = StrToInt(++p);
  DisableThreadLibraryCalls(hInstDll);	// returns TRUE to continue DLL loading
 }
}
Detected encoding: ASCII (7 bit)2