"Ten sam efekt można osiągnąć (nie pamiętam symbolu) na układzie Cypress-a (8051+USB, program zasysa z zewnętrznej pamięci IIC)." Tak kolego są układy konwertera usb na lpt http://obrazki.elektroda.net/19_1273745620_thumb.gif. W załączniku cała dokumentacja jaką posiadam. Może się komuś przyda.
// Eigenschaftsseiten-Lieferant für USB2LPT im Gerätemanager
// Übersetzbar mit Borland C++ 3.1
#define NONAMELESSUNION
#define STRICT
//#include " wutils.h "
#include & lt; stdlib.h & gt; // strtoul
#include & lt; string.h & gt; // _fmemcmp
#include & lt; windows.h & gt;
#include & lt; windowsx.h & gt; // Message Cracker
#include & lt; shellapi.h & gt;
#include & lt; setupx.h & gt;
#include & lt; prsht.h & gt;
#include & lt; compobj.h & gt; // EXTERN_C, benötigt _fmemcmp
#include & lt; initguid.h & gt; // DEFINE_GUID, benötigt EXTERN_C
#include " usb2lpt.h "
#include & lt; varargs.h & gt;
#include " thunk16.h "
#define CTL_CODE(DeviceType,Function,Method,Access) \
(((DWORD)(DeviceType) & lt; & lt; 16)|((Access) & lt; & lt; 14)|((Function) & lt; & lt; 2)|(Method))
#define FILE_DEVICE_UNKNOWN 0x22
#define METHOD_BUFFERED 0
//#define METHOD_IN_DIRECT 1
//#define METHOD_OUT_DIRECT 2
//#define METHOD_NEITHER 3
#define FILE_ANY_ACCESS 0
typedef char TCHAR;
typedef unsigned long ULONG,FAR*LPULONG,NEAR*NPULONG,*PULONG;
typedef UINT *PUINT,NEAR*NPUINT,FAR*LPUINT;
typedef const TCHAR *PCTSTR,NEAR*NPCTSTR,FAR*LPCTSTR;
#define TEXT(x) x
#define nobreak
typedef enum {false,true} bool;
#define hInst (HINSTANCE)_DS
typedef struct{
BYTE usage; // Benutzungszähler, für 2 Dialoge
BYTE wizard; // Install-Wizard aktiv
TUserCfg uc; // Konfiguration für Treiber (3 WORDs)
TAccessCnt ac; // Zugriffszähler aus Treiber (6 DWORDs)
DWORD kernel32; // Geladene 32-bit-DLL
DWORD dev; // Griff zum .SYS-Treiber
HFONT bold,italic; // Für hübscheren Dialog, fette und kursive Schrift
LPDEVICE_INFO info; // brauchen wir zz. nicht: SetupDi==Holzweg!
}TSetup,*PSetup,FAR*LPSetup,NEAR*NPSetup;
TCHAR MBoxTitle[64];
/*********************************************
* Hilfsfunktionen, vornehmlich aus WUTILS.C *
*********************************************/
int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
TCHAR buf1[256],buf2[256];
LoadString(hInst,id,buf1,elemof(buf1));
wvsprintf(buf2,buf1,arglist);
return MessageBox(Wnd,buf2,MBoxTitle,style);
}
int _cdecl MBox(HWND Wnd, UINT id, UINT style,...) {
return vMBox(Wnd,id,style,(va_list)( & style+1));
}
UINT GetComboHex(HWND Wnd) { // Liefert Hex-Wert oder 0 bei Fehler
TCHAR s[20];
GetWindowText(Wnd,s,elemof(s));
return (UINT)strtol(s,NULL,16);
}
UINT GetCheckboxGroup(HWND Wnd, UINT u, UINT o) {
UINT v,m;
for (v=0,m=1; u & lt; =o; u++,m+=m) if (IsDlgButtonChecked(Wnd,u)==1) v|=m;
return v;
}
void SetCheckboxGroup(HWND Wnd, UINT u, UINT o, UINT v) {
for (; u & lt; =o; u++,v & gt; & gt; =1) CheckDlgButton(Wnd,u,v & 1);
}
void ChangeFonts(HWND w) {
/* Macht alle eingeklammerten () statischen Texte kursiv und
* Überschriften von Gruppenfenstern fett; der besseren Übersicht wegen.
* Fonts werden neu erzeugt, wenn die entspr. Felder in TSetup NULL sind.
* Diese beiden Fonts müssen beim Beenden freigegeben werden!
*/
HFONT normal;
LOGFONT font;
PSetup S=(PSetup)GetWindowLong(w,DWL_USER);
normal=GetWindowFont(w);
if (!S- & gt; italic) {
GetObject(normal,sizeof(font), & font);
font.lfItalic=TRUE;
S- & gt; italic=CreateFontIndirect( & font);
}
if (!S- & gt; bold) {
GetObject(normal,sizeof(font), & font);
font.lfWeight=700;
S- & gt; bold=CreateFontIndirect( & font);
}
for (w=GetWindow(w,GW_CHILD);w;w=GetNextSibling(w)) {
TCHAR s[2],cl[10]; // reicht für das erste Zeichen
GetClassName(w,cl,elemof(cl));
if (!lstrcmpi(cl,T( " STATIC " ))) {
GetWindowText(w,s,elemof(s));
if (s[0]=='(') SetWindowFont(w,S- & gt; italic,TRUE);
}
if (!lstrcmpi(cl,T( " BUTTON " ))) {
if ((GetWindowStyle(w) & 0x0F) == BS_GROUPBOX)
SetWindowFont(w,S- & gt; bold,TRUE);
}
}
}
/**********************************************
* Dialogprozedur: Lese-Cache-Feineinstellung *
**********************************************/
BOOL CALLBACK _loadds ExtraDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
S=(PSetup)lParam;
SetWindowLong(Wnd,DWL_USER,lParam);
SetCheckboxGroup(Wnd,20,22,S- & gt; uc.flags & gt; & gt; UCB_ReadCache0);
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDOK: {
S- & gt; uc.flags & =~(7 & lt; & lt; UCB_ReadCache0);
S- & gt; uc.flags|=GetCheckboxGroup(Wnd,20,22) & lt; & lt; UCB_ReadCache0;
}nobreak;
case IDCANCEL: EndDialog(Wnd,wParam);
}
}
return FALSE;
}
//SetupDiOpenDevRegKey
//CONFIGMG_Write_Registry_Value
bool OpenDev(PSetup S) {
TCHAR k[200],n[MAX_PATH];
DWORD lpCreateFile,cfgmgr32,lpGetDevID,lpGetDevIF;
bool ok;
if (S- & gt; dev) return true; // Ist schon offen!
cfgmgr32=LoadLibraryEx32W( " cfgmgr32.dll " ,0,0);
if (!cfgmgr32) return false;
ok=(bool)(lpGetDevID=GetProcAddress32W(cfgmgr32, " CM_Get_Device_IDA " ))
& & (bool)(lpGetDevIF=GetProcAddress32W(cfgmgr32, " CM_Get_Device_Interface_ListA " ))
& & !CallProcEx32W(4,2,lpGetDevID,S- & gt; info- & gt; dnDevnode,(LPSTR)k,(DWORD)sizeof(k),0L)
& & !CallProcEx32W(5,7,lpGetDevIF,(LPGUID) & Vlpt_GUID,(LPSTR)k,(LPSTR)n,(DWORD)sizeof(n),0L);
FreeLibrary32W(cfgmgr32);
if (!ok) return false;
S- & gt; kernel32=LoadLibraryEx32W( " kernel32.dll " ,0,0);
if (!S- & gt; kernel32) return false;
lpCreateFile=GetProcAddress32W(S- & gt; kernel32, " CreateFileA " );
if (!lpCreateFile) return false;
S- & gt; dev=CallProcEx32W(7,1,lpCreateFile,
(LPSTR)n,0xC0000000L,0L,(DWORD)NULL,3L/*OPEN_EXISTING*/,0L,0L);
if (S- & gt; dev==(DWORD)-1) {
S- & gt; dev=0; // wegen von Unix geerbter Win32-Macke!
return false;
}
return true;
}
long DevIoctl(PSetup S,DWORD code,LPVOID p1,long l1,LPVOID p2,long l2) {
long ret=-1;
if (S- & gt; dev) {
DWORD lpDeviceIoControl=GetProcAddress32W(S- & gt; kernel32, " DeviceIoControl " );
if (lpDeviceIoControl) CallProcEx32W(8,0x54,lpDeviceIoControl,
S- & gt; dev,code,p1,l1,p2,l2,(LPVOID) & ret,0L);
}
return ret;
}
void CloseDev(PSetup S) {
if (S- & gt; kernel32 & & S- & gt; dev) {
DWORD lpCloseHandle=GetProcAddress32W(S- & gt; kernel32, " CloseHandle " );
if (lpCloseHandle) CallProcEx32W(1,0,lpCloseHandle,S- & gt; dev);
S- & gt; dev=0;
}
if (S- & gt; kernel32) FreeLibrary32W(S- & gt; kernel32);
S- & gt; kernel32=0;
}
/************************************************
* Eigenschaftsseiten-Dialogprozedur: Emulation *
************************************************/
void CheckButton13(HWND Wnd, PSetup S) {
UINT i=2;
switch ((S- & gt; uc.flags & gt; & gt; UCB_ReadCache0) & 7) {
case 0: i--; nobreak;// i=0
case 7: i--; // i=1
}
CheckDlgButton(Wnd,13,i);
}
BOOL CALLBACK _loadds EmulDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
static const WORD DefLpt[]={0x378,0x278,0x3BC};
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
static const TCHAR LptStd[]=T( " LPT1\0LPT2\0LPT1 anno 1985\0 " );
static const TCHAR LptEnh[]=T( " SPP\0EPP 1.9\0ECP\0ECP + EPP\0 " );
PCTSTR p;
TCHAR s[32];
HWND w0,w2;
int i;
S=(PSetup)((LPPROPSHEETPAGE)lParam)- & gt; lParam;
SetWindowLong(Wnd,DWL_USER,(LONG)S);
ChangeFonts(Wnd);
w0=GetDlgItem(Wnd,100); // Adresse
for (p=LptStd,i=0;*p;p+=lstrlen(p)+1,i++) {
wsprintf(s,T( " %Xh (%u, %s) " ),DefLpt[i],DefLpt[i],(LPCSTR)p);
(void)ComboBox_AddString(w0,s);
}
w2=GetDlgItem(Wnd,102); // Parallelport-Erweiterung
for (p=LptEnh;*p;p+=lstrlen(p)+1) (void)ComboBox_AddString(w2,p);
if (OpenDev(S)) {
DevIoctl(S,IOCTL_VLPT_UserCfg, & S- & gt; uc,0, & S- & gt; uc,sizeof(TUserCfg));
CloseDev(S);
}else{ // Werte aus Registry holen, wie??
MessageBeep(MB_ICONHAND);
}
for (i=0; i & lt; 3; i++) if (S- & gt; uc.LptBase==DefLpt[i]) {
(void)ComboBox_SetCurSel(w0,i);
goto skip;
}
wsprintf(s,T( " %Xh " ),S- & gt; uc.LptBase);
SetWindowText(w0,s);
skip:
(void)ComboBox_SetCurSel(w2,S- & gt; uc.Mode);
SetCheckboxGroup(Wnd,10,12,S- & gt; uc.flags & gt; & gt; UCB_Debugreg);
SetDlgItemInt(Wnd,101,S- & gt; uc.TimeOut,FALSE);
SendMessage(Wnd,WM_COMMAND,12,0);
CheckButton13(Wnd,S);
/*
DWORD length=sizeof(s);
HKEY key;
key=SetupDiOpenDevRegKey(stuff- & gt; info,stuff- & gt; sdd,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ);
if (key) {
if (!RegQueryValueEx(key,T( " Portadresse " ),NULL,NULL,(LPBYTE)s, & length)) {
MessageBeep(0);
DoEnvironmentSubst(s,sizeof(s));
}
RegCloseKey(key);
} // get sample info URL
*/
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 12: EnableWindow(GetDlgItem(Wnd,101),IsDlgButtonChecked(Wnd,12)); break;
case 13: switch (IsDlgButtonChecked(Wnd,13)) {
case 2: CheckDlgButton(Wnd,13,0); nobreak; // 3. Zustand nicht klickbar!
case 0: S- & gt; uc.flags & =~(7 & lt; & lt; UCB_ReadCache0); break; // Alles aus
case 1: S- & gt; uc.flags|=7 & lt; & lt; UCB_ReadCache0; break; // Alles an
}break;
case 103: if (DialogBoxParam(hInst,MAKEINTRESOURCE(102),Wnd,ExtraDlgProc,
(LPARAM)S)==IDOK) CheckButton13(Wnd,S); break;
}break;
case WM_NOTIFY: switch (((LPNMHDR)lParam)- & gt; code){
case PSN_KILLACTIVE: { // Überprüfung der Eingabefelder!
HWND w;
UINT u;
BOOL Ok;
//---
w=GetDlgItem(Wnd,100);
u=GetComboHex(w);
if (!u) {
MBox(Wnd,17,MB_OK|MB_ICONEXCLAMATION);
goto f1;
}
if (u & lt; 0x100 || u & 3 || u & gt; & gt; 16 // gewöhnliche Fehler
|| u==0x1E0 || u==0x1E4 || u==0x1F0 || u==0x1F4 || u==0x3E4 || u==0x3F4) {
MBox(Wnd,18,MB_OK|MB_ICONEXCLAMATION);
f1: SetFocus(w); // Festplattenports vor DAU schützen!!
(void)ComboBox_SetEditSel(w,0,(UINT)-1);
goto fail;
}
S- & gt; uc.LptBase=(WORD)u;
//---
w=GetDlgItem(Wnd,102);
u=ComboBox_GetCurSel(w);
if (u & 1 & & S- & gt; uc.LptBase & 7) {// EPP geht nur mit durch 8 teilbaren Adressen!
MBox(Wnd,19,MB_OK|MB_ICONEXCLAMATION);
SetFocus(w);
goto fail;
}
S- & gt; uc.Mode=(BYTE)u;
//---
if (!S- & gt; wizard) {
w=GetDlgItem(Wnd,101);
u=GetDlgItemInt(Wnd,101, & Ok,FALSE);
if (!Ok || u & gt; 1000) {
MBox(Wnd,20,MB_OK|MB_ICONEXCLAMATION);
SetFocus(w);
Edit_SetSel(w,0,(UINT)-1);
goto fail;
}
S- & gt; uc.TimeOut=(WORD)u;
//---
S- & gt; uc.flags & =~(7 & lt; & lt; UCB_Debugreg);
S- & gt; uc.flags|=GetCheckboxGroup(Wnd,10,12) & lt; & lt; UCB_Debugreg;
}
break;
fail: SetWindowLong(Wnd,DWL_MSGRESULT,1); // Fokus nicht entfernen!
}return TRUE;
case PSN_APPLY: {
int i;
for (i=0; i & lt; elemof(DefLpt); i++) if (DefLpt[i]==S- & gt; uc.LptBase) goto setit;
if (MBox(Wnd,16,MB_YESNO,S- & gt; uc.LptBase)!=IDYES) {
SetWindowLong(Wnd,DWL_MSGRESULT,PSNRET_INVALID);
return TRUE;
}
setit:
if (OpenDev(S)) {
DevIoctl(S,IOCTL_VLPT_UserCfg, & S- & gt; uc,sizeof(TUserCfg), & S- & gt; uc,0);
CloseDev(S);
}else{ // Meckern??
MessageBeep(MB_ICONHAND);
}break;
}
}
}
return FALSE;
}
void UpdateEditArray(HWND Wnd, UINT u, UINT o, ULONG*old, ULONG _ss*n) {
for (;u & lt; =o;u++,old++,n++) {
if (*old!=*n) {
char s[32];
wsprintf(s,T( " %lu " ),(*old=*n));
SetDlgItemText(Wnd,u,s);
}
}
}
BOOL CALLBACK _loadds StatDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
S=(PSetup)((LPPROPSHEETPAGE)lParam)- & gt; lParam;
SetWindowLong(Wnd,DWL_USER,(LONG)S);
ChangeFonts(Wnd);
S- & gt; ac.out=S- & gt; ac.in=S- & gt; ac.fail=S- & gt; ac.steal=S- & gt; ac.wpu=S- & gt; ac.rpu=(ULONG)-1;
SendMessage(Wnd,WM_TIMER,0,0);
}return TRUE;
case WM_TIMER: if (OpenDev(S)) { // bleibt normalerweise geöffnet
TAccessCnt AC;
if (DevIoctl(S,IOCTL_VLPT_AccessCnt, & AC,0, & AC,sizeof(AC))!=-1)
UpdateEditArray(Wnd,100,105, & S- & gt; ac.out, & AC.out);
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 106: S- & gt; ac.out=S- & gt; ac.in=S- & gt; ac.fail=S- & gt; ac.steal=0; goto weiter;
case 107: S- & gt; ac.wpu=S- & gt; ac.rpu=0; weiter: {
if (!OpenDev(S)) break;
if (DevIoctl(S,IOCTL_VLPT_AccessCnt, & S- & gt; ac,sizeof(S- & gt; ac), & S- & gt; ac,0)==1)
MessageBeep(MB_ICONEXCLAMATION);
}
}break;
case WM_NOTIFY: switch (((LPNMHDR)lParam)- & gt; code){
case PSN_SETACTIVE: {
SetTimer(Wnd,100,500,NULL);
}break;
case PSN_KILLACTIVE: {
KillTimer(Wnd,100);
CloseDev(S);
}break;
}break;
}
return FALSE;
}
#pragma argsused
UINT CALLBACK _loadds PageCallbackProc(HWND Wnd, UINT Msg, LPPROPSHEETPAGE p) {
PSetup S=(PSetup)p- & gt; lParam;
if (S) switch (Msg) {
case PSPCB_CREATE: S- & gt; usage++; break;
case PSPCB_RELEASE: if (!(--S- & gt; usage)) {
CloseDev(S);
if (S- & gt; italic)DeleteFont(S- & gt; italic);
if (S- & gt; bold) DeleteFont(S- & gt; bold);
LocalFree((HLOCAL)S);
// free(S);
p- & gt; lParam=0;
}
}
return TRUE;
}
UINT InitStruct(void far*buf, unsigned len) {
_fmemset(buf,0,len);
*(unsigned far*)buf=len;
return 0;
}
EXTERN_C BOOL CALLBACK _loadds EnumPropPages(LPDEVICE_INFO lpdi,
LPFNADDPROPSHEETPAGE AddPage, LPARAM lParam) {
HPROPSHEETPAGE hpage;
NPSetup S;
// PSetup S=new TSetup;
PROPSHEETPAGE page;
// _asm int 3;
S=(PSetup)LocalAlloc(LPTR,sizeof(TSetup));
// Ohne HEAPSIZE in der prop16.def hängt LocalAlloc den Prozess auf!
// S=(PSetup)malloc(sizeof(TSetup));
InitStruct( & page,sizeof(page));
LOWORD(page.dwFlags)=PSP_USECALLBACK;
page.hInstance=hInst;
LOWORD(page.DUMMYUNIONNAME.pszTemplate)=100;
page.pfnDlgProc=EmulDlgProc;
page.lParam=(LPARAM)S;
page.pfnCallback=PageCallbackProc;
S- & gt; info=lpdi;
lstrcpy(MBoxTitle,lpdi- & gt; szDescription);
hpage=CreatePropertySheetPage( & page);
if (!AddPage(hpage,lParam)) DestroyPropertySheetPage(hpage);
page.DUMMYUNIONNAME.pszTemplate++;
page.pfnDlgProc=StatDlgProc;
hpage=CreatePropertySheetPage( & page);
if (!AddPage(hpage,lParam)) DestroyPropertySheetPage(hpage);
return TRUE;
}
#pragma argsused
EXTERN_C DWORD CALLBACK _loadds CoDeviceInstall(
UINT InstallFunction,
LPDEVICE_INFO DeviceInfoData,
/*PCOINSTALLER_CONTEXT_DATA*/long Context) {
_asm int 3;
#if 0
switch (InstallFunction) {
case DIF_NEWDEVICEWIZARD_FINISHINSTALL: {
SP_NEWDEVICEWIZARD_DATA NDWD;
PSetup S=(PSetup)LocalAlloc(LPTR,sizeof(TSetup));
PROPSHEETPAGE page={
sizeof(PROPSHEETPAGE),
PSP_USECALLBACK|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE,
hInst, //nicht-statisch
MAKEINTRESOURCE(103),
0, // Icon
NULL, // Titel
EmulDlgProc,
(LPARAM)S, //nicht-statisch
PageCallbackProc,
NULL, //pcRefParent
MAKEINTRESOURCE(21),// erfordert (irgendwo) ein #define _WIN32_IE 0x0500
MAKEINTRESOURCE(22)};
S- & gt; wizard=TRUE;
S- & gt; info=DeviceInfoSet;
S- & gt; sdd= DeviceInfoData;
NDWD.ClassInstallHeader.cbSize=sizeof(SP_CLASSINSTALL_HEADER);
NDWD.ClassInstallHeader.InstallFunction=DIF_ADDPROPERTYPAGE_ADVANCED;
if (SetupDiGetClassInstallParams(DeviceInfoSet,DeviceInfoData,
& NDWD.ClassInstallHeader,sizeof(NDWD),NULL)
& & NDWD.NumDynamicPages & lt; MAX_INSTALLWIZARD_DYNAPAGES) {
NDWD.DynamicPages[NDWD.NumDynamicPages++]=
CreatePropertySheetPage( & page);
SetupDiSetClassInstallParams(DeviceInfoSet,DeviceInfoData,
& NDWD.ClassInstallHeader,sizeof(NDWD));
}else{
MessageBeep(MB_ICONEXCLAMATION);
LocalFree(S);
}
}break;
}
#endif
return 0/*NO_ERROR*/;
}
#pragma argsused
EXTERN_C int CALLBACK LibMain(HANDLE hModule, WORD wDataSeg,
WORD cbHeapSize, LPSTR lpszCmdLine) {
return TRUE;
}
#define VERBOSE
/* Treiber für das USB2LPT-Gerät, haftmann#software 2004-2007
0410xx Zarte Versuche mit tausenden Abstürzen
0501xx Entdeckung der Notwendigkeit der Disassemblierung von Windows' ISRs (HAL)
050410 Erste ordentliche Ausgabe (zz. Verzeichnis " alt " )
051104 Zweite Ausgabe mit WORD+DWORD-Unterstützung, leider Problem mit iMPACT
061007 GALEP-Murks: IRQL runter- und wieder raufsetzen
(GALEP32.EXE läuft dann - allerdings nur mit einem weiteren Patch)
061008 Low-Speed über die beiden Interrupt-Pipes (ungetestet, für 1.5)
061011 Priority-Boost nach dem Warten (sonst pennt Galep bei paralleler DOS-Box)
061203 Debugregister und INT1-Anzapfung bei NT/XP standardmäßig AUS
(wegen zunehmendem SMP-Problem);
der Treiber macht sich erst bei Debugregister-Aktivierung
an der IDT zu schaffen (das betrifft auch den Lesezugriff)
Hoffentlich hilft das auch! - Nein, es hilft nicht.
070209 Bug: EP0STALL (EEPROM-Zugr.) führt zum Absturz; Speicherleck
070602 READ_PORT_UCHAR-Anzapfung bei NT/XP standardmäßig AUS
Hoffentlich hilft das auch! - Nein, es hilft nicht.
070602 BUG:Bluescreen: durch Timer-DPC nach RemoveDevice (erledigt?)
070930 Low-Speed-Extrawurst raus: unnötig unter Windows
64-bit-Zeiger-Kompatibilität (ULONG_PTR)
Windows-95-Extrawürste raus (Treiber funktionierte eh' nie unter 95)
IRP_MN_QUERY_RESOURCE_REQUIREMENTS raus (hat nie funktioniert)
HookSyscalls beim Treiber-Start (Absturz von Gerätemanager?)
Flags retten bei Read_Port_Uchar() usw.
Supervisor Mode Bit in Ausgangszustand (Absturz svchost.exe bei SMP?)
*/
#define INIT_MY_GUID // In diese .OBJ-Datei kommt die 16-Byte-GUID hinein
#include " usb2lpt.h "
// Nach dem Warten auf den (meistens) USB-IN-Transfer wird die dynamische
// Thread-Priorität um folgenden Betrag angehoben:
#define USB2LPT_BOOST IO_PARALLEL_INCREMENT // =1
#define ExFreePoolWithTag(p,t) ExFreePool(p) // sonst läuft's nicht unter Win98
/****************************
** PnP-Standardbehandlung **
****************************/
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION X, PIRP I) {
IoSkipCurrentIrpStackLocation(I);
return IoCallDriver(X- & gt; ldo,I);
}
NTSTATUS OnRequestComplete(IN PDEVICE_OBJECT fdo,IN PIRP Irp,IN PKEVENT pev) {
KeSetEvent(pev, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS ForwardAndWait(PDEVICE_EXTENSION X,IN PIRP Irp) {
/* Forward request to lower level and await completion
The processor must be at PASSIVE IRQL because this function initializes
and waits for non-zero time on a kernel event object.
*/
KEVENT event;
NTSTATUS ntStatus;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
// Initialize a kernel event object to use in waiting for the lower-level
// driver to finish processing the object.
KeInitializeEvent( & event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,
(PVOID) & event, TRUE, TRUE, TRUE);
ntStatus = IoCallDriver(X- & gt; ldo, Irp);
if (ntStatus==STATUS_PENDING) {
KeWaitForSingleObject( & event, Executive, KernelMode, FALSE, NULL);
ntStatus = Irp- & gt; IoStatus.Status;
}
return ntStatus;
}
NTSTATUS CompleteRequest(IN PIRP I,IN NTSTATUS ret,IN ULONG_PTR info) {
/* Mark I/O request complete
Arguments:
Irp - I/O request in question
status - returned status code
info Additional information related to status code
Reicht & lt; status & gt; durch
*/
I- & gt; IoStatus.Status=ret;
I- & gt; IoStatus.Information=info;
IoCompleteRequest(I,IO_NO_INCREMENT);
return ret;
}
/*****************************
** Asynchrone USBD-Aufrufe **
*****************************/
typedef struct{ // " Arbeitsauftrag " für kombinierte Bulk-Aus- und Eingabe
PDEVICE_EXTENSION x; // Unsere Geräteerweiterung
PIRP irpj; // Job-IRP, oder NULL für angezapften Ein/Ausgabebefehl
PIRP irpw; // IRP zum Bulk-Schreiben
PIRP irpr; // IRP zum Bulk-Lesen (NULL für Nur-Schreiben)
NTSTATUS stat; // zurückzugebender Status (initialisieren mit 0)
ULONG rlen; // Gelesene Bytes (initialisieren mit 0)
struct _URB_BULK_OR_INTERRUPT_TRANSFER urbw; // Ohne extra ExAllocatePool
struct _URB_BULK_OR_INTERRUPT_TRANSFER urbr; // entfällt bei irpr=0
}JOB,*PJOB;
void SetUsbStatus(PDEVICE_EXTENSION X, NTSTATUS *stat, PURB U) {
if (!USBD_SUCCESS(U- & gt; UrbHeader.Status)) {
Vlpt_KdPrint(( " *** USB-Fehlerkode %X\n " ,U- & gt; UrbHeader.Status));
X- & gt; LastFailedUrbStatus=U- & gt; UrbHeader.Status;
if (stat & & NT_SUCCESS(*stat)) *stat=STATUS_UNSUCCESSFUL;
}
}
// Komplettierungsroutine für IOCTL_OutIn und PortTrap
NTSTATUS OutInFertig(PDEVICE_OBJECT fdo,PIRP I,PJOB J) {
// fdo enthält keinen brauchbaren Zeiger!
if (NT_SUCCESS(J- & gt; stat)) J- & gt; stat=I- & gt; IoStatus.Status;
if (I==J- & gt; irpw) {
SetUsbStatus(J- & gt; x, & J- & gt; stat,(PURB) & J- & gt; urbw);
Vlpt_KdPrint2(( " Ausgabe von %d Bytes FERTIG\n " ,J- & gt; urbw.TransferBufferLength));
J- & gt; irpw=NULL; // erledigt!
}else if (I==J- & gt; irpr) {
SetUsbStatus(J- & gt; x, & J- & gt; stat,(PURB) & J- & gt; urbr);
J- & gt; rlen=J- & gt; urbr.TransferBufferLength;
Vlpt_KdPrint2(( " Eingabe von %d Bytes FERTIG\n " ,J- & gt; rlen));
J- & gt; irpr=NULL; // erledigt!
}else ASSERT(FALSE);
IoFreeIrp(I);
if (!J- & gt; irpw & & !J- & gt; irpr) { // Alles erledigt?
if (J- & gt; irpj) { // wenn ursächlich DeviceIoControl (User)
J- & gt; irpj- & gt; IoStatus.Status=J- & gt; stat;
J- & gt; irpj- & gt; IoStatus.Information=J- & gt; rlen;
IoCompleteRequest(J- & gt; irpj,USB2LPT_BOOST);
}else{
J- & gt; x- & gt; bfill=0; // ursächlich Port-Trap (System)
KeSetEvent( & J- & gt; x- & gt; ev,USB2LPT_BOOST,FALSE); // bfill-Ampel auf Grün
}
ExFreePoolWithTag(J,'tplJ'); // Job (Arbeitsauftrag) samt URBs erledigt
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
// Routine zum " gleichzeitigen " Laufenlassen zweier USB-Transfers!
// J muss von ExAllocatePoolWithTag(...'tplJ') kommen, wird letztendlich freigegeben
// I muss von IoAllocateIrp kommen, wird am Ende freigegeben
// U=ein Zeiger in J
NTSTATUS AsyncCallUSBDJob(PDEVICE_EXTENSION X,PJOB J,PIRP I,PURB U) {
PIO_STACK_LOCATION nextStack;
NTSTATUS ret;
Vlpt_KdPrint2(( " Aufruf AsyncCallUSBDJob\n " ));
ASSERT(I);
nextStack=IoGetNextIrpStackLocation(I); //IRQL & lt; =2
ASSERT(nextStack);
if (!nextStack) return STATUS_UNSUCCESSFUL;
nextStack- & gt; MajorFunction=IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack- & gt; Parameters.DeviceIoControl.IoControlCode
=IOCTL_INTERNAL_USB_SUBMIT_URB;
nextStack- & gt; Parameters.Others.Argument1=U;
IoSetCompletionRoutine(I,OutInFertig,J,TRUE,TRUE,TRUE);//IRQL & lt; =2
ret=IoCallDriver(X- & gt; ldo,I); //IRQL & lt; =2
if (!NT_SUCCESS(ret) & & I) IoFreeIrp(I);
return ret;
}
NTSTATUS AsyncCallReady(PDEVICE_OBJECT fdo,PIRP I,PURB U) {
NTSTATUS ret=I- & gt; IoStatus.Status;
PDEVICE_EXTENSION X=fdo- & gt; DeviceExtension;
if (I- & gt; PendingReturned) IoMarkIrpPending(I); // will Microsoft so, sonst TOT
if (NT_SUCCESS(ret)) {
Vlpt_KdPrint2(( " Transfer von %d Bytes OK\n " ,I- & gt; IoStatus.Information));
}else{
Vlpt_KdPrint(( " Transfer versagt, Code %X, USB-Code=%X\n " ,ret,U- & gt; UrbHeader.Status));
TRAP();
X- & gt; LastFailedUrbStatus=U- & gt; UrbHeader.Status;
}
ExFreePoolWithTag(U,'tplU');
return ret;
}
// Routine für asynchrone Bearbeitung von EEPROM/XRAM-Blocktransfers
// I wird NICHT freigegeben (IRP " von außen " )
// U muss von ExAllocatePoolWithTag(...'tplU') kommen, wird freigegeben
NTSTATUS AsyncCallUSBD(PDEVICE_EXTENSION X,PIRP I,PURB U) {
PIO_STACK_LOCATION nextStack;
Vlpt_KdPrint2(( " Aufruf AsyncCallUSBD\n " ));
ASSERT(I);
nextStack=IoGetNextIrpStackLocation(I); //IRQL & lt; =2
ASSERT(nextStack);
if (!nextStack) return STATUS_UNSUCCESSFUL;
nextStack- & gt; MajorFunction=IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack- & gt; Parameters.DeviceIoControl.IoControlCode
=IOCTL_INTERNAL_USB_SUBMIT_URB;
nextStack- & gt; Parameters.Others.Argument1=U;
IoSetCompletionRoutine(I,AsyncCallReady,U,TRUE,TRUE,TRUE);//IRQL & lt; =2
return IoCallDriver(X- & gt; ldo,I); //IRQL & lt; =2
}
#define AllocFixed(len,tag) ExAllocatePoolWithTag(NonPagedPool,len,tag)
/***********************
** Ein/Ausgabe-IOCTL **
***********************/
NTSTATUS OutInCheck(PDEVICE_EXTENSION X, ULONG ol, ULONG il) {
// Prüfen der Parameter für Aus- und Eingabe, nur für IOCTL
PUSBD_INTERFACE_INFORMATION ii=X- & gt; Interface;
int i;
if (!ii) {
Vlpt_KdPrint(( " OutInCheck(): keine Interface-Info!\n " ));
return STATUS_UNSUCCESSFUL;
}
if (ii- & gt; NumberOfPipes & lt; 2) {
Vlpt_KdPrint(( " OutInCheck(): Zu wenig Pipes!\n " ));
return STATUS_UNSUCCESSFUL;
}
for (i=0; i & lt; 2; i++) {
PUSBD_PIPE_INFORMATION p=ii- & gt; Pipes+i;
if (p- & gt; PipeType!=UsbdPipeTypeBulk/* & & p- & gt; PipeType!=UsbdPipeTypeInterrupt*/) {
Vlpt_KdPrint(( " OutInCheck(): Pipe nicht vom Typ BULK " /* oder INTERRUPT*/ " !\n " ));
return STATUS_UNSUCCESSFUL;
}
if (!p- & gt; PipeHandle) {
Vlpt_KdPrint(( " OutInCheck(): kein Pipe-Handle!\n " ));
return STATUS_UNSUCCESSFUL;
}
}
if (ol & gt; ii- & gt; Pipes[0].MaximumTransferSize) {
Vlpt_KdPrint(( " OutInCheck(): Zu große Ausgabe-Transferlänge!\n " ));
return STATUS_INVALID_PARAMETER;
}
if (il & gt; ii- & gt; Pipes[1].MaximumTransferSize) {
Vlpt_KdPrint(( " OutInCheck(): Zu große Eingabe-Transferlänge!\n " ));
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS OutIn(PDEVICE_EXTENSION X, PUCHAR ob, ULONG ol, PUCHAR ib, ULONG il,
PULONG bytesread, PIRP I) {
// Aus- und Eingabe über die beiden Pipes zum/vom USB2LPT-Gerät
// ob: Ausgabe-Bytes (i.d.R. Adresse-Data-Adresse-Data...-Adresse+0x10)
// ol: Ausgabe-Länge (i.d.R. & lt; = 64 Bytes)
// ib: Eingabe-Puffer (oft nur 1 Byte)
// il: Eingabe-Pufferlänge (oft == 1)
// bytesread ( " gelesene Bytes " ) == NULL - & gt; kurzer Transfer nicht OK
// I == NULL - & gt; Aufruf kommt vom Port-Trap (kein IRP;keine Komplettierung)
// Gänsemarsch (X- & gt; bmutex) erforderlich! (Aufrufer muss dafür sorgen.)
// Für maximalen Durchsatz werden beide USB-Transfers gleichzeitig initiiert.
// Fertig wird der gesamte OutIn-Transfer, wenn beide USB-Transfers
// fertig sind (normalerweise ist der IN-Transfer zuletzt fertig)
// Diese Routine ist asynchron unabhängig von I.
// IRQL & lt; = DISPATCH_LEVEL
PUSBD_INTERFACE_INFORMATION ii=X- & gt; Interface;
NTSTATUS ret=STATUS_SUCCESS;
PJOB J;
#define USIZE sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)
if (!ol & & !il) return ret;
Vlpt_KdPrint2(( " Aufruf OutIn (ol=%d, il=%d)\n " ,ol,il));
J=AllocFixed(il?sizeof(JOB):sizeof(JOB)-USIZE,'tplJ');
if (!J) return STATUS_NO_MEMORY;
RtlZeroMemory(J,sizeof(JOB)-USIZE-USIZE);
J- & gt; x=X;
J- & gt; irpj=I;
if (!I) KeClearEvent( & X- & gt; ev); // Ampel auf ROT
if (ol) {
// IoBuildDeviceIoControlRequest geht nicht wegen PASSIVE_LEVEL!
UsbBuildInterruptOrBulkTransferRequest((PURB) & J- & gt; urbw,
USIZE, //size of urb
ii- & gt; Pipes[0].PipeHandle, // Erste Pipe = Schreiben
ob,NULL,ol,0,NULL);
J- & gt; irpw=IoAllocateIrp(X- & gt; ldo- & gt; StackSize,FALSE);
if (!J- & gt; irpw) ret=STATUS_INSUFFICIENT_RESOURCES;
}
if (il & & NT_SUCCESS(ret)) {
UsbBuildInterruptOrBulkTransferRequest((PURB) & J- & gt; urbr,
USIZE, //size of urb
ii- & gt; Pipes[1].PipeHandle, // Zweite Pipe = Lesen
ib,NULL,il,
bytesread // je nachdem!
? USBD_TRANSFER_DIRECTION_IN|USBD_SHORT_TRANSFER_OK
: USBD_TRANSFER_DIRECTION_IN,
NULL);
J- & gt; irpr=IoAllocateIrp(X- & gt; ldo- & gt; StackSize,FALSE);
if (!J- & gt; irpr) ret=STATUS_INSUFFICIENT_RESOURCES;
}
if (ol & & NT_SUCCESS(ret)) ret=AsyncCallUSBDJob(X,J,J- & gt; irpw,(PURB) & J- & gt; urbw);
if (!NT_SUCCESS(ret)) J- & gt; irpw=NULL;
if (il & & NT_SUCCESS(ret)) ret=AsyncCallUSBDJob(X,J,J- & gt; irpr,(PURB) & J- & gt; urbr);
if (!NT_SUCCESS(ret)) J- & gt; irpr=NULL;
// BUGBUG, wenn das erste AsyncCallUSBDJob klappte und das zweite schief geht!
if (!NT_SUCCESS(ret)) ExFreePoolWithTag(J,'tplJ');
#undef USIZE
return ret; // Jetzt sind bis zu zwei URBs in Arbeit
}
/****************************
** Ein/Ausgabe-Simulation **
****************************/
// Konvertieren LPT-Adresse in Adressbyte
static UCHAR ab(PDEVICE_EXTENSION X, USHORT adr) { // Adress-Byte ermitteln
adr-=X- & gt; uc.LptBase; // sollte 0..7 oder 400h..403h liefern
if (adr & 0x400) adr|=8; // ECP-Adressbyte draus machen
return (UCHAR)adr;
}
// Aufruf nur im Gänsemarsch möglich! Flags f:
// 1=ForceFlush (sonst nur wenn voll; " voll " heißt Füllstand & gt; =62)
// 2=WaitReady (IRQL muss 0 sein!)
// 4=CancelTimer
static NTSTATUS FlushBuffer(PDEVICE_EXTENSION X,PUCHAR ib,ULONG il,UCHAR f) {
NTSTATUS ret=STATUS_SUCCESS;
if (f & 1 || X- & gt; bfill & gt; =62) {
if (f & UC_WriteCache & X- & gt; uc.flags) KeCancelTimer( & X- & gt; wrcache.tmr);
ret=OutIn(X,X- & gt; buffer,X- & gt; bfill,ib,il,NULL,NULL);
if (f & 2 & & NT_SUCCESS(ret)) {
// Vlpt_KdPrint2(( " Warten auf Ende von OutIn()\n " ));
KeWaitForSingleObject( & X- & gt; ev,Suspended,KernelMode,FALSE,NULL);
// Vlpt_KdPrint2(( " Das Warten hat ein Ende!\n " ));
}
}
return ret;
}
// Umleiten (oder Puffern) eines (bereits geTRAPten) OUT-Befehls, IRQL==0
// adr=Parallelport-Adresse (wird später Adress-Byte)
// b =Datenbyte, -word oder -dword (entspr. OUT DX,AL, DX,AX oder DX,EAX)
// len=Länge (1, 2 oder 4)
bool _stdcall HandleOut(PDEVICE_EXTENSION X,USHORT adr,ULONG b, UCHAR len) {
UCHAR a; // Adress-Byte für USB
bool ok=false;
#if DBG
ULONG mask=(1 & lt; & lt; (len & lt; & lt; 3))-1; // Nicht auszugebende Bytes ausmaskieren (Debug)
#endif
KIRQL irql;
Vlpt_KdPrint2(( " HandleOut(%03Xh,%0*Xh)\n " ,adr,2*len,b & mask));
irql=KeGetCurrentIrql();
if (irql) KeLowerIrql(0); // GALEP: absenken!
if (/*!KeGetCurrentIrql() // kann nicht behandeln, wenn & lt; & gt; 0!
& & */ NT_SUCCESS(IoAcquireRemoveLock( & X- & gt; rlock,NULL))) {
// Ab hier Gänsemarsch erzwingen, anderer OUT- oder IN-Befehl muss warten
if (!KeWaitForMutexObject( & X- & gt; bmutex,Executive,KernelMode,FALSE,NULL)) {
KeWaitForSingleObject( & X- & gt; ev,Suspended,KernelMode,FALSE,NULL);
ASSERT(X- & gt; bfill & lt; =62 & & !(X- & gt; bfill & 1)); // Füllstand muss gerade sein!
a=ab(X,adr); // Adressbyte für USB2LPT-Firmware
do{ // Über die 1-4 Bytes von b iterieren...
if (!NT_SUCCESS(FlushBuffer((X),NULL,0,6))) goto ex;// Puffer voll? Dann ausgeben!
switch (a) {
case 0: { // 378h, Datenport
X- & gt; mirror[0]|=UC_ReadCache0;
X- & gt; mirror[1]=(UCHAR)b;
}break;
case 2: { // 37Ah, Steuerport
X- & gt; mirror[0]|=UC_ReadCache2;
X- & gt; mirror[2]=(UCHAR)b;
}break;
}
X- & gt; buffer[X- & gt; bfill++]=a;
X- & gt; buffer[X- & gt; bfill++]=(UCHAR)b;
a++;
b & gt; & gt; =8;
}while(--len);
if (/*!irql & & */X- & gt; uc.flags & UC_WriteCache) {
if (NT_SUCCESS(KeSetTimer( & X- & gt; wrcache.tmr,
RtlConvertLongToLargeInteger(X- & gt; uc.TimeOut*-10000),
& X- & gt; wrcache.dpc))) ok++; // Zeit wird nicht größer als DWORD
}else if (NT_SUCCESS(FlushBuffer(X,NULL,0,3))) ok++; // sofort versenden!
ex:
KeReleaseMutex( & X- & gt; bmutex,FALSE);
}
IoReleaseRemoveLock( & X- & gt; rlock,NULL);
}
if (irql) KeRaiseIrql(irql, & irql); // GALEP: wieder anheben
Vlpt_KdPrint2(( " HandleOut EXIT\n " ));
if (!ok) {
DbgPrint( " HandleOut(%03Xh) versagt! IRQL=%u\n " ,adr,KeGetCurrentIrql());
TRAP();
}
return ok;
}
// Umleiten eines (bereits geTRAPten) IN-Befehls, IRQL==0
// Parameter wie bei HandleOut()
bool _stdcall HandleIn(PDEVICE_EXTENSION X,USHORT adr,PULONG ret,UCHAR len) {
UCHAR a,ll;
bool ok=false;
ULONG mask=(1UL & lt; & lt; (len & lt; & lt; 3))-1;
KIRQL irql;
*ret|=mask; // Low-Teil im Fehlerfall FFh (oder FFFFh oder FFFFFFFFh)
Vlpt_KdPrint2(( " HandleIn ENTER\n " ));
irql=KeGetCurrentIrql();
if (irql) KeLowerIrql(0); // GALEP: absenken!
if (/*!KeGetCurrentIrql() // kann nicht behandeln!
& & */ NT_SUCCESS(IoAcquireRemoveLock( & X- & gt; rlock,NULL))) {
// Ab hier Gänsemarsch erzwingen, anderer OUT- oder IN-Befehl muss warten
if (!KeWaitForMutexObject( & X- & gt; bmutex,Executive,KernelMode,FALSE,NULL)) {
KeWaitForSingleObject( & X- & gt; ev,Suspended,KernelMode,FALSE,NULL);
ASSERT(X- & gt; bfill & lt; =62 & & !(X- & gt; bfill & 1)); // Füllstand muss gerade sein!
if (len+X- & gt; bfill & gt; 63 // Puffer würde überlaufen? Ausgeben!
& & !NT_SUCCESS(FlushBuffer(X,NULL,0,7))) goto ex;
a=ab(X,adr);
if (len==1) switch (a) {
case 0: if (X- & gt; mirror[0] & X- & gt; uc.flags & UC_ReadCache0) {
*(PUCHAR)ret=X- & gt; mirror[1];
ok++;
goto ex;
}break;
case 2: if (X- & gt; mirror[0] & X- & gt; uc.flags & UC_ReadCache2) {
*(PUCHAR)ret=X- & gt; mirror[2]|0xE0;
ok++;
goto ex;
}break;
}
// Bei Word- und DWord-Zugriffen (len!=1) wird kein Cache benutzt
ll=len; do{
X- & gt; buffer[X- & gt; bfill++]=a|0x10; // 1-4 Adress-Bytes einsetzen
a++;
}while(--ll);
if (NT_SUCCESS(FlushBuffer(X,(PUCHAR)ret,len,7))) ok++;
ex:
KeReleaseMutex( & X- & gt; bmutex,FALSE);
}
IoReleaseRemoveLock( & X- & gt; rlock,NULL);
}
if (irql) KeRaiseIrql(irql, & irql); // GALEP: wieder anheben
Vlpt_KdPrint2(( " HandleIn(%03Xh) liefert %0*Xh\n " ,adr,2*len,*ret & mask));
if (!ok) {
DbgPrint( " HandleIn(%03Xh) versagt! IRQL=%u\n " ,adr,KeGetCurrentIrql());
TRAP();
}
return ok;
}
// Aufruf vom Kernel, wenn Schreibcache-Haltezeit abgelaufen ist
VOID TimerDpc(IN PKDPC Dpc,PDEVICE_EXTENSION X,PVOID a,PVOID b) {
ASSERT(KeGetCurrentIrql()==DISPATCH_LEVEL);
Vlpt_KdPrint2(( " TimerDpc, Ausgabe %u Bytes\n " ,X- & gt; bfill));
FlushBuffer(X,NULL,0,1);
}
/****************************
** Synchrone USBD-Aufrufe **
****************************/
NTSTATUS CallUSBD(PDEVICE_EXTENSION X, PURB U) {
/* Passes a Usb Request Block (URB) to the USB class driver (USBD)
Diese Routine blockiert!! IRQL=0!!
Ein sinnvolles TimeOut wäre hier dringend angeraten!!??
*/
NTSTATUS ret;
PIRP I;
PIO_STACK_LOCATION nextStack;
KEVENT ev;
IO_STATUS_BLOCK ios;
Vlpt_KdPrint2(( " Aufruf CallUSBD\n " ));
ASSERT(!KeGetCurrentIrql());
KeInitializeEvent( & ev,NotificationEvent,FALSE);
I=IoBuildDeviceIoControlRequest( //IRQL=0
IOCTL_INTERNAL_USB_SUBMIT_URB,X- & gt; ldo,NULL,0,NULL,0,TRUE, & ev, & ios);
ASSERT(I);
if (!I) return STATUS_INSUFFICIENT_RESOURCES;
nextStack=IoGetNextIrpStackLocation(I);
ASSERT(nextStack);
nextStack- & gt; Parameters.Others.Argument1=U;
ret=IoCallDriver(X- & gt; ldo,I);
Vlpt_KdPrint2(( " IoCallDriver(USBD) returns %x\n " ,ret));
if (ret==STATUS_PENDING) {
KeWaitForSingleObject( & ev,Suspended,KernelMode,FALSE,NULL);
ret=ios.Status; //IRQL=0 sonst Crash!
}
#if DBG
if (U- & gt; UrbHeader.Status || ret) // im Fehlerfall zucken!
Vlpt_KdPrint(( " URB status %X, IRP status %X\n " ,U- & gt; UrbHeader.Status,ret));
#endif
if (!USBD_SUCCESS(U- & gt; UrbHeader.Status)) {
X- & gt; LastFailedUrbStatus=U- & gt; UrbHeader.Status;
if (NT_SUCCESS(ret)) ret=STATUS_UNSUCCESSFUL;
}
return ret;
}
NTSTATUS SelectInterfaces(PDEVICE_EXTENSION X,
//XREF: ConfigureDevice
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
IN PUSBD_INTERFACE_INFORMATION Interface) {
/*
Initializes an Vlpt Device with multiple interfaces
Arguments:
fdo - this instance of the Vlpt Device
ConfigurationDescriptor
- the USB configuration descriptor containing the interface
and endpoint descriptors.
Interface - pointer to a USBD Interface Information Object
- If this is NULL, then this driver must choose its interface
based on driver-specific criteria, and the driver must also
CONFIGURE the device.
- If it is NOT NULL, then the driver has already been given
an interface and the device has already been configured by
the parent of this device driver.
Return Value:
NT status code
*/
NTSTATUS ntStatus;
PURB urb;
ULONG j;
// UCHAR alternateSetting /*, MyInterfaceNumber*/;
PUSBD_INTERFACE_INFORMATION interfaceObject;
USBD_INTERFACE_LIST_ENTRY interfaceList[2];
Vlpt_KdPrint2(( " enter SelectInterfaces\n " ));
// MyInterfaceNumber = SAMPLE_INTERFACE_NBR;
// Search the configuration descriptor for the first interface/alternate setting
interfaceList[0].InterfaceDescriptor =
USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor,
ConfigurationDescriptor,
-1, // Interface - don't care
-1, // Alternate Setting - don't care
-1, // Class - don't care
-1, // SubClass - don't care
-1); // Protocol - don't care
ASSERT(interfaceList[0].InterfaceDescriptor);
interfaceList[1].InterfaceDescriptor=NULL;
interfaceList[1].Interface=NULL;
urb=USBD_CreateConfigurationRequestEx(ConfigurationDescriptor, & interfaceList[0]);
if (!urb) Vlpt_KdPrint(( " USBD_CreateConfigurationRequestEx failed\n " ));
// DumpBuffer(urb, urb- & gt; UrbHeader.Length);
interfaceObject=(PUSBD_INTERFACE_INFORMATION) ( & (urb- & gt; UrbSelectConfiguration.Interface));
// We set up a default max transfer size for the endpoints. Your driver will
// need to change this to reflect the capabilities of your device's endpoints.
for (j=0; j & lt; interfaceList[0].InterfaceDescriptor- & gt; bNumEndpoints; j++)
interfaceObject- & gt; Pipes[j].MaximumTransferSize = (64 * 1024) - 1;
ntStatus=CallUSBD(X, urb);
// DumpBuffer(urb, urb- & gt; UrbHeader.Length);
if (NT_SUCCESS(ntStatus) & & USBD_SUCCESS(urb- & gt; UrbHeader.Status)) {
// Save the configuration handle for this device
// X- & gt; ConfigurationHandle=urb- & gt; UrbSelectConfiguration.ConfigurationHandle;
X- & gt; Interface=AllocFixed(interfaceObject- & gt; Length,'tplV');
// save a copy of the interfaceObject information returned
RtlCopyMemory(X- & gt; Interface,interfaceObject,interfaceObject- & gt; Length);
// Dump the interfaceObject to the debugger
Vlpt_KdPrint (( " ---------\n " ));
Vlpt_KdPrint (( " NumberOfPipes %d\n " , X- & gt; Interface- & gt; NumberOfPipes));
Vlpt_KdPrint (( " Length %d\n " , X- & gt; Interface- & gt; Length));
Vlpt_KdPrint (( " Alt Setting 0x%x\n " ,X- & gt; Interface- & gt; AlternateSetting));
Vlpt_KdPrint (( " Interface Number 0x%x\n " ,X- & gt; Interface- & gt; InterfaceNumber));
// Dump the pipe info
for (j=0; j & lt; interfaceObject- & gt; NumberOfPipes; j++) {
PUSBD_PIPE_INFORMATION i;
i= & X- & gt; Interface- & gt; Pipes[j];
Vlpt_KdPrint (( " ---------\n " ));
Vlpt_KdPrint (( " PipeType 0x%x\n " ,i- & gt; PipeType));
Vlpt_KdPrint (( " EndpointAddress 0x%x\n " ,i- & gt; EndpointAddress));
Vlpt_KdPrint (( " MaxPacketSize %d\n " , i- & gt; MaximumPacketSize));
Vlpt_KdPrint (( " Interval %d\n " , i- & gt; Interval));
Vlpt_KdPrint (( " Handle 0x%x\n " ,i- & gt; PipeHandle));
Vlpt_KdPrint (( " MaxTransferSize %d\n " , i- & gt; MaximumTransferSize));
}
Vlpt_KdPrint (( " ---------\n " ));
}
Vlpt_KdPrint2(( " leave SelectInterfaces (%x)\n " ,ntStatus));
return ntStatus;
}
NTSTATUS ConfigureDevice(PDEVICE_EXTENSION X) {
//XREF: StartDevice
/*
Routine Description:
Configures the USB device via USB-specific device requests and interaction
with the USB software subsystem.
Arguments:
fdo - pointer to the device object for this instance of the Vlpt Device
Return Value:
NT status code
*/
NTSTATUS ntStatus=STATUS_NO_MEMORY;
// PURB urb=NULL;
struct _URB_CONTROL_DESCRIPTOR_REQUEST urb;
PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor=NULL;
ULONG siz;
Vlpt_KdPrint2(( " enter ConfigureDevice\n " ));
// Get memory for the USB Request Block (urb).
// if (!Alloc( & urb,sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST))) goto NoMemory;
//
// Set size of the data buffer. Note we add padding to cover hardware faults
// that may cause the device to go past the end of the data buffer
//
siz=sizeof(USB_CONFIGURATION_DESCRIPTOR)+16;
// Get the nonpaged pool memory for the data buffer
if (!(configurationDescriptor=AllocFixed(siz,'tplV'))) goto NoMemory;
UsbBuildGetDescriptorRequest((PURB) & urb,
(USHORT)sizeof(urb),
USB_CONFIGURATION_DESCRIPTOR_TYPE,0,0,configurationDescriptor,
NULL,sizeof(USB_CONFIGURATION_DESCRIPTOR),/* Get only the configuration descriptor */
NULL);
ntStatus=CallUSBD(X,(PURB) & urb);
if (NT_SUCCESS(ntStatus)) {
Vlpt_KdPrint2(( " Configuration Descriptor is at %x, bytes txferred: %d\n "
" Configuration Descriptor Actual Length: %d\n " ,
configurationDescriptor,
urb./*UrbControlDescriptorRequest.*/TransferBufferLength,
configurationDescriptor- & gt; wTotalLength));
}//if
siz=configurationDescriptor- & gt; wTotalLength+16; // Größe merken
ExFreePoolWithTag(configurationDescriptor,'tplV'); // bisherigen Deskriptor verwerfen
ntStatus=STATUS_NO_MEMORY;
if (!(configurationDescriptor=AllocFixed(siz,'tplV'))) goto NoMemory; // neu allozieren
UsbBuildGetDescriptorRequest((PURB) & urb,
(USHORT)sizeof(urb),
USB_CONFIGURATION_DESCRIPTOR_TYPE,0,0,configurationDescriptor,
NULL,siz, /* Get all the descriptor data*/ NULL);
ntStatus=CallUSBD(X,(PURB) & urb);
if (NT_SUCCESS(ntStatus)) {
Vlpt_KdPrint2(( " Entire Configuration Descriptor is at %x, bytes txferred: %d\n " ,
configurationDescriptor,
urb./*UrbControlDescriptorRequest.*/TransferBufferLength));
// We have the configuration descriptor for the configuration
// we want.
// Now we issue the SelectConfiguration command to get
// the pipes associated with this configuration.
ntStatus=SelectInterfaces(X,configurationDescriptor,NULL); // NULL=Device not yet configured
}
NoMemory:
ExFreePoolWithTag(configurationDescriptor,'tplV');
// Free( & urb);
Vlpt_KdPrint2(( " leave ConfigureDevice (%x)\n " , ntStatus));
return ntStatus;
}
/***************************
** Aktivierung der Traps **
***************************/
void FreeTraps(PDEVICE_EXTENSION X) {
int i;
// TRAP();
// Debugregister-Anzapfung löschen
if (CurThreadPtr & & X- & gt; instance & lt; 3) { // Patch auf 9x beschränken
*(PUSHORT)(0x408+X- & gt; instance*2)=X- & gt; oldlpt;
*(PUSHORT)(0x410)=X- & gt; oldsys;
}
for (i=0; i & lt; 3; i++) if (X- & gt; debugreg[i]) {
FreeDRN(X- & gt; debugreg[i]-1);
X- & gt; debugreg[i]=0;
}
X- & gt; mirror[0]=0; // Spiegel löschen
// READ_PORT_UCHAR-Anzapfung löschen
// if (!(X- & gt; f & No_Function)) { // Anzapfung existent?
// UnhookSyscalls(); // Anzapfung aufheben
X- & gt; f|=No_Function;
// }
}
void SetTraps(PDEVICE_EXTENSION X) {
int e;
TRAP();
// READ_PORT_UCHAR-Anzapfung setzen
// if (X- & gt; uc.flags & UC_Function) { // Anzapfung soll gesetzt sein
// HookSyscalls(); // Anzapfung setzen
X- & gt; f & =~No_Function;
// }
// Debugregister-Anzapfung setzen
if (CurThreadPtr & & X- & gt; instance & lt; 3) { // Patch auf 9x beschränken
register PUSHORT a1=(PUSHORT)(0x408+X- & gt; instance*2);
X- & gt; oldlpt=*a1;
X- & gt; oldsys=*(PUSHORT)(0x410);
*a1=X- & gt; uc.LptBase;
if (X- & gt; oldsys & gt; & gt; 14 & lt; = X- & gt; instance) {
*(PUSHORT)(0x410)=X- & gt; oldsys & 0x3FFF | ((X- & gt; instance+1) & lt; & lt; 14);
}
}
e=AllocDR(X- & gt; uc.LptBase,X); // Debugregister anfordern
if (e & gt; =0) X- & gt; debugreg[0]=e+1; // abspeichern
else Vlpt_KdPrint(( " Kann LptBase (SPP) nicht anzapfen (%d)\n " ,e));
if (X- & gt; uc.Mode & 1) { // EPP
e=AllocDR((USHORT)(X- & gt; uc.LptBase+4),X);
if (e & gt; =0) X- & gt; debugreg[1]=e+1;
else Vlpt_KdPrint(( " Kann LptBase+4 (EPP) nicht anzapfen (%d)\n " ,e));
}
if (X- & gt; uc.Mode & 2) { // ECP
e=AllocDR((USHORT)(X- & gt; uc.LptBase+0x400),X);
if (e & gt; =0) X- & gt; debugreg[2]=e+1;
else Vlpt_KdPrint(( " Kann LptBase+400h (ECP) nicht anzapfen (%d)\n " ,e));
}
}
NTSTATUS StartDevice(PDEVICE_EXTENSION X) {
//XREF: HandleStartDevice
/* Initializes a given instance of the Vlpt Device on the USB.
fdo - pointer to the device object for this instance of a Vlpt Device*/
NTSTATUS ntStatus;
PUSB_DEVICE_DESCRIPTOR DDesc;
// PURB urb;
struct _URB_CONTROL_DESCRIPTOR_REQUEST U;
// const ULONG siz=sizeof(USB_DEVICE_DESCRIPTOR);
Vlpt_KdPrint (( " enter StartDevice\n " ));
// Get some memory from then non paged pool (fixed, locked system memory)
// for use by the USB Request Block (urb) for the specific USB Request we
// will be performing below (a USB device request).
// if (Alloc( & urb,sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST))) {
// Get some non paged memory for the device descriptor contents
if ((DDesc=AllocFixed(sizeof(*DDesc),'tplV'))) {
// Use a macro in the standard USB header files to build the URB
UsbBuildGetDescriptorRequest((PURB) & U,sizeof(U),
USB_DEVICE_DESCRIPTOR_TYPE,0,0,DDesc,NULL,sizeof(*DDesc),NULL);
// Get the device descriptor
ntStatus=CallUSBD(X,(PURB) & U);
// Dump out the descriptor info to the debugger
if (NT_SUCCESS(ntStatus)) {
Vlpt_KdPrint((
" Device Descriptor = %x, len %x\n " ,
" Vlpt Device Descriptor:\n "
" -------------------------\n "
" bLength\t%d\n "
" bDescriptorType\t0x%x\n "
" bcdUSB\t%#x\n "
" bDeviceClass\t%#x\n "
" bDeviceSubClass\t%#x\n "
" bDeviceProtocol\t%#x\n "
" bMaxPacketSize0\t%#x\n "
" idVendor\t%#x\n "
" idProduct\t%#x\n "
" bcdDevice\t%#x\n "
" iManufacturer\t%#x\n "
" iProduct\t%#x\n "
" iSerialNumber\t%#x\n "
" bNumConfigurations\t%#x\n " ,
DDesc,U.TransferBufferLength,
DDesc- & gt; bLength,
DDesc- & gt; bDescriptorType,
DDesc- & gt; bcdUSB,
DDesc- & gt; bDeviceClass,
DDesc- & gt; bDeviceSubClass,
DDesc- & gt; bDeviceProtocol,
DDesc- & gt; bMaxPacketSize0,
DDesc- & gt; idVendor,
DDesc- & gt; idProduct,
DDesc- & gt; bcdDevice,
DDesc- & gt; iManufacturer,
DDesc- & gt; iProduct,
DDesc- & gt; iSerialNumber,
DDesc- & gt; bNumConfigurations));
}
}else ntStatus = STATUS_NO_MEMORY;
if (NT_SUCCESS(ntStatus)) {
// Put a ptr to the device descriptor in the device extension for easy
// access (like a " cached " copy). We will free this memory when the
// device is removed. See the " RemoveDevice " code.
X- & gt; DeviceDescriptor = DDesc;
X- & gt; f & =~Stopped;
}else if (DDesc) {
/*
// If the bus transaction failed, then free up the memory created to hold
// the device descriptor, since the device is probably non-functional
*/
ExFreePoolWithTag(DDesc,'tplV');
X- & gt; DeviceDescriptor=NULL;
}
// ExFreePool(urb);
// }else ntStatus = STATUS_NO_MEMORY;
// If the Get_Descriptor call was successful, then configure the device.
if (NT_SUCCESS(ntStatus)) ntStatus = ConfigureDevice(X);
if (NT_SUCCESS(ntStatus)) {
PUSBD_INTERFACE_INFORMATION ii;
PUSBD_PIPE_INFORMATION pi;
ii=X- & gt; Interface;
if (!ii) goto ex;
if (ii- & gt; NumberOfPipes & lt; 2) goto ex;
pi=ii- & gt; Pipes; // Erste Pipe = Schreiben (s.a. Firmware)
if (pi- & gt; PipeType!=UsbdPipeTypeBulk /*=2*/) goto ex;
if (pi- & gt; MaximumTransferSize & lt; 64) goto ex;
if (!(pi- & gt; PipeHandle)) goto ex;
pi=ii- & gt; Pipes+1; // Zweite Pipe = Lesen
if (pi- & gt; PipeType!=UsbdPipeTypeBulk /*=2*/
& & pi- & gt; PipeType!=UsbdPipeTypeInterrupt /*=3*/) goto ex;
if (pi- & gt; MaximumTransferSize & lt; 64) goto ex;
if (!(pi- & gt; PipeHandle)) goto ex;
KeInitializeMutex( & X- & gt; bmutex,0);
KeInitializeTimer( & X- & gt; wrcache.tmr);
KeInitializeDpc( & X- & gt; wrcache.dpc,TimerDpc,X);
KeInitializeEvent( & X- & gt; ev,NotificationEvent,TRUE);
//IoInitializeDpcRequest(X- & gt; fdo,...)
TRAP();
if (X- & gt; uc.LptBase & 0xFF00) SetTraps(X);
ex:;
}
Vlpt_KdPrint (( " leave StartDevice (%x)\n " , ntStatus));
return ntStatus;
}
NTSTATUS Quatsch(PUNICODE_STRING us,PCSZ str) {
// Unicode-String aus ASCIIZ initialisieren
// Gefüllter Unicode-String & lt; us & gt; muss mit RtlFreeUnicodeString() freigegeben werden!
ANSI_STRING as;
RtlInitAnsiString( & as,str); // svw. Länge durchzählen
return RtlAnsiStringToUnicodeString(us, & as,TRUE);
}
NTSTATUS HandleStartDevice(PDEVICE_EXTENSION X, PIRP I) {
//XREF: DispatchPnp
NTSTATUS ntStatus;
HANDLE key;
ntStatus=ForwardAndWait(X,I); // Zuerst niedere Treiber starten lassen
if (!NT_SUCCESS(IoSetDeviceInterfaceState( & X- & gt; ifname,TRUE))) {
Vlpt_KdPrint (( " IoSetDeviceInterfaceState versagt!\n " ));
TRAP();
}
if (!NT_SUCCESS(IoOpenDeviceRegistryKey(X- & gt; pdo,PLUGPLAY_REGKEY_DEVICE,
KEY_QUERY_VALUE, & key))) {
Vlpt_KdPrint (( " IoOpenDeviceRegistryKey versagt!\n " ));
TRAP();
}else{
// Benutzerdefinierte (binäre) Konfigurationsdaten ( " UserCfg " ) laden
UNICODE_STRING us;
struct {
KEY_VALUE_PARTIAL_INFORMATION v; // 3 DWORDs = 12 Bytes
UCHAR data[sizeof(TUserCfg)-1]; // insgesamt 6 Bytes Daten, Summe 18
}val;
ULONG needed;
static const CHAR Tag[]= " UserCfg " ;
if (NT_SUCCESS(Quatsch( & us,Tag))) {
TRAP();
if (NT_SUCCESS(ZwQueryValueKey(key, & us,KeyValuePartialInformation,
& val.v,sizeof(val), & needed))
& & val.v.Type==REG_BINARY) {
X- & gt; uc=*(PUserCfg)val.v.Data; // Persistente Daten kopieren
}
RtlFreeUnicodeString( & us);
}
ZwClose(key);
}
if (!NT_SUCCESS(ntStatus)) return CompleteRequest(I,ntStatus,I- & gt; IoStatus.Information);
// now do whatever we need to do to start the device
return CompleteRequest(I,StartDevice(X),0);
}
NTSTATUS StopDevice(PDEVICE_EXTENSION X) {
//XREF: DispatchPnp
NTSTATUS ret=STATUS_SUCCESS;
PURB U;
Vlpt_KdPrint2(( " enter StopDevice\n " ));
KeCancelTimer( & X- & gt; wrcache.tmr);
FreeTraps(X);
X- & gt; f|=Stopped;
#define USIZE sizeof(struct _URB_SELECT_CONFIGURATION)
U=AllocFixed(USIZE,'tplU');
if (U) {
NTSTATUS status;
UsbBuildSelectConfigurationRequest(U,USIZE,NULL); // Auf " unkonfiguriert "
status=CallUSBD(X,U);
Vlpt_KdPrint(( " Device Configuration Closed status = %x usb status = %x.\n " ,
status, U- & gt; UrbHeader.Status));
ExFreePoolWithTag(U,'tplU');
#undef USIZE
}else ret=STATUS_NO_MEMORY;
Vlpt_KdPrint2(( " leave StopDevice (%x)\n " ,ret));
return ret;
}
NTSTATUS AbortPipe(PDEVICE_EXTENSION X,IN USBD_PIPE_HANDLE PipeHandle) {
//XREF: HandleRemoveDevice, ProcessIOCTL
/* cancel pending transfers for a pipe */
NTSTATUS ntStatus;
PURB U;
Vlpt_KdPrint2(( " USB2LPT.SYS: AbortPipe \n " ));
#define USIZE sizeof(struct _URB_PIPE_REQUEST)
U=ExAllocatePoolWithTag(NonPagedPool,USIZE,'tplU');
if (U) {
RtlZeroMemory(U,USIZE);
U- & gt; UrbHeader.Length=(USHORT)USIZE;
U- & gt; UrbHeader.Function=URB_FUNCTION_ABORT_PIPE;
U- & gt; UrbPipeRequest.PipeHandle=PipeHandle;
ntStatus=CallUSBD(X,U);
ExFreePoolWithTag(U,'tplU');
#undef USIZE
}else ntStatus=STATUS_INSUFFICIENT_RESOURCES; // Wieso diesmal Resources??
return ntStatus;
}
NTSTATUS DevName(PUNICODE_STRING us,int Instance) {
// liefert " durchnummerierten " Device-String
static const CHAR deviceName[]= " \\Device\\Parallel? " ;
NTSTATUS ret;
ret=Quatsch(us,deviceName);
if (NT_SUCCESS(ret)) us- & gt; Buffer[sizeof(deviceName)-2]=(USHORT)('0'+Instance);
return ret;
}
NTSTATUS DevLink(PUNICODE_STRING us,int Instance) {
// liefert " durchnummerierten " DosDevices-String
static const CHAR deviceLink[]= " \\DosDevices\\LPT? " ;
NTSTATUS ret;
ret=Quatsch(us,deviceLink);
if (NT_SUCCESS(ret)) us- & gt; Buffer[sizeof(deviceLink)-2]=(USHORT)('1'+Instance);
return ret;
}
NTSTATUS RemoveDevice(PDEVICE_EXTENSION X) {
//XREF: HandleRemoveDevice
UNICODE_STRING ucDeviceLink;
Vlpt_KdPrint(( " RemoveDevice\n " ));
if (X- & gt; DeviceDescriptor) ExFreePoolWithTag(X- & gt; DeviceDescriptor,'tplV');
// Free up any interface structures in our device extension
if (X- & gt; Interface) ExFreePoolWithTag(X- & gt; Interface,'tplV');
// remove the device object's symbolic link
if (NT_SUCCESS(DevLink( & ucDeviceLink,X- & gt; instance))) {
IoDeleteSymbolicLink( & ucDeviceLink);
RtlFreeUnicodeString( & ucDeviceLink);
}
RtlFreeUnicodeString( & X- & gt; ifname);
IoDetachDevice(X- & gt; ldo);
IoDeleteDevice(X- & gt; fdo);
return STATUS_SUCCESS;
}
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION X, PIRP I) {
//XREF: DispatchPnp
PUSBD_INTERFACE_INFORMATION ii=X- & gt; Interface;
ULONG i;
HANDLE key;
// set the removing flag to prevent any new I/O's
X- & gt; f|=removing;
IoSetDeviceInterfaceState( & X- & gt; ifname,FALSE);
// brute force - send an abort pipe message to all pipes to cancel any
// pending transfers. this should solve the problem of the driver blocking
// on a REMOVE message because there is a pending transfer.
if (!NT_SUCCESS(IoOpenDeviceRegistryKey(X- & gt; pdo,PLUGPLAY_REGKEY_DEVICE,
KEY_WRITE, & key))) {
Vlpt_KdPrint (( " IoOpenDeviceRegistryKey versagt!\n " ));
TRAP();
}else{
// Aktuelle Benutzer-Konfiguration retten!
UNICODE_STRING us;
static const CHAR Tag[]= " UserCfg " ;
if (NT_SUCCESS(Quatsch( & us,Tag))) {
ZwSetValueKey(key, & us,0,REG_BINARY, & X- & gt; uc,sizeof(X- & gt; uc));
RtlFreeUnicodeString( & us);
}
ZwClose(key);
}
if (ii) for (i=0; i & lt; ii- & gt; NumberOfPipes; i++) {
AbortPipe(X,(USBD_PIPE_HANDLE)ii- & gt; Pipes[i].PipeHandle);
}
IoReleaseRemoveLockAndWait( & X- & gt; rlock,NULL);
RemoveDevice(X);
return DefaultPnpHandler(X,I); // lower-level completed IoStatus already
}
#if DBG
const char* LocateStr(const char*l,int n) {
// Findet in Multi-SZ-String & lt; l & gt; den String mit Index & lt; n & gt;
// und liefert Zeiger darauf. Ist n zu groß, Zeiger auf letzte Null
for(;;n--){
if (!n || !*l) return l; // gefunden oder Liste zu Ende
while (*++l); // kein strlen zu finden!!
l++; // Hinter die Null
}
}
#endif
NTSTATUS DispatchPnp(IN PDEVICE_OBJECT fdo,IN PIRP I) {
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION X=fdo- & gt; DeviceExtension;
ULONG fcn;
NTSTATUS ret;
#if DBG
static const char MsgNames[]=
" START_DEVICE\0 "
" QUERY_REMOVE_DEVICE\0 "
" REMOVE_DEVICE\0 "
" CANCEL_REMOVE_DEVICE\0 "
" STOP_DEVICE\0 "
" QUERY_STOP_DEVICE\0 "
" CANCEL_STOP_DEVICE\0 "
" QUERY_DEVICE_RELATIONS\0 "
" QUERY_INTERFACE\0 "
" QUERY_CAPABILITIES\0 "
" QUERY_RESOURCES\0 "
" QUERY_RESOURCE_REQUIREMENTS\0 "
" QUERY_DEVICE_TEXT\0 "
" FILTER_RESOURCE_REQUIREMENTS\0 "
" ?\0 "
" READ_CONFIG\0 "
" WRITE_CONFIG\0 "
" EJECT\0 "
" SET_LOCK\0 "
" QUERY_ID\0 "
" QUERY_PNP_DEVICE_STATE\0 "
" QUERY_BUS_INFORMATION\0 "
" DEVICE_USAGE_NOTIFICATION\0 "
" SURPRISE_REMOVAL\0 " ;
#endif
ret=IoAcquireRemoveLock( & X- & gt; rlock,NULL);
if (!NT_SUCCESS(ret)) return CompleteRequest(I,ret,0);
// Get a pointer to the current location in the Irp. This is where
// the function codes and parameters are located.
irpStack=IoGetCurrentIrpStackLocation(I);
ASSERT(irpStack- & gt; MajorFunction==IRP_MJ_PNP);
fcn=irpStack- & gt; MinorFunction;
Vlpt_KdPrint(( " IRP_MJ_PNP:IRP_MN_%s\n " ,LocateStr(MsgNames,fcn)));
// TRAP();
switch (fcn) {
case IRP_MN_START_DEVICE: {
ret=HandleStartDevice(X,I);
if (NT_SUCCESS(ret)) X- & gt; f|=Started;
}break;
case IRP_MN_STOP_DEVICE: {
DefaultPnpHandler(X,I);
ret=StopDevice(X);
}break;
case IRP_MN_SURPRISE_REMOVAL: {
if (!(X- & gt; f & Stopped)) KeCancelTimer( & X- & gt; wrcache.tmr);
FreeTraps(X);
X- & gt; f|=surprise;
}goto def;
case IRP_MN_REMOVE_DEVICE: {
if (!(X- & gt; f & Stopped)) KeCancelTimer( & X- & gt; wrcache.tmr);
FreeTraps(X);
}return HandleRemoveDevice(X,I); // ohne IoReleaseRemoveLock()
case IRP_MN_QUERY_CAPABILITIES: {
// This code swiped from Walter Oney. Please buy his book!!
PDEVICE_CAPABILITIES pdc=irpStack- & gt; Parameters.DeviceCapabilities.Capabilities;
if (pdc- & gt; Version & lt; 1) goto def;
ret=ForwardAndWait(X,I);
if (NT_SUCCESS(ret)) { // IRP succeeded
pdc=irpStack- & gt; Parameters.DeviceCapabilities.Capabilities;
// setting this field prevents NT5 from notifying the user
// when the device is removed.
pdc- & gt; SurpriseRemovalOK = TRUE;
} // IRP succeeded
ret=CompleteRequest(I,ret,I- & gt; IoStatus.Information);
}break;
def:
default: ret=DefaultPnpHandler(X,I);
}
IoReleaseRemoveLock( & X- & gt; rlock,NULL);
return ret;
}
NTSTATUS DispatchPower(IN PDEVICE_OBJECT fdo,IN PIRP I) {
PDEVICE_EXTENSION X=fdo- & gt; DeviceExtension;
NTSTATUS ntStatus;
Vlpt_KdPrint (( " Vlpt_DispatchPower\n " ));
TRAP();
I- & gt; IoStatus.Status=STATUS_SUCCESS;
I- & gt; IoStatus.Information=0;
// IoSkipCurrentIrpStackLocation(I); // Geht nicht! Warum?
IoCopyCurrentIrpStackLocationToNext(I);
PoStartNextPowerIrp(I);
ntStatus=PoCallDriver(X- & gt; ldo,I);
if (ntStatus==STATUS_PENDING) IoMarkIrpPending(I);
return ntStatus;
}
NTSTATUS CreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
OUT PDEVICE_OBJECT *fdo,LONG Instance) {
/* Creates a Functional DeviceObject
Arguments:
Instance - Geräte-Nummer, null-basiert
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise
*/
NTSTATUS ntStatus;
UNICODE_STRING ucDeviceName;
UNICODE_STRING ucDeviceLink;
DevName( & ucDeviceName,Instance);
Vlpt_KdPrint(( " Gerätename: & lt; %ws & gt; \n " , ucDeviceName.Buffer));
ntStatus=IoCreateDevice(DriverObject,sizeof(DEVICE_EXTENSION),
& ucDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,fdo);
if (NT_SUCCESS(ntStatus)) {
if (NT_SUCCESS(DevLink( & ucDeviceLink,Instance))) {
Vlpt_KdPrint(( " DOS-Gerätename: & lt; %ws & gt; \n " ,ucDeviceLink.Buffer));
IoCreateSymbolicLink( & ucDeviceLink, & ucDeviceName);
RtlFreeUnicodeString( & ucDeviceLink);
}
}
RtlFreeUnicodeString( & ucDeviceName);
return ntStatus;
}
// Überfällige Hilfsroutine
NTSTATUS AllocUnicodeString(PUNICODE_STRING us,ULONG len) {
us- & gt; Length=0;
us- & gt; MaximumLength=(USHORT)len;
us- & gt; Buffer=ExAllocatePoolWithTag(PagedPool,len,'TPLV');
return us- & gt; Buffer ? STATUS_SUCCESS : STATUS_NO_MEMORY;
}
void SetFriendlyName(PDEVICE_EXTENSION X) {
// Setzt " FriendlyName " und " PortName " - das genügt PonyProg2000 für W98
// Eigentlich müssen noch Ressourcen angemeldet werden!
HANDLE k;
PWCHAR buf;
ULONG r;
UNICODE_STRING us,ulpt;
static const CHAR lpt[]= " LPT? " ; // sollte MSVC zusammenlegen
if (NT_SUCCESS(Quatsch( & ulpt,lpt))) {
ulpt.Buffer[sizeof(lpt)-2]=(USHORT)('1'+X- & gt; instance);
if (IoGetDeviceProperty(X- & gt; pdo,DevicePropertyDeviceDescription,
0,NULL, & r)==STATUS_BUFFER_TOO_SMALL) {
r+=16; // Platz für " (LPTx) " und Luft für später mehr als 9 LPTs
if (NT_SUCCESS(AllocUnicodeString( & us,r))) {
buf=us.Buffer;
if (NT_SUCCESS(IoGetDeviceProperty(X- & gt; pdo,
DevicePropertyDeviceDescription,r,buf, & r))) {
us.Length=(USHORT)(r-2);
if (NT_SUCCESS(IoOpenDeviceRegistryKey(X- & gt; pdo,
PLUGPLAY_REGKEY_DEVICE,KEY_WRITE, & k))) {
//W98: (keine Device-Parameter!)
//W2K: CCS/Enum/USB/Vid_5348 & Pid_2131 & MI_00/5 & 3A843828 & 1 & 0/Device Parameters
//W2K: FriendlyName wird (offenbar) ein Verzeichnis hoch kopiert, PortName nicht
//WXP: Nichts funktioniert wie unter W2K! Man müsste ein Verzeichnis hochkommen...
// Vielleicht mit ZwQueryKey()?
RtlAppendUnicodeToString( & us,L " ( " );
RtlAppendUnicodeStringToString( & us, & ulpt);
RtlAppendUnicodeToString( & us,L " ) " );
r=us.Length+2;
if (NT_SUCCESS(Quatsch( & us, " FriendlyName " ))) {
ZwSetValueKey(k, & us,0,REG_SZ,buf,r);
RtlFreeUnicodeString( & us);
}
if (NT_SUCCESS(Quatsch( & us, " PortName " ))) {
ZwSetValueKey(k, & us,0,REG_SZ,ulpt.Buffer,ulpt.Length+2);
RtlFreeUnicodeString( & us);
}
ZwClose(k);
}
}
ExFreePool(buf);
}
}
RtlFreeUnicodeString( & ulpt);
}
return;
}
#define MAX_VLPT_DEVICES 9
static const USHORT Ports[]={0x378,0x278,0x3BC};
NTSTATUS AddDevice(IN PDRIVER_OBJECT drv,IN PDEVICE_OBJECT pdo) {
/* This routine is called to create a new instance of the device
Arguments:
DriverObject - driver object for this instance of Vlpt
pdo - a device object created by the bus
*/
NTSTATUS ntStatus = STATUS_SUCCESS,j;
PDEVICE_OBJECT fdo;
PDEVICE_EXTENSION X;
int instance;
// char e;
Vlpt_KdPrint2(( " enter AddDevice\n " ));
instance=-1;
do{
++instance;
if (CurThreadPtr & & instance & lt; 3
& & *(PUSHORT)(0x408+instance*2)) ntStatus=-1; // nur 9x
else ntStatus=CreateDeviceObject(drv, & fdo,instance);
}while (!NT_SUCCESS(ntStatus) & & (instance & lt; MAX_VLPT_DEVICES-1));
if (NT_SUCCESS(ntStatus)) {
fdo- & gt; Flags|=DO_DIRECT_IO|DO_POWER_PAGABLE;
X=fdo- & gt; DeviceExtension;
RtlZeroMemory(X,sizeof(DEVICE_EXTENSION));
X- & gt; instance=instance;
X- & gt; f=No_Function; // noch keine READ_PORT_UCHAR-Anzapfung
X- & gt; fdo=fdo; // Rück-Bezug setzen
X- & gt; pdo=pdo; // Physikalisches Gerät (Bustreiber) setzen
j=IoRegisterDeviceInterface(pdo, & Vlpt_GUID,NULL, & X- & gt; ifname);
if (NT_SUCCESS(j)) {
Vlpt_KdPrint(( " Interface-Name: & lt; %ws & gt; , Ergebnis %d\n " ,X- & gt; ifname.Buffer,j));
}
IoInitializeRemoveLock( & X- & gt; rlock,0,1,100);
X- & gt; ldo=IoAttachDeviceToDeviceStack(fdo,pdo); // niederes Gerät ankoppeln
ASSERT(X- & gt; ldo);
if (X- & gt; instance & lt; elemof(Ports)) {
X- & gt; uc.LptBase=Ports[X- & gt; instance]; // Vorgaben setzen
X- & gt; uc.TimeOut=200;
X- & gt; uc.flags=UC_Function|UC_WriteCache;
if (!IoIsWdmVersionAvailable(1,10)) X- & gt; uc.flags|=UC_Debugreg;
}
SetFriendlyName(X);
fdo- & gt; Flags & =~DO_DEVICE_INITIALIZING;
}
Vlpt_KdPrint2(( " leave AddDevice (%x)\n " , ntStatus));
return ntStatus;
}
NTSTATUS ProcessIOCTL(IN PDEVICE_OBJECT dev,IN PIRP I) {
PIO_STACK_LOCATION irpStack;
PVOID iob;
ULONG il; // Eingabedatenlänge (OUT-Befehle und IN-Adressen)
ULONG ol; // Ausgabedatenlänge (IN-Daten)
ULONG cc;
PDEVICE_EXTENSION X=dev- & gt; DeviceExtension;
NTSTATUS ret;
ULONG rlen=0; // im Standardfall keine Bytes zurück
Vlpt_KdPrint2(( " IRP_MJ_DEVICE_CONTROL\n " ));
if (X- & gt; f & removing) {
ret=STATUS_DEVICE_REMOVED;
goto exi;
}
ret=IoAcquireRemoveLock( & X- & gt; rlock,NULL);
if (!NT_SUCCESS(ret)) goto exi;
irpStack=IoGetCurrentIrpStackLocation(I);
cc=irpStack- & gt; Parameters.DeviceIoControl.IoControlCode;
iob=I- & gt; AssociatedIrp.SystemBuffer;
il=irpStack- & gt; Parameters.DeviceIoControl.InputBufferLength;
ol=irpStack- & gt; Parameters.DeviceIoControl.OutputBufferLength;
ret=STATUS_SUCCESS;
switch (cc) {
case IOCTL_VLPT_UserCfg: {
Vlpt_KdPrint(( " IOCTL_VLPT_UserCfg\n " ));
if (il==sizeof(TUserCfg)
& & RtlCompareMemory( & X- & gt; uc,iob,sizeof(TUserCfg))!=sizeof(TUserCfg)) {
FreeTraps(X);
X- & gt; uc=*(PUserCfg)iob; // Konfiguration setzen
SetTraps(X);
}
if (ol==sizeof(TUserCfg)) {
*(PUserCfg)iob=X- & gt; uc; // Konfiguration lesen
rlen=sizeof(TUserCfg);
}
}break;
case IOCTL_VLPT_AccessCnt: {
Vlpt_KdPrint(( " IOCTL_VLPT_AccessCnt\n " ));
if (il & gt; sizeof(TAccessCnt)) il=sizeof(TAccessCnt); // begrenzen
if (il) {
KIRQL o;
KeRaiseIrql(DISPATCH_LEVEL, & o); // Kontextwechsel verhindern
RtlCopyMemory( & X- & gt; ac,iob,il); // Zugriffszähler (null)setzen
KeLowerIrql(o);
}
if (ol & gt; sizeof(TAccessCnt)) ol=sizeof(TAccessCnt); // begrenzen
RtlCopyMemory(iob, & X- & gt; ac,ol); // Zugriffszähler lesen
rlen=ol;
}break;
case IOCTL_VLPT_OutIn: { // Bedienung der beiden Pipes
Vlpt_KdPrint(( " IOCTL_VLPT_OutIn\n " ));
ret=OutInCheck(X,il,ol); // il und ol sind absichtlich vertauscht!
if (!NT_SUCCESS(ret)) break;
IoMarkIrpPending(I); // Eigentlich per Mutex Sequenz schützen!?
ret=KeWaitForMutexObject( & X- & gt; bmutex,Executive,KernelMode,FALSE,NULL);
if (!NT_SUCCESS(ret)) break;
KeWaitForSingleObject( & X- & gt; ev,Suspended,KernelMode,FALSE,NULL);
FlushBuffer(X,NULL,0,5); // Puffer ausräumen, Timer killen
ret=OutIn(X,iob,il,iob,ol, & rlen,I);
KeReleaseMutex( & X- & gt; bmutex,FALSE);
}break;
case IOCTL_VLPT_AbortPipe: { // Eine Pipe abbrechen
Vlpt_KdPrint(( " IOCTL_VLPT_AbortPipe\n " ));
if (ol & gt; =sizeof(ULONG)) {
AbortPipe(X,X- & gt; Interface- & gt; Pipes[*(PULONG)iob].PipeHandle);
}else ret=STATUS_INVALID_PARAMETER;
}break;
case IOCTL_VLPT_GetLastError: { // Letzter USB-Fehler
Vlpt_KdPrint(( " IOCTL_VLPT_GetLastError\n " ));
if (ol & gt; =sizeof(ULONG)) { // Nur wenn Puffer groß genug ist!
*((PULONG)iob)=X- & gt; LastFailedUrbStatus;
rlen=sizeof(ULONG);
}else ret=STATUS_INVALID_PARAMETER;
}break;
case IOCTL_VLPT_AnchorDownload:
case IOCTL_VLPT_EepromRead:
case IOCTL_VLPT_EepromWrite:
case IOCTL_VLPT_XramRead:
case IOCTL_VLPT_XramWrite: {
ULONG a=0; // wValue (Low-Teil) und wIndex (High-Teil)
PURB U;
Vlpt_KdPrint(( " IOCTL_VLPT_%X\n " ,(UCHAR)(cc & gt; & gt; 2)));
if (il & gt; 4) il=4;
RtlCopyBytes( & a,iob,il);
#define USIZE sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)
U=ExAllocatePoolWithTag(NonPagedPool,USIZE,'tplU');
if (!U) return STATUS_NO_MEMORY;
UsbBuildVendorRequest(U,URB_FUNCTION_VENDOR_DEVICE,USIZE,
cc & METHOD_OUT_DIRECT ? USBD_TRANSFER_DIRECTION_IN : 0,
0,(UCHAR)(cc & gt; & gt; 2),(USHORT)a,(USHORT)(a & gt; & gt; 16),
NULL,I- & gt; MdlAddress,ol,NULL);
ret=AsyncCallUSBD(X,I,U);
if (ret!=STATUS_PENDING) {
Vlpt_KdPrint(( " Komplett oder nicht komplett, das ist hier die Frage!\n " ));
TRAP();
IoReleaseRemoveLock( & X- & gt; rlock,NULL);
return ret;
}
#undef USIZE
}break;
default: ret=STATUS_INVALID_PARAMETER;
}
IoReleaseRemoveLock( & X- & gt; rlock,NULL);
exi:
if (ret==STATUS_PENDING) return ret;
return CompleteRequest(I,ret,rlen);
}
NTSTATUS Create(IN PDEVICE_OBJECT dev,IN PIRP I) {
// Aufruf von CreateFile() (könnte " \\.\Vlpt-x\yyzz " sein).
PDEVICE_EXTENSION X=(PDEVICE_EXTENSION)dev- & gt; DeviceExtension;
Vlpt_KdPrint2(( " Enter Create()\n " ));
if (!X- & gt; f & Started) {
Vlpt_KdPrint2(( " Öffnen ohne zu starten!\n " ));
TRAP();
return CompleteRequest(I,STATUS_UNSUCCESSFUL,0);
}
return CompleteRequest(I,STATUS_SUCCESS,0);
}
NTSTATUS Close(IN PDEVICE_OBJECT dev,IN PIRP I) {
// Aufrif von CloseHandle()
PDEVICE_EXTENSION X=(PDEVICE_EXTENSION)dev- & gt; DeviceExtension;
Vlpt_KdPrint2(( " Close()\n " ));
return CompleteRequest(I,STATUS_SUCCESS,0);
}
VOID Unload(IN PDRIVER_OBJECT DriverObject) {
// Wenn das letzte Gerät verschwindet, verschwindet auch der Treiber
UnhookSyscalls(); // Anzapfung aufheben
Vlpt_KdPrint2(( " Unload()\n " ));
TRAP();
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath) {
Vlpt_KdPrint (( " DriverEntry (Build: " __DATE__ " / " __TIME__ " \n " ));
TRAP();
if (IsPentium()) PrepareDR(); // setzt CurThreadPtr!
HookSyscalls(); // Anzapfung setzen
DriverObject- & gt; MajorFunction[IRP_MJ_CREATE] = Create;
DriverObject- & gt; MajorFunction[IRP_MJ_CLOSE] = Close;
DriverObject- & gt; DriverUnload = Unload;
DriverObject- & gt; MajorFunction[IRP_MJ_DEVICE_CONTROL] = ProcessIOCTL;
DriverObject- & gt; MajorFunction[IRP_MJ_PNP] = DispatchPnp;
DriverObject- & gt; MajorFunction[IRP_MJ_POWER]= DispatchPower;
DriverObject- & gt; DriverExtension- & gt; AddDevice = AddDevice;
return STATUS_SUCCESS;
}
// Für Win98DDK
#pragma once
typedef struct {
LONG ct;
LONG rm;
KEVENT ev;
}IO_REMOVE_LOCK, *PIO_REMOVE_LOCK;
#define STATUS_DEVICE_REMOVED ((NTSTATUS)0xC00002B6L)
#define IoInitializeRemoveLock(a,b,c,d) InitializeRemoveLock(a)
#define IoAcquireRemoveLock(a,b) AcquireRemoveLock(a)
#define IoReleaseRemoveLock(a,b) ReleaseRemoveLock(a)
#define IoReleaseRemoveLockAndWait(a,b) ReleaseRemoveLockAndWait(a)
VOID InitializeRemoveLock(PIO_REMOVE_LOCK);
NTSTATUS AcquireRemoveLock(PIO_REMOVE_LOCK);
VOID ReleaseRemoveLock(PIO_REMOVE_LOCK);
VOID ReleaseRemoveLockAndWait(PIO_REMOVE_LOCK);
// Eigenschaftsseiten-Lieferant für USB2LPT im Gerätemanager
// Ressource zweisprachig deutsch/englisch
#define WIN32_LEAN_AND_MEAN
#include & lt; windows.h & gt;
#include & lt; windowsx.h & gt;
#include & lt; winioctl.h & gt;
#include & lt; setupapi.h & gt;
#include & lt; shellapi.h & gt;
#include & lt; shlwapi.h & gt;
#include & lt; cfgmgr32.h & gt;
#include & lt; objbase.h & gt;
#include & lt; initguid.h & gt;
#include " usb2lpt.h "
#pragma comment(linker, " /NOD /OPT:NOWIN98 /LARGEADDRESSAWARE /RELEASE " )
typedef CONST TCHAR *PCTSTR;
typedef enum {false,true} bool;
#define nobreak
HINSTANCE hInst;
static const TCHAR HelpFileName[]= " USB2LPT.HLP " ;
typedef struct{
BYTE usage; // Benutzungszähler, für 2 Dialoge
BYTE wizard; // Install-Wizard aktiv
TUserCfg uc; // Konfiguration für Treiber (3 WORDs)
TAccessCnt ac; // Zugriffszähler aus Treiber (6 DWORDs)
HANDLE dev; // Griff zum .SYS-Treiber
HFONT bold,italic; // Für hübscheren Dialog, fette und kursive Schrift
HDEVINFO info; // brauchen wir zz. nicht: SetupDi==Holzweg!
PSP_DEVINFO_DATA sdd;
}TSetup,*PSetup;
TCHAR MBoxTitle[64];
/*********************************************
* Hilfsfunktionen, vornehmlich aus WUTILS.C *
*********************************************/
int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
TCHAR buf1[256],buf2[256];
LoadString(hInst,id,buf1,elemof(buf1));
wvsprintf(buf2,buf1,arglist);
return MessageBox(Wnd,buf2,MBoxTitle,style);
}
int _cdecl MBox(HWND Wnd, UINT id, UINT style,...) {
return vMBox(Wnd,id,style,(va_list)( & style+1));
}
UINT GetComboHex(HWND Wnd) { // Liefert Hex-Wert oder 0 bei Fehler
TCHAR s[20];
UINT ret=0;
s[0]=T('0'); s[1]=T('x'); // Ohne Präfix will StrToIntEx nicht!
GetWindowText(Wnd,s+2,elemof(s)-2);
StrToIntEx(s,STIF_SUPPORT_HEX, & ret);
return ret;
}
UINT GetCheckboxGroup(HWND Wnd, UINT u, UINT o) {
UINT v,m;
for (v=0,m=1; u & lt; =o; u++,m+=m) if (IsDlgButtonChecked(Wnd,u)==1) v|=m;
return v;
}
void SetCheckboxGroup(HWND Wnd, UINT u, UINT o, UINT v) {
for (; u & lt; =o; u++,v & gt; & gt; =1) CheckDlgButton(Wnd,u,v & 1);
}
void ChangeFonts(HWND w) {
/* Macht alle eingeklammerten () statischen Texte kursiv und
* Überschriften von Gruppenfenstern fett; der besseren Übersicht wegen.
* Fonts werden neu erzeugt, wenn die entspr. Felder in TSetup NULL sind.
* Diese beiden Fonts müssen beim Beenden freigegeben werden!
*/
HFONT normal;
LOGFONT font;
PSetup S=(PSetup)GetWindowLong(w,DWL_USER);
normal=GetWindowFont(w);
if (!S- & gt; italic) {
GetObject(normal,sizeof(font), & font);
font.lfItalic=TRUE;
S- & gt; italic=CreateFontIndirect( & font);
}
if (!S- & gt; bold) {
GetObject(normal,sizeof(font), & font);
font.lfWeight=700;
S- & gt; bold=CreateFontIndirect( & font);
}
for (w=GetWindow(w,GW_CHILD);w;w=GetNextSibling(w)) {
TCHAR s[2],cl[10]; // reicht für das erste Zeichen
GetClassName(w,cl,elemof(cl));
if (!lstrcmpi(cl,T( " STATIC " ))) {
GetWindowText(w,s,elemof(s));
if (s[0]=='(') SetWindowFont(w,S- & gt; italic,TRUE);
}
if (!lstrcmpi(cl,T( " BUTTON " ))) {
if ((GetWindowStyle(w) & 0x0F) == BS_GROUPBOX)
SetWindowFont(w,S- & gt; bold,TRUE);
}
}
}
void WM_ContextMenu_to_WM_Help(HWND Wnd, LPARAM lParam){
HELPINFO hi;
hi.cbSize=sizeof(hi);
hi.iContextType=HELPINFO_WINDOW;
hi.MousePos.x=((PPOINTS) & lParam)- & gt; x;
hi.MousePos.y=((PPOINTS) & lParam)- & gt; y;
hi.hItemHandle=WindowFromPoint(hi.MousePos);
hi.iCtrlId=GetDlgCtrlID(hi.hItemHandle);
hi.dwContextId=GetWindowContextHelpId(hi.hItemHandle);
SendMessage(Wnd,WM_HELP,0,(LPARAM)(LPHELPINFO) & hi);
}
/**********************************************
* Dialogprozedur: Lese-Cache-Feineinstellung *
**********************************************/
BOOL WINAPI ExtraDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
S=(PSetup)lParam;
SetWindowLong(Wnd,DWL_USER,lParam);
SetCheckboxGroup(Wnd,20,22,S- & gt; uc.flags & gt; & gt; UCB_ReadCache0);
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case IDOK: {
S- & gt; uc.flags & =~(7 & lt; & lt; UCB_ReadCache0);
S- & gt; uc.flags|=GetCheckboxGroup(Wnd,20,22) & lt; & lt; UCB_ReadCache0;
}nobreak;
case IDCANCEL: EndDialog(Wnd,wParam);
}
}
return FALSE;
}
bool OpenDev(PSetup S) {
TCHAR k[100],n[MAX_PATH];
if (S- & gt; dev) return true; // Ist schon offen!
if (CM_Get_Device_ID(S- & gt; sdd- & gt; DevInst,k,elemof(k),0)!=CR_SUCCESS) return false;
if (CM_Get_Device_Interface_List((LPGUID) & Vlpt_GUID,
k,n,sizeof(n),0)!=CR_SUCCESS) return false;
S- & gt; dev=CreateFile(n,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,0);
if (S- & gt; dev==INVALID_HANDLE_VALUE) {
S- & gt; dev=0; // wegen von Unix geerbter Win32-Macke!
return false;
}
return true;
}
void CloseDev(PSetup S) {
if (S- & gt; dev) CloseHandle(S- & gt; dev); S- & gt; dev=0;
}
/************************************************
* Eigenschaftsseiten-Dialogprozedur: Emulation *
************************************************/
void CheckButton13(HWND Wnd, PSetup S) {
UINT i=2;
switch (S- & gt; uc.flags & gt; & gt; UCB_ReadCache0 & 7) {
case 0: i--; nobreak;// i=0
case 7: i--; // i=1
}
CheckDlgButton(Wnd,13,i);
}
BOOL WINAPI EmulDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
static const WORD DefLpt[]={0x378,0x278,0x3BC};
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
static const TCHAR LptStd[]=T( " LPT1\0LPT2\0LPT1 anno 1985 " );
static const TCHAR LptEnh[]=T( " SPP\0EPP 1.9\0ECP\0ECP + EPP\0 " );
PCTSTR p;
TCHAR s[128];
HWND w0,w2;
int i;
S=(PSetup)((LPPROPSHEETPAGE)lParam)- & gt; lParam;
SetWindowLong(Wnd,DWL_USER,(LONG)S);
ChangeFonts(Wnd);
SetupDiGetDeviceRegistryProperty(S- & gt; info,S- & gt; sdd,SPDRP_DEVICEDESC,
NULL,MBoxTitle,sizeof(MBoxTitle),NULL);
if (S- & gt; wizard) {
SendDlgItemMessage(Wnd,10,STM_SETICON,(WPARAM)LoadIcon(0,IDI_WARNING),0);
SendDlgItemMessage(Wnd,11,STM_SETICON,(WPARAM)LoadIcon(0,IDI_INFORMATION),0);
PropSheet_SetWizButtons(GetParent(Wnd),PSWIZB_NEXT);
}
w0=GetDlgItem(Wnd,100); // Adresse
for (p=LptStd,i=0;*p;p+=lstrlen(p)+1,i++) {
wsprintf(s,T( " %Xh (%u, %s) " ),DefLpt[i],DefLpt[i],p);
ComboBox_AddString(w0,s);
}
w2=GetDlgItem(Wnd,102); // Parallelport-Erweiterung
for (p=LptEnh;*p;p+=lstrlen(p)+1) ComboBox_AddString(w2,p);
if (OpenDev(S)) {
DWORD BytesRet;
DeviceIoControl(S- & gt; dev,IOCTL_VLPT_UserCfg,
& S- & gt; uc,0, & S- & gt; uc,sizeof(TUserCfg), & BytesRet,NULL);
CloseDev(S);
}else{ // Werte aus Registry holen, wie??
MessageBeep(MB_ICONHAND);
}
for (i=0; i & lt; 3; i++) if (S- & gt; uc.LptBase==DefLpt[i]) {
ComboBox_SetCurSel(w0,i);
goto skip;
}
wsprintf(s,T( " %Xh " ),S- & gt; uc.LptBase);
SetWindowText(w0,s);
skip:
ComboBox_SetCurSel(w2,S- & gt; uc.Mode);
if (!S- & gt; wizard) {
SetCheckboxGroup(Wnd,10,12,S- & gt; uc.flags & gt; & gt; UCB_Debugreg);
SetDlgItemInt(Wnd,101,S- & gt; uc.TimeOut,FALSE);
SendMessage(Wnd,WM_COMMAND,12,0);
CheckButton13(Wnd,S);
}
/*
DWORD length=sizeof(s);
HKEY key;
key=SetupDiOpenDevRegKey(stuff- & gt; info,stuff- & gt; sdd,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ);
if (key) {
if (!RegQueryValueEx(key,T( " Portadresse " ),NULL,NULL,(LPBYTE)s, & length)) {
MessageBeep(0);
DoEnvironmentSubst(s,sizeof(s));
}
RegCloseKey(key);
} // get sample info URL
*/
}return TRUE;
case WM_CONTEXTMENU: WM_ContextMenu_to_WM_Help(Wnd,lParam); break;
case WM_HELP: {
unsigned PopupId=0;
switch (((LPHELPINFO)lParam)- & gt; iCtrlId) {
case 100: PopupId=1; break; // Adresse
case 102: PopupId=2; break; // Erweiterung
case 10:
case 11: PopupId=3; break; // Methode
case 12:
case 101:
case 13:
case 103: PopupId=4; break; // PerfOpt
}
if (PopupId) {
WinHelp(Wnd,HelpFileName,HELP_CONTEXTPOPUP,PopupId);
SetWindowLong(Wnd,DWL_MSGRESULT,1);
return TRUE;
}
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 12: EnableWindow(GetDlgItem(Wnd,101),IsDlgButtonChecked(Wnd,12)); break;
case 13: switch (IsDlgButtonChecked(Wnd,13)) {
case 2: CheckDlgButton(Wnd,13,0); nobreak; // 3. Zustand nicht klickbar!
case 0: S- & gt; uc.flags & =~(7 & lt; & lt; UCB_ReadCache0); break; // Alles aus
case 1: S- & gt; uc.flags|=7 & lt; & lt; UCB_ReadCache0; break; // Alles an
}break;
case 103: if (DialogBoxParam(hInst,MAKEINTRESOURCE(102),Wnd,ExtraDlgProc,
(LPARAM)S)==IDOK) CheckButton13(Wnd,S); break;
}break;
case WM_NOTIFY: switch (((LPNMHDR)lParam)- & gt; code){
case PSN_KILLACTIVE: { // Überprüfung der Eingabefelder!
HWND w;
UINT u;
//---
w=GetDlgItem(Wnd,100);
u=GetComboHex(w);
if (!u) {
MBox(Wnd,17,MB_OK|MB_ICONEXCLAMATION);
goto f1;
}
if (u & lt; 0x100 || u & 3 || u & gt; & gt; 16 // gewöhnliche Fehler
|| u==0x1E0 || u==0x1E4 || u==0x1F0 || u==0x1F4 || u==0x3E4 || u==0x3F4) {
MBox(Wnd,18,MB_OK|MB_ICONEXCLAMATION);
f1: SetFocus(w); // Festplattenports vor DAU schützen!!
ComboBox_SetEditSel(w,0,(UINT)-1);
goto fail;
}
S- & gt; uc.LptBase=(WORD)u;
//---
w=GetDlgItem(Wnd,102);
u=ComboBox_GetCurSel(w);
if (u & 1 & & S- & gt; uc.LptBase & 7) {// EPP geht nur mit durch 8 teilbaren Adressen!
MBox(Wnd,19,MB_OK|MB_ICONEXCLAMATION);
SetFocus(w);
goto fail;
}
S- & gt; uc.Mode=(BYTE)u;
//---
if (!S- & gt; wizard) {
BOOL OK;
w=GetDlgItem(Wnd,101);
u=GetDlgItemInt(Wnd,101, & OK,FALSE);
if (!OK || u & gt; 1000) {
MBox(Wnd,20,MB_OK|MB_ICONEXCLAMATION);
SetFocus(w);
Edit_SetSel(w,0,(UINT)-1);
goto fail;
}
S- & gt; uc.TimeOut=(WORD)u;
//---
S- & gt; uc.flags & =~(7 & lt; & lt; UCB_Debugreg);
S- & gt; uc.flags|=GetCheckboxGroup(Wnd,10,12) & lt; & lt; UCB_Debugreg;
}
break;
fail: SetWindowLong(Wnd,DWL_MSGRESULT,1); // Fokus nicht entfernen!
}return TRUE;
case PSN_WIZNEXT: {
((LPNMHDR)lParam)- & gt; code=PSN_KILLACTIVE; // Fehlende Msg nachholen??
SendMessage(Wnd,WM_NOTIFY,wParam,lParam);
}nobreak;
case PSN_APPLY: {
int i;
for (i=0; i & lt; elemof(DefLpt); i++) if (DefLpt[i]==S- & gt; uc.LptBase) goto setit;
if (MBox(Wnd,16,MB_YESNO,S- & gt; uc.LptBase)!=IDYES) {
SetWindowLong(Wnd,DWL_MSGRESULT,S- & gt; wizard?-1:PSNRET_INVALID);
return TRUE;
}
setit:
if (OpenDev(S)) {
DWORD BytesRet;
DeviceIoControl(S- & gt; dev,IOCTL_VLPT_UserCfg,
& S- & gt; uc,sizeof(TUserCfg), & S- & gt; uc,0, & BytesRet,NULL);
CloseDev(S);
}else{ // Meckern??
MessageBeep(MB_ICONHAND);
}break;
//SetupDiOpenDevRegKey
//CONFIGMG_Write_Registry_Value
}
}
}
return FALSE;
}
/************************************************
* Eigenschaftsseiten-Dialogprozedur: Statistik *
************************************************/
void UpdateEditArray(HWND Wnd, UINT u, UINT o, PULONG old, PULONG n) {
for (;u & lt; =o;u++,old++,n++) {
if (*old!=*n) {
#ifdef WIN32
SetDlgItemInt(Wnd,u,*n,FALSE);
#else
TCHAR s[32];
wsprintf(s,T( " %lu " ),*n);
SetDlgItemText(Wnd,u,s);
#endif
*old=*n;
}
}
}
BOOL WINAPI StatDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
PSetup S=(PSetup)GetWindowLong(Wnd,DWL_USER);
switch (Msg) {
case WM_INITDIALOG: {
S=(PSetup)((LPPROPSHEETPAGE)lParam)- & gt; lParam;
SetWindowLong(Wnd,DWL_USER,(LONG)S);
ChangeFonts(Wnd);
S- & gt; ac.out=S- & gt; ac.in=S- & gt; ac.fail=S- & gt; ac.steal=S- & gt; ac.wpu=S- & gt; ac.rpu=(ULONG)-1;
SendMessage(Wnd,WM_TIMER,0,0);
}return TRUE;
case WM_TIMER: if (OpenDev(S)) { // bleibt normalerweise geöffnet
DWORD BytesRet;
TAccessCnt AC;
if (DeviceIoControl(S- & gt; dev,IOCTL_VLPT_AccessCnt,
& AC,0, & AC,sizeof(AC), & BytesRet,NULL))
UpdateEditArray(Wnd,100,105,(PULONG) & S- & gt; ac,(PULONG) & AC);
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 106:
case 107: {
TAccessCnt AC=S- & gt; ac;
DWORD BytesRet;
if (LOWORD(wParam)==106) AC.out=AC.in=AC.fail=AC.steal=0;
else AC.wpu=AC.rpu=0;
if (!OpenDev(S)) break;
if (!DeviceIoControl(S- & gt; dev,IOCTL_VLPT_AccessCnt,
& AC,sizeof(AC), & AC,0, & BytesRet,NULL))
MessageBeep(MB_ICONEXCLAMATION);
}
}break;
case WM_NOTIFY: switch (((LPNMHDR)lParam)- & gt; code){
case PSN_SETACTIVE: {
SetTimer(Wnd,100,500,NULL);
}break;
case PSN_KILLACTIVE: {
KillTimer(Wnd,100);
CloseDev(S);
}break;
}break;
}
return FALSE;
}
UINT CALLBACK PageCallbackProc(HWND junk,UINT Msg, LPPROPSHEETPAGE p) {
PSetup S=(PSetup)p- & gt; lParam;
if (S) switch (Msg) {
case PSPCB_CREATE: S- & gt; usage++; break;
case PSPCB_RELEASE: if (!(--S- & gt; usage)) {
CloseDev(S);
if (S- & gt; italic)DeleteFont(S- & gt; italic);
if (S- & gt; bold) DeleteFont(S- & gt; bold);
LocalFree((HLOCAL)S);
p- & gt; lParam=0;
}
}
return TRUE;
}
/*************************
* Zwei DLL-Aufrufpunkte *
*************************/
BOOL __declspec(dllexport) CALLBACK EnumPropPages(
PSP_PROPSHEETPAGE_REQUEST p,LPFNADDPROPSHEETPAGE AddPage, LPARAM lParam) {
HPROPSHEETPAGE hpage;
PSetup S=(PSetup)LocalAlloc(LPTR,sizeof(TSetup));
PROPSHEETPAGE page={
sizeof(PROPSHEETPAGE),
PSP_USECALLBACK,
hInst, //nicht-statisch
MAKEINTRESOURCE(100),
0, // Icon
NULL, // Titel
EmulDlgProc,
(LPARAM)S, //nicht-statisch
PageCallbackProc};
S- & gt; info=p- & gt; DeviceInfoSet;
S- & gt; sdd= p- & gt; DeviceInfoData;
hpage=CreatePropertySheetPage( & page);
if (!AddPage(hpage,lParam)) DestroyPropertySheetPage(hpage);
page.pszTemplate=MAKEINTRESOURCE(101);
page.pfnDlgProc=StatDlgProc;
hpage=CreatePropertySheetPage( & page);
if (!AddPage(hpage,lParam)) DestroyPropertySheetPage(hpage);
return TRUE;
}
DWORD __declspec(dllexport) CALLBACK CoDeviceInstall(
DI_FUNCTION InstallFunction,
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
PCOINSTALLER_CONTEXT_DATA Context) {
#ifdef DIF_NEWDEVICEWIZARD_FINISHINSTALL
// Win98DDK hat Probleme!
switch (InstallFunction) {
case DIF_NEWDEVICEWIZARD_FINISHINSTALL: {
SP_NEWDEVICEWIZARD_DATA NDWD;
PSetup S=(PSetup)LocalAlloc(LPTR,sizeof(TSetup));
PROPSHEETPAGE page={
sizeof(PROPSHEETPAGE),
PSP_USECALLBACK|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE,
hInst, //nicht-statisch
MAKEINTRESOURCE(103),
0, // Icon
NULL, // Titel
EmulDlgProc,
(LPARAM)S, //nicht-statisch
PageCallbackProc,
NULL, //pcRefParent
MAKEINTRESOURCE(21),// erfordert (irgendwo) ein #define _WIN32_IE 0x0500
MAKEINTRESOURCE(22)};
S- & gt; wizard=TRUE;
S- & gt; info=DeviceInfoSet;
S- & gt; sdd= DeviceInfoData;
NDWD.ClassInstallHeader.cbSize=sizeof(SP_CLASSINSTALL_HEADER);
NDWD.ClassInstallHeader.InstallFunction=DIF_ADDPROPERTYPAGE_ADVANCED;
if (SetupDiGetClassInstallParams(DeviceInfoSet,DeviceInfoData,
& NDWD.ClassInstallHeader,sizeof(NDWD),NULL)
& & NDWD.NumDynamicPages & lt; MAX_INSTALLWIZARD_DYNAPAGES) {
NDWD.DynamicPages[NDWD.NumDynamicPages++]=
CreatePropertySheetPage( & page);
SetupDiSetClassInstallParams(DeviceInfoSet,DeviceInfoData,
& NDWD.ClassInstallHeader,sizeof(NDWD));
}else{
MessageBeep(MB_ICONEXCLAMATION);
LocalFree(S);
}
}break;
}
#endif
return NO_ERROR;
}
BOOL APIENTRY _DllMainCRTStartup(HANDLE hModule, DWORD reason, LPVOID lpReserved) {
switch (reason) {
case DLL_PROCESS_ATTACH: {
hInst=(HINSTANCE)hModule;
DisableThreadLibraryCalls(hModule);
}
}
return TRUE;
}
#include & lt; windows.h & gt;
#include & lt; winioctl.h & gt;
HANDLE hStdIn, hStdOut, hStdErr;
HANDLE hAccess;
void _cdecl printf(const char*t,...){
TCHAR buf[256];
DWORD len;
len=wvsprintf(buf,t,(va_list)( & t+1));
CharToOemBuff(buf,buf,len);
WriteConsole(hStdOut,buf,len, & len,NULL);
}
int JaNein(void){
char c;
DWORD len,OldMode;
GetConsoleMode(hStdIn, & OldMode);
SetConsoleMode(hStdIn,0);
ReadConsole(hStdIn, & c,1, & len,NULL);
printf( " %c\n " ,c);
SetConsoleMode(hStdIn,OldMode);
switch (c){
case 'J':
case 'j':
case 'Y':
case 'y': return IDYES;
case 'N':
case 'n': return IDNO;
}
return 0;
}
void _cdecl mainCRTStartup(){
BYTE data=0;
WORD adr=0;
DWORD dw;
hStdIn =GetStdHandle(STD_INPUT_HANDLE);
hStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
hStdErr=GetStdHandle(STD_ERROR_HANDLE);
printf( " Hilfsprogramm zum Löschen (Deaktivieren) der Firmware vom h#s USB2LPT-Gerät\n " );
printf( " Versionen 1.0 bis 1.4, April 2007\n\n " );
for (dw=8;dw;dw--){ // von hinten probieren
TCHAR DevName[16];
wsprintf(DevName, " \\\\.\\LPT%u " ,dw);
hAccess=CreateFile(DevName,
GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);
if (hAccess!=INVALID_HANDLE_VALUE) break;
}
if (hAccess==INVALID_HANDLE_VALUE){
printf( " USB2LPT anstecken, muss LPTx sein!\n " );
goto ende;
}
// C2-Byte zur Kontrolle lesen (B2 beim AN2131)
if (!DeviceIoControl(hAccess,
CTL_CODE(FILE_DEVICE_UNKNOWN,0x08A2,METHOD_OUT_DIRECT,FILE_ANY_ACCESS),
& adr,2, & data,1, & dw,NULL) /*|| dw!=1*/) {
printf( " Fehler beim Ausführen der Gerätesteuerung, USB2LPT nicht angesteckt?\n " );
goto ende;
}
if (data!=0xC2 & & data!=0xB2) {
printf( " Firmware ist bereits gelöscht. USB2LPT abziehen und wieder anstecken!\n " );
goto ende;
}
// C2-Byte löschen (Brücke in Rev. 2 und 3 funktioniert nicht!)
printf( " Firmware im EEPROM des USB2LPT jetzt löschen?\n "
" [Zum Beschreiben des EEPROM wird die kostenlose Entwicklungssoftware von\n "
" www.cypress.com benötigt, davon m.W. das Programm EzMr.exe.]\n "
" Erstes Byte (0x%02X) löschen (überschreiben mit 0xFF)? J/N: " ,data);
if (JaNein()!=IDYES) goto ende;
data=0xFF;
if (!DeviceIoControl(hAccess,
CTL_CODE(FILE_DEVICE_UNKNOWN,0x08A2,METHOD_IN_DIRECT,FILE_ANY_ACCESS),
& adr,2, & data,1, & dw,NULL) /*|| dw!=1*/) {
printf( " Fehler beim Ausführen der Gerätesteuerung, USBLPT nicht angesteckt?\n " );
goto ende;
}
printf( " Firmware wurde durch Überschreiben des ersten Bytes gelöscht. " );
ende:
CloseHandle(hAccess);
printf( " \nBeliebige Taste zum Programmende drücken ... " );
JaNein();
ExitProcess(0);
}
To put the new firmware into ATmega's flash ROM:
* Ensure that you have an ATmega8. ATmegaX8 currently will not work.
* This firmware has an adjanced bootloader so firmware updates
don't require a parallel port with adapter or programming device.
* When you use PonyProg2000:
- ensure that programmer settings are correct
(Avr ISP I/O, no inversions), check connection with „Probe“
- power the ATmega, e.g. by USB cable
(the computer will detect a non-functional USB device)
- load bootloadHID\main.hex as FLASH file, it will place at 1800h
- load usb2lpt6.hex as FLASH file, it will place at 0000h
- set the fuses as shown in „PonyProg Fuses.png“ picture
- connect to target device and burn all (the fuses and the flash)
- re-plug the USB cable, the h#s low-speed USB2LPT adapter should occur
* When you use avrdude (inside WinAVR):
- adopt the ./bootloadHID/Makefile to match your programmer
- connect to target device and power the ATmega chip
- run „make flash fuse lock“ in directory ./bootloadHID
- bridge pins 1 and 14 of SubD receptacle
- plug the USB cable to PC's USB port (a HID device should occur)
- run „make flash“ in directory . to burn the firmware
- remove the bridge
- unplug and re-plug USB cable, the h#s low-speed USB2LPT adapter
should occur
Note that you don't need a crystal and you don't see any oscillations
on pin PB6 and PB7.
081103
OBJECTIVE DEVELOPMENT GmbH's AVR-USB driver software is distributed under the
terms and conditions of the GNU GPL version 2, see the text below. In addition
to the requirements in the GPL, we STRONGLY ENCOURAGE you to do the following:
(1) Publish your entire project on a web site and drop us a note with the URL.
Use the form at http://www.obdev.at/avrusb/feedback.html for your submission.
(2) Adhere to minimum publication standards. Please include AT LEAST:
- a circuit diagram in PDF, PNG or GIF format
- full source code for the host software
- a Readme.txt file in ASCII format which describes the purpose of the
project and what can be found in which directories and which files
- a reference to http://www.obdev.at/avrusb/
(3) If you improve the driver firmware itself, please give us a free license
to your modifications for our commercial license offerings.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The " Program " , below,
refers to any such program or work, and a " work based on the Program "
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term " modification " .) Each licensee is addressed as " you " .
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and " any
later version " , you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM " AS IS " WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the " copyright " line and a pointer to where the full notice is found.
& lt; one line to give the program's name and a brief idea of what it does. & gt;
Copyright (C) & lt; year & gt; & lt; name of author & gt;
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a " copyright disclaimer " for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
& lt; signature of Ty Coon & gt; , 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
/* Name: usbconfig.h
* Project: AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2005-04-01
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig-prototype.h 659 2008-10-20 15:58:33Z cs $
*/
#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__
/*
General Description:
This file is an example configuration (with inline documentation) for the USB
driver. It configures AVR-USB for USB D+ connected to Port D bit 2 (which is
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may
wire the lines to any other port, as long as D+ is also wired to INT0 (or any
other hardware interrupt, as long as it is the highest level interrupt, see
section at the end of this file).
+ To create your own usbconfig.h file, copy this file to your project's
+ firmware source directory) and rename it to " usbconfig.h " .
+ Then edit it accordingly.
*/
/* ---------------------------- Hardware Config ---------------------------- */
#define USB_CFG_IOPORTNAME D
/* This is the port where the USB bus is connected. When you configure it to
* " B " , the registers PORTB, PINB and DDRB will be used.
*/
#define USB_CFG_DMINUS_BIT 4
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
#define USB_CFG_DPLUS_BIT 2
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
* This may be any bit in the port. Please note that D+ must also be connected
* to interrupt pin INT0! [You can also use other interrupts, see section
* " Optional MCU Description " below, or you can connect D- to the interrupt, as
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame
* markers every millisecond.]
*/
#define USB_CFG_CLOCK_KHZ (F_CPU/1000)
/* Clock rate of the AVR in MHz. Legal values are 12000, 12800, 15000, 16000,
* 16500 and 20000. The 12.8 MHz and 16.5 MHz versions of the code require no
* crystal, they tolerate +/- 1% deviation from the nominal frequency. All
* other rates require a precision of 2000 ppm and thus a crystal!
* Default if not specified: 12 MHz
*/
/* ----------------------- Optional Hardware Config ------------------------ */
/* #define USB_CFG_PULLUP_IOPORTNAME D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
/* #define USB_CFG_PULLUP_BIT 4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
*/
/* --------------------------- Functional Range ---------------------------- */
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0
/* Define this to 1 if you want to compile a version with two endpoints: The
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint
* number).
*/
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0
/* Define this to 1 if you want to compile a version with three endpoints: The
* default control endpoint 0, an interrupt-in endpoint 3 (or the number
* configured below) and a catch-all default interrupt-in endpoint as above.
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.
*/
#define USB_CFG_EP3_NUMBER 3
/* If the so-called endpoint 3 is used, it can now be configured to any other
* endpoint number (except 0) with this macro. Default if undefined is 3.
*/
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */
/* The above macro defines the startup condition for data toggling on the
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1.
* Since the token is toggled BEFORE sending any data, the first packet is
* sent with the oposite value of this configuration!
*/
#define USB_CFG_IMPLEMENT_HALT 0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
* for endpoint 1 (interrupt endpoint). Although you may not need this feature,
* it is required by the standard. We have made it a config option because it
* bloats the code considerably.
*/
#define USB_CFG_INTR_POLL_INTERVAL 10
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
* interval. The value is in milliseconds and must not be less than 10 ms for
* low speed devices.
*/
#define USB_CFG_IS_SELF_POWERED 0
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
* device is powered from the USB bus.
*/
#define USB_CFG_MAX_BUS_POWER 100
/* Set this variable to the maximum USB bus power consumption of your device.
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
#define USB_CFG_IMPLEMENT_FN_WRITE 0
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.
*/
#define USB_CFG_IMPLEMENT_FN_READ 0
/* Set this to 1 if you need to send control replies which are generated
* " on the fly " when usbFunctionRead() is called. If you only want to send
* data from a static buffer, set it to 0 and return the data from
* usbFunctionSetup(). This saves a couple of bytes.
*/
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
* You must implement the function usbFunctionWriteOut() which receives all
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number
* can be found in 'usbRxToken'.
*/
#define USB_CFG_HAVE_FLOWCONTROL 0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in
* usbdrv.h.
*/
#define USB_CFG_LONG_TRANSFERS 0
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
* in a single control-in or control-out transfer. Note that the capability
* for long transfers increases the driver size.
*/
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it is
* defined, it's inserted at the beginning of received message processing.
* If you eat the received message and don't want default processing to
* proceed, do a return after doing your things. One possible application
* (besides debugging) is to flash a status LED on each packet.
*/
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */
/* This macro is a hook if you need to know when an USB RESET occurs. It has
* one parameter which distinguishes between the start of RESET state and its
* end.
*/
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
* received.
*/
#define USB_COUNT_SOF 0
/* define this macro to 1 if you need the global variable " usbSofCount " which
* counts SOF packets. This feature requires that the hardware interrupt is
* connected to D- instead of D+.
*/
/* #ifdef __ASSEMBLER__
* macro myAssemblerMacro
* in YL, TCNT0
* sts timer0Snapshot, YL
* endm
* #endif
* #define USB_SOF_HOOK myAssemblerMacro
* This macro (if defined) is executed in the assembler module when a
* Start Of Frame condition is detected. It is recommended to define it to
* the name of an assembler macro which is defined here as well so that more
* than one assembler instruction can be used. The macro may use the register
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages
* immediately after an SOF pulse may be lost and must be retried by the host.
* What can you do with this hook? Since the SOF signal occurs exactly every
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in
* designs running on the internal RC oscillator.
* Please note that Start Of Frame detection works only if D- is wired to the
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!
*/
#define USB_CFG_CHECK_DATA_TOGGLING 0
/* define this macro to 1 if you want to filter out duplicate data packets
* sent by the host. Duplicates occur only as a consequence of communication
* errors, when the host does not receive an ACK. Please note that you need to
* implement the filtering yourself in usbFunctionWriteOut() and
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable
* for each control- and out-endpoint to check for duplicate packets.
*/
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0
/* define this macro to 1 if you want the function usbMeasureFrameLength()
* compiled in. This function can be used to calibrate the AVR's RC oscillator.
*/
/* -------------------------- Device Description --------------------------- */
#define USB_CFG_VENDOR_ID 0xc0, 0x16
/* USB vendor ID for the device, low byte first. If you have registered your
* own Vendor ID, define it here. Otherwise you use one of obdev's free shared
* VID/PID pairs. Be sure to read USBID-License.txt for rules!
* + This template uses obdev's shared VID/PID pair: 0x16c0/0x5dc.
* + Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_ID 0xdc, 0x05
/* This is the ID of the product, low byte first. It is interpreted in the
* scope of the vendor ID. If you have registered your own VID with usb.org
* or if you have licensed a PID from somebody else, define it here. Otherwise
* you use obdev's free shared VID/PID pair. Be sure to read the rules in
* USBID-License.txt!
* + This template uses obdev's shared VID/PID pair: 0x16c0/0x5dc.
* + Use this VID/PID pair ONLY if you understand the implications!
*/
#define USB_CFG_DEVICE_VERSION 0x00, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'
#define USB_CFG_VENDOR_NAME_LEN 8
/* These two values define the vendor name returned by the USB device. The name
* must be given as a list of characters under single quotes. The characters
* are interpreted as Unicode (UTF-16) entities.
* If you don't want a vendor name string, undefine these macros.
* ALWAYS define a vendor name containing your Internet domain name if you use
* obdev's free shared VID/PID pair. See the file USBID-License.txt for
* details.
*/
#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e'
#define USB_CFG_DEVICE_NAME_LEN 8
/* Same as above for the device name. If you don't want a device name, undefine
* the macros. See the file USBID-License.txt before you assign a name if you
* use a shared VID/PID.
*/
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Same as above for the serial number. If you don't want a serial number,
* undefine the macros.
* It may be useful to provide the serial number through other means than at
* compile time. See the section about descriptor properties below for how
* to fine tune control over USB descriptors such as the string descriptor
* for the serial number.
*/
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
* Class 0xff is " vendor specific " .
*/
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */
#define USB_CFG_INTERFACE_SUBCLASS 0
#define USB_CFG_INTERFACE_PROTOCOL 0
/* See USB specification if you want to conform to an existing device class or
* protocol. The following classes must be set at interface level:
* HID class is 3, no subclass and protocol required (but may be useful!)
* CDC class is 2, use subclass 2 and protocol 1 for ACM
*/
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */
/* Define this to the length of the HID report descriptor, if you implement
* an HID device. Otherwise don't define it or define it to 0.
* If you use this define, you must add a PROGMEM character array named
* " usbHidReportDescriptor " to your code which contains the report descriptor.
* Don't forget to keep the array and this define in sync!
*/
/* #define USB_PUBLIC static */
/* Use the define above if you #include usbdrv.c instead of linking against it.
* This technique saves a couple of bytes in flash memory.
*/
/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
* provide our own. These can be provided as (1) fixed length static data in
* flash memory, (2) fixed length static data in RAM or (3) dynamically at
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
* information about this function.
* Descriptor handling is configured through the descriptor's properties. If
* no properties are defined or if they are 0, the default descriptor is used.
* Possible properties are:
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if
* you want RAM pointers.
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
* in static memory is in RAM, not in flash memory.
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
* the driver must know the descriptor's length. The descriptor itself is
* found at the address of a well known identifier (see below).
* List of static descriptor names (must be declared PROGMEM if in flash):
* char usbDescriptorDevice[];
* char usbDescriptorConfiguration[];
* char usbDescriptorHidReport[];
* char usbDescriptorString0[];
* int usbDescriptorStringVendor[];
* int usbDescriptorStringDevice[];
* int usbDescriptorStringSerialNumber[];
* Other descriptors can't be provided statically, they must be provided
* dynamically at runtime.
*
* Descriptor properties are or-ed or added together, e.g.:
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
*
* The following descriptors are defined:
* USB_CFG_DESCR_PROPS_DEVICE
* USB_CFG_DESCR_PROPS_CONFIGURATION
* USB_CFG_DESCR_PROPS_STRINGS
* USB_CFG_DESCR_PROPS_STRING_0
* USB_CFG_DESCR_PROPS_STRING_VENDOR
* USB_CFG_DESCR_PROPS_STRING_PRODUCT
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
* USB_CFG_DESCR_PROPS_HID
* USB_CFG_DESCR_PROPS_HID_REPORT
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
*
* Note about string descriptors: String descriptors are not just strings, they
* are Unicode strings prefixed with a 2 byte header. Example:
* int serialNumberDescriptor[] = {
* USB_STRING_DESCRIPTOR_HEADER(6),
* 'S', 'e', 'r', 'i', 'a', 'l'
* };
*/
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0
/* ----------------------- Optional MCU Description ------------------------ */
/* The following configurations have working defaults in usbdrv.h. You
* usually don't need to set them explicitly. Only if you want to run
* the driver on a device which is not yet supported or with a compiler
* which is not fully supported (such as IAR C) or if you use a differnt
* interrupt than INT0, you may have to define some of these.
*/
/* #define USB_INTR_CFG MCUCR */
/* #define USB_INTR_CFG_SET ((1 & lt; & lt; ISC00) | (1 & lt; & lt; ISC01)) */
/* #define USB_INTR_CFG_CLR 0 */
/* #define USB_INTR_ENABLE GIMSK */
/* #define USB_INTR_ENABLE_BIT INT0 */
/* #define USB_INTR_PENDING GIFR */
/* #define USB_INTR_PENDING_BIT INTF0 */
/* #define USB_INTR_VECTOR SIG_INTERRUPT0 */
#endif /* __usbconfig_h_included__ */
This file documents changes in the firmware-only USB driver for atmel's AVR
microcontrollers. New entries are always appended to the end of the file.
Scroll down to the bottom to see the most recent changes.
2005-04-01:
- Implemented endpoint 1 as interrupt-in endpoint.
- Moved all configuration options to usbconfig.h which is not part of the
driver.
- Changed interface for usbVendorSetup().
- Fixed compatibility with ATMega8 device.
- Various minor optimizations.
2005-04-11:
- Changed interface to application: Use usbFunctionSetup(), usbFunctionRead()
and usbFunctionWrite() now. Added configuration options to choose which
of these functions to compile in.
- Assembler module delivers receive data non-inverted now.
- Made register and bit names compatible with more AVR devices.
2005-05-03:
- Allow address of usbRxBuf on any memory page as long as the buffer does
not cross 256 byte page boundaries.
- Better device compatibility: works with Mega88 now.
- Code optimization in debugging module.
- Documentation updates.
2006-01-02:
- Added (free) default Vendor- and Product-IDs bought from voti.nl.
- Added USBID-License.txt file which defines the rules for using the free
shared VID/PID pair.
- Added Readme.txt to the usbdrv directory which clarifies administrative
issues.
2006-01-25:
- Added " configured state " to become more standards compliant.
- Added " HALT " state for interrupt endpoint.
- Driver passes the " USB Command Verifier " test from usb.org now.
- Made " serial number " a configuration option.
- Minor optimizations, we now recommend compiler option " -Os " for best
results.
- Added a version number to usbdrv.h
2006-02-03:
- New configuration variable USB_BUFFER_SECTION for the memory section where
the USB rx buffer will go. This defaults to " .bss " if not defined. Since
this buffer MUST NOT cross 256 byte pages (not even touch a page at the
end), the user may want to pass a linker option similar to
" -Wl,--section-start=.mybuffer=0x800060 " .
- Provide structure for usbRequest_t.
- New defines for USB constants.
- Prepared for HID implementations.
- Increased data size limit for interrupt transfers to 8 bytes.
- New macro usbInterruptIsReady() to query interrupt buffer state.
2006-02-18:
- Ensure that the data token which is sent as an ack to an OUT transfer is
always zero sized. This fixes a bug where the host reports an error after
sending an out transfer to the device, although all data arrived at the
device.
- Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite().
* Release 2006-02-20
- Give a compiler warning when compiling with debugging turned on.
- Added Oleg Semyonov's changes for IAR-cc compatibility.
- Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect()
(also thanks to Oleg!).
- Rearranged tests in usbPoll() to save a couple of instructions in the most
likely case that no actions are pending.
- We need a delay between the SET ADDRESS request until the new address
becomes active. This delay was handled in usbPoll() until now. Since the
spec says that the delay must not exceed 2ms, previous versions required
aggressive polling during the enumeration phase. We have now moved the
handling of the delay into the interrupt routine.
- We must not reply with NAK to a SETUP transaction. We can only achieve this
by making sure that the rx buffer is empty when SETUP tokens are expected.
We therefore don't pass zero sized data packets from the status phase of
a transfer to usbPoll(). This change MAY cause troubles if you rely on
receiving a less than 8 bytes long packet in usbFunctionWrite() to
identify the end of a transfer. usbFunctionWrite() will NEVER be called
with a zero length.
* Release 2006-03-14
- Improved IAR C support: tiny memory model, more devices
- Added template usbconfig.h file under the name usbconfig-prototype.h
* Release 2006-03-26
- Added provision for one more interrupt-in endpoint (endpoint 3).
- Added provision for one interrupt-out endpoint (endpoint 1).
- Added flowcontrol macros for USB.
- Added provision for custom configuration descriptor.
- Allow ANY two port bits for D+ and D-.
- Merged (optional) receive endpoint number into global usbRxToken variable.
- Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the
variable name from the single port letter instead of computing the address
of related ports from the output-port address.
* Release 2006-06-26
- Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the
new features.
- Removed " #warning " directives because IAR does not understand them. Use
unused static variables instead to generate a warning.
- Do not include & lt; avr/io.h & gt; when compiling with IAR.
- Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each
USB descriptor should be handled. It is now possible to provide descriptor
data in Flash, RAM or dynamically at runtime.
- STALL is now a status in usbTxLen* instead of a message. We can now conform
to the spec and leave the stall status pending until it is cleared.
- Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the
application code to reset data toggling on interrupt pipes.
* Release 2006-07-18
- Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes
an assembler error.
- usbDeviceDisconnect() takes pull-up resistor to high impedance now.
* Release 2007-02-01
- Merged in some code size improvements from usbtiny (thanks to Dick
Streefland for these optimizations!)
- Special alignment requirement for usbRxBuf not required any more. Thanks
again to Dick Streefland for this hint!
- Reverted to " #warning " instead of unused static variables -- new versions
of IAR CC should handle this directive.
- Changed Open Source license to GNU GPL v2 in order to make linking against
other free libraries easier. We no longer require publication of the
circuit diagrams, but we STRONGLY encourage it. If you improve the driver
itself, PLEASE grant us a royalty free license to your changes for our
commercial license.
* Release 2007-03-29
- New configuration option " USB_PUBLIC " in usbconfig.h.
- Set USB version number to 1.10 instead of 1.01.
- Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and
USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences
to USB_CFG_DESCR_PROPS_STRING_PRODUCT.
- New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver
code.
- New assembler module for 16 MHz crystal.
- usbdrvasm.S contains common code only, clock-specific parts have been moved
to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively.
* Release 2007-06-25
- 16 MHz module: Do SE0 check in stuffed bits as well.
* Release 2007-07-07
- Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary
for negative values.
- Added 15 MHz module contributed by V. Bosch.
- Interrupt vector name can now be configured. This is useful if somebody
wants to use a different hardware interrupt than INT0.
* Release 2007-08-07
- Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is
not exceeded.
- More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN,
USB_COUNT_SOF
- USB_INTR_PENDING can now be a memory address, not just I/O
* Release 2007-09-19
- Split out common parts of assembler modules into separate include file
- Made endpoint numbers configurable so that given interface definitions
can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h.
- Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut()
can handle any number of endpoints.
- Define usbDeviceConnect() and usbDeviceDisconnect() even if no
USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this
case.
* Release 2007-12-01
- Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size
when USB_CFG_PULLUP_IOPORTNAME is not defined.
* Release 2007-12-13
- Renamed all include-only assembler modules from *.S to *.inc so that
people don't add them to their project sources.
- Distribute leap bits in tx loop more evenly for 16 MHz module.
- Use " macro " and " endm " instead of " .macro " and " .endm " for IAR
- Avoid compiler warnings for constant expr range by casting some values in
USB descriptors.
* Release 2008-01-21
- Fixed bug in 15 and 16 MHz module where the new address set with
SET_ADDRESS was already accepted at the next NAK or ACK we send, not at
the next data packet we send. This caused problems when the host polled
too fast. Thanks to Alexander Neumann for his help and patience debugging
this issue!
* Release 2008-02-05
- Fixed bug in 16.5 MHz module where a register was used in the interrupt
handler before it was pushed. This bug was introduced with version
2007-09-19 when common parts were moved to a separate file.
- Optimized CRC routine (thanks to Reimar Doeffinger).
* Release 2008-02-16
- Removed outdated IAR compatibility stuff (code sections).
- Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK().
- Added optional routine usbMeasureFrameLength() for calibration of the
internal RC oscillator.
* Release 2008-02-28
- USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we
start with sending USBPID_DATA0.
- Changed defaults in usbconfig-prototype.h
- Added free USB VID/PID pair for MIDI class devices
- Restructured AVR-USB as separate package, not part of PowerSwitch any more.
* Release 2008-04-18
- Restructured usbdrv.c so that it is easier to read and understand.
- Better code optimization with gcc 4.
- If a second interrupt in endpoint is enabled, also add it to config
descriptor.
- Added config option for long transfers (above 254 bytes), see
USB_CFG_LONG_TRANSFERS in usbconfig.h.
- Added 20 MHz module contributed by Jeroen Benschop.
* Release 2008-05-13
- Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length
was not incremented, pointer to length was incremented instead.
- Added code to command line tool(s) which claims an interface. This code
is disabled by default, but may be necessary on newer Linux kernels.
- Added usbconfig.h option " USB_CFG_CHECK_DATA_TOGGLING " .
- New header " usbportability.h " prepares ports to other development
environments.
- Long transfers (above 254 bytes) did not work when usbFunctionRead() was
used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!]
- In hiddata.c (example code for sending/receiving data over HID), use
USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so
that we need not claim the interface.
- in usbPoll() loop 20 times polling for RESET state instead of 10 times.
This accounts for the higher clock rates we now support.
- Added a module for 12.8 MHz RC oscillator with PLL in receiver loop.
- Added hook to SOF code so that oscillator can be tuned to USB frame clock.
- Added timeout to waitForJ loop. Helps preventing unexpected hangs.
- Added example code for oscillator tuning to libs-device (thanks to
Henrik Haftmann for the idea to this routine).
/* Name: oddebug.h
* Project: AVR library
* Author: Christian Starkjohann
* Creation Date: 2005-01-16
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.h 623 2008-06-17 18:13:38Z cs $
*/
#ifndef __oddebug_h_included__
#define __oddebug_h_included__
/*
General Description:
This module implements a function for debug logs on the serial line of the
AVR microcontroller. Debugging can be configured with the define
'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging
calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is
2, DBG1 and DBG2 logs will be printed.
A debug log consists of a label ('prefix') to indicate which debug log created
the output and a memory block to dump in hex ('data' and 'len').
*/
#ifndef F_CPU
# define F_CPU 12000000 /* 12 MHz */
#endif
/* make sure we have the UART defines: */
#include " usbportability.h "
#ifndef uchar
# define uchar unsigned char
#endif
#if DEBUG_LEVEL & gt; 0 & & !(defined TXEN || defined TXEN0) /* no UART in device */
# warning " Debugging disabled because device has no UART "
# undef DEBUG_LEVEL
#endif
#ifndef DEBUG_LEVEL
# define DEBUG_LEVEL 0
#endif
/* ------------------------------------------------------------------------- */
#if DEBUG_LEVEL & gt; 0
# define DBG1(prefix, data, len) odDebug(prefix, data, len)
#else
# define DBG1(prefix, data, len)
#endif
#if DEBUG_LEVEL & gt; 1
# define DBG2(prefix, data, len) odDebug(prefix, data, len)
#else
# define DBG2(prefix, data, len)
#endif
/* ------------------------------------------------------------------------- */
#if DEBUG_LEVEL & gt; 0
extern void odDebug(uchar prefix, uchar *data, uchar len);
/* Try to find our control registers; ATMEL likes to rename these */
#if defined UBRR
# define ODDBG_UBRR UBRR
#elif defined UBRRL
# define ODDBG_UBRR UBRRL
#elif defined UBRR0
# define ODDBG_UBRR UBRR0
#elif defined UBRR0L
# define ODDBG_UBRR UBRR0L
#endif
#if defined UCR
# define ODDBG_UCR UCR
#elif defined UCSRB
# define ODDBG_UCR UCSRB
#elif defined UCSR0B
# define ODDBG_UCR UCSR0B
#endif
#if defined TXEN
# define ODDBG_TXEN TXEN
#else
# define ODDBG_TXEN TXEN0
#endif
#if defined USR
# define ODDBG_USR USR
#elif defined UCSRA
# define ODDBG_USR UCSRA
#elif defined UCSR0A
# define ODDBG_USR UCSR0A
#endif
#if defined UDRE
# define ODDBG_UDRE UDRE
#else
# define ODDBG_UDRE UDRE0
#endif
#if defined UDR
# define ODDBG_UDR UDR
#elif defined UDR0
# define ODDBG_UDR UDR0
#endif
static inline void odDebugInit(void)
{
ODDBG_UCR |= (1 & lt; & lt; ODDBG_TXEN);
ODDBG_UBRR = F_CPU / (19200 * 16L) - 1;
}
#else
# define odDebugInit()
#endif
/* ------------------------------------------------------------------------- */
#endif /* __oddebug_h_included__ */
/* Name: oddebug.c
* Project: AVR library
* Author: Christian Starkjohann
* Creation Date: 2005-01-16
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: oddebug.c 275 2007-03-20 09:58:28Z cs $
*/
#include " oddebug.h "
#if DEBUG_LEVEL & gt; 0
#warning " Never compile production devices with debugging enabled "
static void uartPutc(char c)
{
while(!(ODDBG_USR & (1 & lt; & lt; ODDBG_UDRE))); /* wait for data register empty */
ODDBG_UDR = c;
}
static uchar hexAscii(uchar h)
{
h & = 0xf;
if(h & gt; = 10)
h += 'a' - (uchar)10 - '0';
h += '0';
return h;
}
static void printHex(uchar c)
{
uartPutc(hexAscii(c & gt; & gt; 4));
uartPutc(hexAscii(c));
}
void odDebug(uchar prefix, uchar *data, uchar len)
{
printHex(prefix);
uartPutc(':');
while(len--){
uartPutc(' ');
printHex(*data++);
}
uartPutc('\r');
uartPutc('\n');
}
#endif