Added an unknown VxD error code.
[wine] / dlls / serialui / confdlg.c
1 /*              
2  * This DLL contains the user interface for the serial driver.
3  *    a dialog box to configure the specified COMM port
4  *    an interface to the control panel (??)
5  *    functions to load and save default configuration
6  *
7  * Eventually the 32 bit comm port driver could be moved into here
8  * and interfaced to KERNEL32 using the WIN95 or WINNT comm driver interface.
9  * This way, different driver DLLS could be written to support other
10  * serial interfaces, such as X.25, etc.
11  *
12  * Basic structure copied from COMCTL32 code.
13  *
14  * Copyright 2000 Mike McCormack
15  */
16
17 #include <string.h>
18 #include <stdio.h>
19
20 #include "winbase.h"
21 #include "winreg.h"
22 #include "wingdi.h"
23 #include "winuser.h"
24 #include "debugtools.h"
25 #include "serialui.h"
26 #include "winerror.h"
27
28 DEFAULT_DEBUG_CHANNEL(comm);
29
30 HMODULE SERIALUI_hModule = 0;
31
32 /***********************************************************************
33  * SERIALUI_LibMain [Internal] Initializes the internal 'SERIALUI.DLL'.
34  *
35  * PARAMS
36  *     hinstDLL    [I] handle to the DLL's instance
37  *     fdwReason   [I]
38  *     lpvReserved [I] reserved, must be NULL
39  *
40  * RETURNS
41  *     Success: TRUE
42  *     Failure: FALSE
43  */
44
45 BOOL WINAPI
46 SERIALUI_LibMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
47 {
48     TRACE("%x,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
49
50     switch (fdwReason) {
51         case DLL_PROCESS_ATTACH:
52             SERIALUI_hModule = hinstDLL;
53             break;
54         case DLL_PROCESS_DETACH:
55             break;
56     }
57
58     return TRUE;
59 }
60
61
62 /***********************************************************************
63  * SERIALUI_EnumPropPages (SERIALUI.2)
64  *
65  * Called by the device manager to add prop sheets in Control Panel ???
66  * Pointed to in Win98 registry by 
67  * \System\CurrentControlSet\Services\Class\ports\0000\EnumPropPages =
68  *  "serialui.dll,EnumPropPages"
69  */
70 typedef LPVOID LPDEVICE_INFO;
71 typedef LPVOID LPFNADDPROPSHEETPAGE;
72 BOOL WINAPI SERIALUI_EnumPropPages(LPDEVICE_INFO pdi, LPFNADDPROPSHEETPAGE pfnAdd, LPARAM lParam )
73 {
74     FIXME("(%p %p %lx)\n",pdi,pfnAdd,lParam);
75     return FALSE;
76 }
77
78 /*
79  * These data structures are convert from values used in fields of a DCB
80  * to strings used in the CommConfigDialog.
81  */
82 typedef struct tagPARAM2STRDATA
83 {
84     DWORD        val;
85     CONST CHAR  *name;
86 } PARAM2STRDATA, *LPPARAM2STRDATA;
87
88 typedef struct tagPARAM2STR
89 {
90     DWORD         dwSize;
91     LPPARAM2STRDATA data;
92 } PARAM2STR, *LPPARAM2STR;
93 typedef const LPPARAM2STR LPCPARAM2STR;
94
95 #define SERIALUI_TABLESIZE(x) ((sizeof (x))/(sizeof (x[0])))
96
97 static PARAM2STRDATA SERIALUI_Baud2StrData[]={
98   {110, "110"}, {300, "300"}, {600, "600"}, {1200, "1200"},
99   {2400, "2400"}, {4800, "4800"}, {9600, "9600"}, {14400, "14400"},
100   {19200, "19200"}, {38400L, "38400"}, {56000L, "56000"}, {57600L, "57600"},
101   {115200L, "115200"}, {128000L, "128000"}, {256000L, "256000"}
102 };
103 static PARAM2STR SERIALUI_Baud2Str={ SERIALUI_TABLESIZE(SERIALUI_Baud2StrData),SERIALUI_Baud2StrData };
104
105 static PARAM2STRDATA SERIALUI_Parity2StrData[]={ 
106   {NOPARITY,"None"}, {ODDPARITY,"Odd"}, {EVENPARITY,"Even"}, {MARKPARITY,"Mark"},
107   {SPACEPARITY,"Space"}
108 };
109 static PARAM2STR SERIALUI_Parity2Str={ SERIALUI_TABLESIZE(SERIALUI_Parity2StrData),SERIALUI_Parity2StrData };
110
111 static PARAM2STRDATA SERIALUI_Stop2StrData[]={
112   {ONESTOPBIT,"1"}, {ONE5STOPBITS,"1.5"}, {TWOSTOPBITS,"2"}
113 };
114 static PARAM2STR SERIALUI_Stop2Str={ SERIALUI_TABLESIZE(SERIALUI_Stop2StrData),SERIALUI_Stop2StrData };
115
116 static PARAM2STRDATA SERIALUI_Data2StrData[]={
117   {5,"5"}, {6,"6"}, {7,"7"}, {8, "8"}, {16,"16"}
118 };
119 static PARAM2STR SERIALUI_Data2Str={ SERIALUI_TABLESIZE(SERIALUI_Data2StrData),SERIALUI_Data2StrData };
120
121 static PARAM2STRDATA SERIALUI_Flow2StrData[]={
122   {0,"None"}, {1,"Hardware (RTS/CTS)"}, {2,"Software (XON/XOFF)"}
123 };
124 static PARAM2STR SERIALUI_Flow2Str={ SERIALUI_TABLESIZE(SERIALUI_Flow2StrData),SERIALUI_Flow2StrData };
125
126 /*
127  * Add all the fields to a combo box and highlight the current value
128  */
129 static void SERIALUI_AddConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, DWORD dwVal)
130 {
131     int i,n;
132     HWND hControl = GetDlgItem(hDlg,id);
133
134     if(!hControl)
135         return;
136
137     for(i=0; i<table->dwSize; i++)
138     {
139         n = SendMessageA(hControl, CB_ADDSTRING, 0L, (LPARAM)table->data[i].name);
140         if(dwVal == table->data[i].val)
141         {
142             SendMessageA(hControl, CB_SETCURSEL, (WPARAM)n, (LPARAM)0);
143         }
144     }
145 }
146
147 /*
148  * Get the current sellection of the given combo box and set a DCB field to
149  * the value matching that selection.
150  */
151 static BOOL SERIALUI_GetConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, LPDWORD lpdwVal)
152 {
153     DWORD i;
154     CHAR lpEntry[20];
155     HWND hControl = GetDlgItem(hDlg,id);
156
157     if( (!hControl) || (!lpdwVal))
158     {
159         TRACE("Couldn't get window handle for item %lx\n",id);
160         return FALSE;
161     }
162
163     if(!GetWindowTextA(hControl, &lpEntry[0], sizeof lpEntry))
164     {
165         TRACE("Couldn't get window text for item %lx\n",id);
166         return FALSE;
167     }
168     /* TRACE("%ld contains %s\n",id, lpEntry); */
169
170     for(i=0; i<table->dwSize; i++)
171     {
172         if(!lstrcmpA(table->data[i].name,lpEntry))
173         {
174             *lpdwVal = table->data[i].val;
175             return TRUE;
176         }
177     }
178
179     return FALSE;
180 }
181
182 /*
183  * Both the enumerated values CBR_XXXX and integer baud rates are valid
184  * dcb.BaudRate. This code is to convert back and forth between CBR_ style
185  * and integers. The dialog box uses integer values.
186  */
187 static DWORD SERIALUI_BaudConvertTable[] =  {
188   CBR_110, 110, CBR_300, 300, CBR_600, 600, CBR_1200, 1200,
189   CBR_2400, 2400, CBR_4800, 4800, CBR_9600, 9600, CBR_14400, 14400,
190   CBR_19200, 19200, CBR_38400, 38400, CBR_56000, 56000, CBR_57600, 57600,
191   CBR_115200, 115200, CBR_128000, 128000, CBR_256000, 256000
192 };
193
194 static BOOL SERIALUI_MakeBaudDword(LPDWORD lpdwBaudRate)
195 {
196     int i;
197
198     for(i=0; i<(sizeof(SERIALUI_BaudConvertTable)/sizeof(DWORD)); i+=2)
199     {
200         if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i])
201         {
202             *lpdwBaudRate = SERIALUI_BaudConvertTable[i+1];
203             return TRUE;
204         }
205     }
206     return FALSE;
207 }
208
209 static BOOL SERIALUI_MakeBaudEnum(LPDWORD lpdwBaudRate)
210 {
211     int i;
212
213     for(i=0; i<(sizeof(SERIALUI_BaudConvertTable)/sizeof(DWORD)); i+=2)
214     {
215         if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i+1])
216         {
217             *lpdwBaudRate = SERIALUI_BaudConvertTable[i];
218             return TRUE;
219         }
220     }
221     return FALSE;
222 }
223
224 typedef struct tagSERIALUI_DialogInfo
225 {
226     LPCSTR lpszDevice;
227     LPCOMMCONFIG lpCommConfig;
228     BOOL bConvert; /* baud rate was converted to a DWORD */
229     DWORD dwFlowControl; /* old flow control */
230 } SERIALUI_DialogInfo;
231
232 static void SERIALUI_DCBToDialogInfo(HWND hDlg, SERIALUI_DialogInfo *info)
233 {
234     DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
235     LPDCB lpdcb = &info->lpCommConfig->dcb;
236
237     /* pass integer pointers to SERIALUI_ dialog config fns */
238     dwBaudRate    = lpdcb->BaudRate;
239     dwStopBits    = lpdcb->StopBits;
240     dwParity      = lpdcb->Parity;
241     dwByteSize    = lpdcb->ByteSize;
242
243     /* map flow control state, if it looks normal */
244     if((lpdcb->fRtsControl == RTS_CONTROL_HANDSHAKE) ||
245        (lpdcb->fOutxCtsFlow == TRUE)) {
246         dwFlowControl = 1;
247     } else if(lpdcb->fOutX || lpdcb->fInX) {
248         dwFlowControl = 2;
249     } else {
250         dwFlowControl = 0;
251     }
252
253     info->bConvert = SERIALUI_MakeBaudDword(&dwBaudRate);
254
255     SERIALUI_AddConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str ,dwBaudRate);
256     SERIALUI_AddConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str ,dwStopBits);
257     SERIALUI_AddConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str ,dwParity);
258     SERIALUI_AddConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str ,dwByteSize);
259     SERIALUI_AddConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, dwFlowControl );
260
261     info->dwFlowControl = dwFlowControl;
262 }
263
264 static void SERIALUI_DialogInfoToDCB(HWND hDlg, SERIALUI_DialogInfo *info)
265 {
266     DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
267     LPDCB lpdcb = &info->lpCommConfig->dcb;
268
269     SERIALUI_GetConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str, &dwBaudRate);
270     SERIALUI_GetConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str, &dwStopBits);
271     SERIALUI_GetConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str, &dwParity);
272     SERIALUI_GetConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str, &dwByteSize);
273     SERIALUI_GetConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, &dwFlowControl );
274  
275     TRACE("baud=%ld stop=%ld parity=%ld data=%ld flow=%ld\n",
276           dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl);
277     
278     lpdcb->BaudRate = dwBaudRate;
279     lpdcb->StopBits = dwStopBits;
280     lpdcb->Parity   = dwParity;
281     lpdcb->ByteSize = dwByteSize;
282
283     /* try not to change flow control if the user didn't change it */
284     if(info->dwFlowControl != dwFlowControl)
285     {
286         switch(dwFlowControl)
287         {
288         case 0:
289             lpdcb->fOutxCtsFlow = FALSE;
290             lpdcb->fOutxDsrFlow = FALSE;
291             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
292             lpdcb->fOutX        = FALSE;
293             lpdcb->fInX         = FALSE;
294             lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
295             break;
296         case 1: /* CTS/RTS */
297             lpdcb->fOutxCtsFlow = TRUE;
298             lpdcb->fOutxDsrFlow = FALSE;
299             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
300             lpdcb->fOutX        = FALSE;
301             lpdcb->fInX         = FALSE;
302             lpdcb->fRtsControl  = RTS_CONTROL_HANDSHAKE;
303             break;
304         case 2:
305             lpdcb->fOutxCtsFlow = FALSE;
306             lpdcb->fOutxDsrFlow = FALSE;
307             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
308             lpdcb->fOutX        = TRUE;
309             lpdcb->fInX         = TRUE;
310             lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
311             break;
312         }
313     }
314
315     if(info->bConvert)
316         SERIALUI_MakeBaudEnum(&lpdcb->BaudRate);
317 }
318
319 /***********************************************************************
320  * SERIALUI_ConfigDialogProc
321  *
322  * Shows a dialog for configuring a COMM port
323  */
324 BOOL WINAPI SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
325 {
326     CHAR szTitle[30];
327     SERIALUI_DialogInfo *info;
328
329     switch (uMsg)
330     {
331     case WM_INITDIALOG:
332         info = (SERIALUI_DialogInfo*) lParam;
333         if(!info)
334             return FALSE;
335         SetWindowLongA(hWnd, DWL_USER, lParam);
336         snprintf(szTitle, sizeof szTitle, "Settings for %s", info->lpszDevice);
337         SetWindowTextA(hWnd, szTitle);
338         SERIALUI_DCBToDialogInfo(hWnd, info);
339         return TRUE;
340
341     case WM_COMMAND:
342     {
343         WORD wID = LOWORD(wParam);
344
345         info = (SERIALUI_DialogInfo *) GetWindowLongA(hWnd, DWL_USER);
346         if(!info)
347             EndDialog(hWnd,0);
348         switch (wID)
349         {
350         case IDOK:
351             SERIALUI_DialogInfoToDCB(hWnd,info);
352             EndDialog(hWnd,1);
353             return TRUE;
354         case IDCANCEL:
355             EndDialog(hWnd,0);
356             return TRUE;
357 /* test code for Get/SetDefaultCommConfig begins */
358         case ID_GETDEFAULT:
359             {
360                 DWORD r,dwConfSize = sizeof (COMMCONFIG);
361                 r = GetDefaultCommConfigA(info->lpszDevice, 
362                           info->lpCommConfig, &dwConfSize);
363                 if(!r)
364                     MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
365             }
366             SERIALUI_DCBToDialogInfo(hWnd, info);
367             break;
368         case ID_SETDEFAULT:
369             {
370                 DWORD r;
371                 SERIALUI_DialogInfoToDCB(hWnd,info);
372                 r = SetDefaultCommConfigA(info->lpszDevice, 
373                           info->lpCommConfig, sizeof (COMMCONFIG));
374                 if(!r)
375                     MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
376             }
377             break;
378 /* test code for Get/SetDefaultCommConfig ends */
379         }
380     }
381     default:
382         return FALSE;
383     }
384 }
385
386 /***********************************************************************
387  * SERIALUI_CommConfigDialog (SERIALUI.3)
388  *
389  * Used by Win9x KERNEL to show a dialog for configuring a COMM port.
390  */
391 BOOL WINAPI SERIALUI_CommConfigDialog(
392         LPCSTR lpszName, 
393         HWND hWndParent, 
394         LPCOMMCONFIG lpCommConfig
395 ) {
396     SERIALUI_DialogInfo info;
397
398     info.lpCommConfig  = lpCommConfig;
399     info.lpszDevice    = lpszName;
400     info.bConvert      = FALSE;
401     info.dwFlowControl = 0;
402
403     if(!lpCommConfig)
404         return FALSE;
405
406     return DialogBoxParamA(SERIALUI_hModule,
407                            MAKEINTRESOURCEA(IDD_SERIALUICONFIG),
408                            hWndParent, 
409                            (DLGPROC) SERIALUI_ConfigDialogProc,
410                            (LPARAM)&info);
411 }
412
413 static LPCSTR lpszCommKey = "System\\CurrentControlSet\\Services\\Class\\Ports";
414 static LPCSTR lpszDCB     = "DCB";
415
416 /***********************************************************************
417  * SERIALUI_SetDefaultCommConfig (SERIALUI.4)
418  *
419  * Used by Win98 KERNEL to set the default config for a COMM port
420  * FIXME: uses the wrong registry key... should use a digit, not
421  *        the comm port name.
422  */
423 BOOL WINAPI SERIALUI_SetDefaultCommConfig(
424         LPCSTR lpszDevice, 
425         LPCOMMCONFIG lpCommConfig,
426         DWORD dwSize
427 ) {
428     HKEY hKeyReg=0, hKeyPort=0;
429     CHAR szKeyName[100];
430     DWORD r,dwDCBSize;
431
432     TRACE("%p %p %lx\n",lpszDevice,lpCommConfig,dwSize);
433
434     if(!lpCommConfig)
435         return FALSE;
436
437     if(dwSize < sizeof (COMMCONFIG))
438         return FALSE;
439
440     r = RegConnectRegistryA(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
441     if(r != ERROR_SUCCESS)
442         return FALSE;
443
444     snprintf(szKeyName, sizeof szKeyName, "%s\\%s", lpszCommKey ,lpszDevice);
445     r = RegCreateKeyA(hKeyReg, szKeyName, &hKeyPort);
446     if(r == ERROR_SUCCESS)
447     {
448         dwDCBSize = sizeof (DCB);
449         r = RegSetValueExA( hKeyPort, lpszDCB, 0, REG_BINARY,
450                             (LPSTR)&lpCommConfig->dcb,dwDCBSize);
451         TRACE("write key r=%ld\n",r);
452         RegCloseKey(hKeyPort);
453     }
454
455     RegCloseKey(hKeyReg);
456
457     return (r==ERROR_SUCCESS);
458 }
459
460 /***********************************************************************
461  * SERIALUI_GetDefaultCommConfig (SERIALUI.5)
462  *
463  * Used by Win9x KERNEL to get the default config for a COMM port
464  * FIXME: uses the wrong registry key... should use a digit, not
465  *        the comm port name.
466  */
467 BOOL WINAPI SERIALUI_GetDefaultCommConfig(
468         LPCSTR lpszDevice, 
469         LPCOMMCONFIG lpCommConfig,
470         LPDWORD lpdwSize
471 ) {
472     HKEY hKeyReg, hKeyPort;
473     CHAR szKeyName[100];
474     DWORD r,dwSize,dwType;
475
476     TRACE("%p %p %p\n",lpszDevice,lpCommConfig,lpdwSize);
477
478     if(!lpCommConfig)
479         return FALSE;
480
481     if(!lpdwSize)
482         return FALSE;
483
484     if(*lpdwSize < sizeof (COMMCONFIG))
485         return FALSE;
486
487     *lpdwSize = sizeof (COMMCONFIG);
488     memset(lpCommConfig, 0 , sizeof (COMMCONFIG));
489     lpCommConfig->dwSize = sizeof (COMMCONFIG);
490     lpCommConfig->wVersion = 1;
491
492     r = RegConnectRegistryA(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
493     if(r != ERROR_SUCCESS)
494         return FALSE;
495
496     snprintf(szKeyName, sizeof szKeyName, "%s\\%s", lpszCommKey ,lpszDevice);
497     r = RegOpenKeyA(hKeyReg, szKeyName, &hKeyPort);
498     if(r == ERROR_SUCCESS)
499     {
500         dwSize = sizeof (DCB);
501         dwType = 0;
502         r = RegQueryValueExA( hKeyPort, lpszDCB, NULL,
503                              &dwType, (LPSTR)&lpCommConfig->dcb,&dwSize);
504         if ((r==ERROR_SUCCESS) && (dwType != REG_BINARY))
505             r = 1;
506         if ((r==ERROR_SUCCESS) && (dwSize != sizeof(DCB)))
507             r = 1;
508            
509         RegCloseKey(hKeyPort);
510     }
511     else
512     {
513         /* FIXME: default to a hardcoded commconfig */
514         
515         lpCommConfig->dcb.DCBlength = sizeof(DCB);
516         lpCommConfig->dcb.BaudRate = 9600;
517         lpCommConfig->dcb.fBinary = TRUE;
518         lpCommConfig->dcb.fParity = FALSE;
519         lpCommConfig->dcb.ByteSize = 8;
520         lpCommConfig->dcb.Parity = NOPARITY;
521         lpCommConfig->dcb.StopBits = ONESTOPBIT;
522         return TRUE;
523     }
524
525     RegCloseKey(hKeyReg);
526
527     return (r==ERROR_SUCCESS);
528 }