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
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.
12 * Basic structure copied from COMCTL32 code.
14 * Copyright 2000, 2004 Mike McCormack
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/port.h"
33 #include "wine/unicode.h"
44 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(comm
);
50 static HMODULE SERIALUI_hModule
;
52 static const WCHAR comW
[] = {'c','o','m',0 };
54 /***********************************************************************
55 * DllMain [Internal] Initializes the internal 'SERIALUI.DLL'.
58 * hinstDLL [I] handle to the DLL's instance
60 * lpvReserved [I] reserved, must be NULL
67 BOOL WINAPI
DllMain (HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
69 TRACE("%p,%x,%p\n", hinstDLL
, fdwReason
, lpvReserved
);
72 case DLL_PROCESS_ATTACH
:
73 DisableThreadLibraryCalls(hinstDLL
);
74 SERIALUI_hModule
= hinstDLL
;
76 case DLL_PROCESS_DETACH
:
84 /***********************************************************************
85 * EnumPropPages (SERIALUI.2)
87 * Called by the device manager to add prop sheets in Control Panel ???
88 * Pointed to in Win98 registry by
89 * \System\CurrentControlSet\Services\Class\ports\0000\EnumPropPages =
90 * "serialui.dll,EnumPropPages"
92 typedef LPVOID LPDEVICE_INFO
;
93 typedef LPVOID LPFNADDPROPSHEETPAGE
;
94 BOOL WINAPI
EnumPropPages(LPDEVICE_INFO pdi
, LPFNADDPROPSHEETPAGE pfnAdd
, LPARAM lParam
)
96 FIXME("(%p %p %lx)\n",pdi
,pfnAdd
,lParam
);
101 * These data structures are convert from values used in fields of a DCB
102 * to strings used in the CommConfigDialog.
104 typedef struct tagPARAM2STRDATA
108 } PARAM2STRDATA
, *LPPARAM2STRDATA
;
110 typedef struct tagPARAM2STR
113 LPPARAM2STRDATA data
;
114 } PARAM2STR
, *LPPARAM2STR
;
115 typedef const PARAM2STR
*LPCPARAM2STR
;
117 #define SERIALUI_TABLESIZE(x) ((sizeof (x))/(sizeof (x[0])))
119 static PARAM2STRDATA SERIALUI_Baud2StrData
[]={
120 {110, "110"}, {300, "300"}, {600, "600"}, {1200, "1200"},
121 {2400, "2400"}, {4800, "4800"}, {9600, "9600"}, {14400, "14400"},
122 {19200, "19200"}, {38400L, "38400"}, {56000L, "56000"}, {57600L, "57600"},
123 {115200L, "115200"}, {128000L, "128000"}, {256000L, "256000"}
125 static PARAM2STR SERIALUI_Baud2Str
={ SERIALUI_TABLESIZE(SERIALUI_Baud2StrData
),SERIALUI_Baud2StrData
};
127 static PARAM2STRDATA SERIALUI_Parity2StrData
[]={
128 {NOPARITY
,"None"}, {ODDPARITY
,"Odd"}, {EVENPARITY
,"Even"}, {MARKPARITY
,"Mark"},
129 {SPACEPARITY
,"Space"}
131 static PARAM2STR SERIALUI_Parity2Str
={ SERIALUI_TABLESIZE(SERIALUI_Parity2StrData
),SERIALUI_Parity2StrData
};
133 static PARAM2STRDATA SERIALUI_Stop2StrData
[]={
134 {ONESTOPBIT
,"1"}, {ONE5STOPBITS
,"1.5"}, {TWOSTOPBITS
,"2"}
136 static PARAM2STR SERIALUI_Stop2Str
={ SERIALUI_TABLESIZE(SERIALUI_Stop2StrData
),SERIALUI_Stop2StrData
};
138 static PARAM2STRDATA SERIALUI_Data2StrData
[]={
139 {5,"5"}, {6,"6"}, {7,"7"}, {8, "8"}, {16,"16"}
141 static PARAM2STR SERIALUI_Data2Str
={ SERIALUI_TABLESIZE(SERIALUI_Data2StrData
),SERIALUI_Data2StrData
};
143 static PARAM2STRDATA SERIALUI_Flow2StrData
[]={
144 {0,"None"}, {1,"Hardware (RTS/CTS)"}, {2,"Software (XON/XOFF)"}
146 static PARAM2STR SERIALUI_Flow2Str
={ SERIALUI_TABLESIZE(SERIALUI_Flow2StrData
),SERIALUI_Flow2StrData
};
149 * Add all the fields to a combo box and highlight the current value
151 static void SERIALUI_AddConfItems(HWND hDlg
, DWORD id
, LPCPARAM2STR table
, DWORD dwVal
)
155 HWND hControl
= GetDlgItem(hDlg
,id
);
160 for(i
=0; i
<table
->dwSize
; i
++)
162 n
= SendMessageA(hControl
, CB_ADDSTRING
, 0L, (LPARAM
)table
->data
[i
].name
);
163 if(dwVal
== table
->data
[i
].val
)
165 SendMessageA(hControl
, CB_SETCURSEL
, n
, 0);
171 * Get the current selection of the given combo box and set a DCB field to
172 * the value matching that selection.
174 static BOOL
SERIALUI_GetConfItems(HWND hDlg
, DWORD id
, LPCPARAM2STR table
, LPDWORD lpdwVal
)
178 HWND hControl
= GetDlgItem(hDlg
,id
);
180 if( (!hControl
) || (!lpdwVal
))
182 TRACE("Couldn't get window handle for item %x\n",id
);
186 if(!GetWindowTextA(hControl
, &lpEntry
[0], sizeof(lpEntry
)))
188 TRACE("Couldn't get window text for item %x\n",id
);
191 /* TRACE("%ld contains %s\n",id, lpEntry); */
193 for(i
=0; i
<table
->dwSize
; i
++)
195 if(!lstrcmpA(table
->data
[i
].name
,lpEntry
))
197 *lpdwVal
= table
->data
[i
].val
;
206 * Both the enumerated values CBR_XXXX and integer baud rates are valid
207 * dcb.BaudRate. This code is to convert back and forth between CBR_ style
208 * and integers. The dialog box uses integer values.
210 static const DWORD SERIALUI_BaudConvertTable
[] = {
211 CBR_110
, 110, CBR_300
, 300, CBR_600
, 600, CBR_1200
, 1200,
212 CBR_2400
, 2400, CBR_4800
, 4800, CBR_9600
, 9600, CBR_14400
, 14400,
213 CBR_19200
, 19200, CBR_38400
, 38400, CBR_56000
, 56000, CBR_57600
, 57600,
214 CBR_115200
, 115200, CBR_128000
, 128000, CBR_256000
, 256000
217 static BOOL
SERIALUI_MakeBaudDword(LPDWORD lpdwBaudRate
)
221 for(i
=0; i
<(sizeof(SERIALUI_BaudConvertTable
)/sizeof(DWORD
)); i
+=2)
223 if(*lpdwBaudRate
== SERIALUI_BaudConvertTable
[i
])
225 *lpdwBaudRate
= SERIALUI_BaudConvertTable
[i
+1];
232 static BOOL
SERIALUI_MakeBaudEnum(LPDWORD lpdwBaudRate
)
236 for(i
=0; i
<(sizeof(SERIALUI_BaudConvertTable
)/sizeof(DWORD
)); i
+=2)
238 if(*lpdwBaudRate
== SERIALUI_BaudConvertTable
[i
+1])
240 *lpdwBaudRate
= SERIALUI_BaudConvertTable
[i
];
247 typedef struct tagSERIALUI_DialogInfo
250 LPCOMMCONFIG lpCommConfig
;
251 BOOL bConvert
; /* baud rate was converted to a DWORD */
252 DWORD dwFlowControl
; /* old flow control */
253 } SERIALUI_DialogInfo
;
255 static void SERIALUI_DCBToDialogInfo(HWND hDlg
, SERIALUI_DialogInfo
*info
)
257 DWORD dwBaudRate
, dwStopBits
, dwParity
, dwByteSize
, dwFlowControl
;
258 LPDCB lpdcb
= &info
->lpCommConfig
->dcb
;
260 /* pass integer pointers to SERIALUI_ dialog config fns */
261 dwBaudRate
= lpdcb
->BaudRate
;
262 dwStopBits
= lpdcb
->StopBits
;
263 dwParity
= lpdcb
->Parity
;
264 dwByteSize
= lpdcb
->ByteSize
;
266 /* map flow control state, if it looks normal */
267 if((lpdcb
->fRtsControl
== RTS_CONTROL_HANDSHAKE
) ||
268 (lpdcb
->fOutxCtsFlow
)) {
270 } else if(lpdcb
->fOutX
|| lpdcb
->fInX
) {
276 info
->bConvert
= SERIALUI_MakeBaudDword(&dwBaudRate
);
278 SERIALUI_AddConfItems( hDlg
, IDC_BAUD
, &SERIALUI_Baud2Str
,dwBaudRate
);
279 SERIALUI_AddConfItems( hDlg
, IDC_STOP
, &SERIALUI_Stop2Str
,dwStopBits
);
280 SERIALUI_AddConfItems( hDlg
, IDC_PARITY
, &SERIALUI_Parity2Str
,dwParity
);
281 SERIALUI_AddConfItems( hDlg
, IDC_DATA
, &SERIALUI_Data2Str
,dwByteSize
);
282 SERIALUI_AddConfItems( hDlg
, IDC_FLOW
, &SERIALUI_Flow2Str
, dwFlowControl
);
284 info
->dwFlowControl
= dwFlowControl
;
287 static void SERIALUI_DialogInfoToDCB(HWND hDlg
, SERIALUI_DialogInfo
*info
)
289 DWORD dwBaudRate
, dwStopBits
, dwParity
, dwByteSize
, dwFlowControl
;
290 LPDCB lpdcb
= &info
->lpCommConfig
->dcb
;
292 SERIALUI_GetConfItems( hDlg
, IDC_BAUD
, &SERIALUI_Baud2Str
, &dwBaudRate
);
293 SERIALUI_GetConfItems( hDlg
, IDC_STOP
, &SERIALUI_Stop2Str
, &dwStopBits
);
294 SERIALUI_GetConfItems( hDlg
, IDC_PARITY
, &SERIALUI_Parity2Str
, &dwParity
);
295 SERIALUI_GetConfItems( hDlg
, IDC_DATA
, &SERIALUI_Data2Str
, &dwByteSize
);
296 SERIALUI_GetConfItems( hDlg
, IDC_FLOW
, &SERIALUI_Flow2Str
, &dwFlowControl
);
298 TRACE("baud=%d stop=%d parity=%d data=%d flow=%d\n",
299 dwBaudRate
, dwStopBits
, dwParity
, dwByteSize
, dwFlowControl
);
301 lpdcb
->BaudRate
= dwBaudRate
;
302 lpdcb
->StopBits
= dwStopBits
;
303 lpdcb
->Parity
= dwParity
;
304 lpdcb
->ByteSize
= dwByteSize
;
306 /* try not to change flow control if the user didn't change it */
307 if(info
->dwFlowControl
!= dwFlowControl
)
309 switch(dwFlowControl
)
312 lpdcb
->fOutxCtsFlow
= FALSE
;
313 lpdcb
->fOutxDsrFlow
= FALSE
;
314 lpdcb
->fDtrControl
= DTR_CONTROL_DISABLE
;
315 lpdcb
->fOutX
= FALSE
;
317 lpdcb
->fRtsControl
= RTS_CONTROL_DISABLE
;
319 case 1: /* CTS/RTS */
320 lpdcb
->fOutxCtsFlow
= TRUE
;
321 lpdcb
->fOutxDsrFlow
= FALSE
;
322 lpdcb
->fDtrControl
= DTR_CONTROL_DISABLE
;
323 lpdcb
->fOutX
= FALSE
;
325 lpdcb
->fRtsControl
= RTS_CONTROL_HANDSHAKE
;
328 lpdcb
->fOutxCtsFlow
= FALSE
;
329 lpdcb
->fOutxDsrFlow
= FALSE
;
330 lpdcb
->fDtrControl
= DTR_CONTROL_DISABLE
;
333 lpdcb
->fRtsControl
= RTS_CONTROL_DISABLE
;
339 SERIALUI_MakeBaudEnum(&lpdcb
->BaudRate
);
342 /***********************************************************************
343 * SERIALUI_ConfigDialogProc
345 * Shows a dialog for configuring a COMM port
347 static INT_PTR CALLBACK
SERIALUI_ConfigDialogProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
349 WCHAR szTitle
[128], format
[40];
350 SERIALUI_DialogInfo
*info
;
355 info
= (SERIALUI_DialogInfo
*) lParam
;
358 SetWindowLongPtrW(hWnd
, DWLP_USER
, lParam
);
359 GetWindowTextW(hWnd
, format
, sizeof(format
)/sizeof(WCHAR
));
360 snprintfW( szTitle
, sizeof(szTitle
)/sizeof(WCHAR
), format
, info
->lpszDevice
);
361 SetWindowTextW(hWnd
, szTitle
);
362 SERIALUI_DCBToDialogInfo(hWnd
, info
);
367 WORD wID
= LOWORD(wParam
);
369 info
= (SERIALUI_DialogInfo
*) GetWindowLongPtrW(hWnd
, DWLP_USER
);
375 SERIALUI_DialogInfoToDCB(hWnd
,info
);
376 EndDialog(hWnd
, ERROR_SUCCESS
);
379 EndDialog(hWnd
, ERROR_CANCELLED
);
381 /* test code for Get/SetDefaultCommConfig begins */
384 DWORD r
,dwConfSize
= sizeof (COMMCONFIG
);
385 r
= GetDefaultCommConfigW(info
->lpszDevice
,
386 info
->lpCommConfig
, &dwConfSize
);
388 MessageBoxA(hWnd
,"Failed","GetDefaultCommConfig",MB_OK
);
390 SERIALUI_DCBToDialogInfo(hWnd
, info
);
395 SERIALUI_DialogInfoToDCB(hWnd
,info
);
396 r
= SetDefaultCommConfigW(info
->lpszDevice
,
397 info
->lpCommConfig
, sizeof (COMMCONFIG
));
399 MessageBoxA(hWnd
,"Failed","GetDefaultCommConfig",MB_OK
);
402 /* test code for Get/SetDefaultCommConfig ends */
410 static LPWSTR
SERIALUI_strdup( LPCSTR str
)
417 len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
418 strW
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
419 MultiByteToWideChar( CP_ACP
, 0, str
, -1, strW
, len
);
423 static VOID
SERIALUI_strfree( LPWSTR strW
)
425 HeapFree( GetProcessHeap(), 0, strW
);
428 /***********************************************************************
429 * drvCommConfigDialogW (SERIALUI.@)
431 * Show a dialog for configuring a Serial Port.
434 DWORD WINAPI
drvCommConfigDialogW(LPCWSTR lpszName
, HWND hWndParent
, LPCOMMCONFIG lpCommConfig
)
436 SERIALUI_DialogInfo info
;
439 info
.lpCommConfig
= lpCommConfig
;
440 info
.lpszDevice
= lpszName
;
441 info
.bConvert
= FALSE
;
442 info
.dwFlowControl
= 0;
444 if ((!lpCommConfig
) || (!lpszName
))
445 return ERROR_INVALID_PARAMETER
;
447 if (lpCommConfig
->dwSize
< sizeof(COMMCONFIG
))
448 return ERROR_INSUFFICIENT_BUFFER
;
453 res
= DialogBoxParamW( SERIALUI_hModule
,
454 MAKEINTRESOURCEW(IDD_SERIALUICONFIG
),
456 SERIALUI_ConfigDialogProc
,
459 return (res
== -1) ? GetLastError() : res
;
462 /***********************************************************************
463 * drvCommConfigDialogA (SERIALUI.@)
465 DWORD WINAPI
drvCommConfigDialogA(LPCSTR lpszName
, HWND hWndParent
, LPCOMMCONFIG lpCommConfig
)
467 LPWSTR strW
= SERIALUI_strdup( lpszName
);
468 DWORD r
= drvCommConfigDialogW( strW
, hWndParent
, lpCommConfig
);
469 SERIALUI_strfree( strW
);
473 static const WCHAR lpszCommKey
[] = {
474 'S','y','s','t','e','m','\\',
475 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
476 'S','e','r','v','i','c','e','s','\\',
477 'C','l','a','s','s','\\','P','o','r','t','s',0
479 static const WCHAR lpszDCB
[] = {'D','C','B',0};
481 /***********************************************************************
482 * drvSetDefaultCommConfigW (SERIALUI.@)
484 * Used by Win98 KERNEL to set the default config for a COMM port
485 * FIXME: uses the wrong registry key... should use a digit, not
486 * the comm port name.
488 BOOL WINAPI
drvSetDefaultCommConfigW(
489 LPCWSTR lpszDevice
, LPCOMMCONFIG lpCommConfig
, DWORD dwSize
)
491 HKEY hKeyReg
=0, hKeyPort
=0;
492 WCHAR szKeyName
[100];
494 static const WCHAR fmt
[] = {'%','s','\\','%','s',0 };
496 TRACE("%p %p %x\n",lpszDevice
,lpCommConfig
,dwSize
);
501 if(dwSize
< sizeof (COMMCONFIG
))
504 r
= RegConnectRegistryW(NULL
, HKEY_LOCAL_MACHINE
, &hKeyReg
);
505 if(r
!= ERROR_SUCCESS
)
508 snprintfW(szKeyName
, sizeof(szKeyName
)/sizeof(WCHAR
), fmt
, lpszCommKey
,lpszDevice
);
509 r
= RegCreateKeyW(hKeyReg
, szKeyName
, &hKeyPort
);
510 if(r
== ERROR_SUCCESS
)
512 dwDCBSize
= sizeof (DCB
);
513 r
= RegSetValueExW( hKeyPort
, lpszDCB
, 0, REG_BINARY
,
514 (LPBYTE
)&lpCommConfig
->dcb
,dwDCBSize
);
515 TRACE("write key r=%d\n",r
);
516 RegCloseKey(hKeyPort
);
519 RegCloseKey(hKeyReg
);
521 return (r
==ERROR_SUCCESS
);
524 /***********************************************************************
525 * drvSetDefaultCommConfigA (SERIALUI.@)
527 BOOL WINAPI
drvSetDefaultCommConfigA(
528 LPCSTR lpszDevice
, LPCOMMCONFIG lpCommConfig
, DWORD dwSize
)
530 LPWSTR strW
= SERIALUI_strdup( lpszDevice
);
531 BOOL r
= drvSetDefaultCommConfigW( strW
, lpCommConfig
, dwSize
);
532 SERIALUI_strfree( strW
);
536 /***********************************************************************
537 * drvGetDefaultCommConfigW (SERIALUI.@)
539 * Used by Win9x KERNEL to get the default config for a COMM port
540 * FIXME: uses the wrong registry key... should use a digit, not
541 * the comm port name.
543 DWORD WINAPI
drvGetDefaultCommConfigW(
544 LPCWSTR lpszDevice
, LPCOMMCONFIG lpCommConfig
, LPDWORD lpdwSize
)
546 HKEY hKeyReg
, hKeyPort
;
547 WCHAR szKeyName
[100];
548 DWORD r
,dwSize
,dwType
;
549 static const WCHAR fmt
[] = {'%','s','\\','%','s',0 };
551 TRACE("(%s, %p, %p) *lpdwSize: %u\n", debugstr_w(lpszDevice
), lpCommConfig
, lpdwSize
, lpdwSize
? *lpdwSize
: 0);
553 if ((!lpszDevice
) || (!lpCommConfig
) || (!lpdwSize
)) {
554 return ERROR_INVALID_PARAMETER
;
557 if (*lpdwSize
< sizeof (COMMCONFIG
)) {
558 *lpdwSize
= sizeof (COMMCONFIG
);
559 return ERROR_INSUFFICIENT_BUFFER
;
562 /* only "com1" - "com9" is allowed */
563 r
= sizeof(comW
) / sizeof(WCHAR
); /* len of "com\0" */
564 lstrcpynW(szKeyName
, lpszDevice
, r
); /* simulate a lstrcmpnW */
567 if( lstrcmpiW(szKeyName
, comW
) ||
568 (lpszDevice
[r
] < '1') || (lpszDevice
[r
] > '9') || lpszDevice
[r
+1]) {
572 *lpdwSize
= sizeof (COMMCONFIG
);
573 memset(lpCommConfig
, 0 , sizeof (COMMCONFIG
));
574 lpCommConfig
->dwSize
= sizeof (COMMCONFIG
);
575 lpCommConfig
->wVersion
= 1;
576 lpCommConfig
->dwProviderSubType
= PST_RS232
;
578 r
= RegConnectRegistryW(NULL
, HKEY_LOCAL_MACHINE
, &hKeyReg
);
579 if(r
!= ERROR_SUCCESS
) return r
;
581 snprintfW(szKeyName
, sizeof(szKeyName
)/sizeof(WCHAR
), fmt
, lpszCommKey
,lpszDevice
);
582 r
= RegOpenKeyW(hKeyReg
, szKeyName
, &hKeyPort
);
583 if(r
== ERROR_SUCCESS
)
585 dwSize
= sizeof (DCB
);
587 r
= RegQueryValueExW( hKeyPort
, lpszDCB
, NULL
,
588 &dwType
, (LPBYTE
)&lpCommConfig
->dcb
, &dwSize
);
590 RegCloseKey(hKeyPort
);
591 if ((r
!=ERROR_SUCCESS
) || (dwType
!= REG_BINARY
) || (dwSize
!= sizeof(DCB
))) {
592 RegCloseKey(hKeyReg
);
593 return ERROR_INVALID_PARAMETER
;
599 /* FIXME: default to a hardcoded commconfig */
600 lpCommConfig
->dcb
.DCBlength
= sizeof(DCB
);
601 lpCommConfig
->dcb
.BaudRate
= 9600;
602 lpCommConfig
->dcb
.fBinary
= TRUE
;
603 lpCommConfig
->dcb
.fParity
= FALSE
;
604 lpCommConfig
->dcb
.ByteSize
= 8;
605 lpCommConfig
->dcb
.Parity
= NOPARITY
;
606 lpCommConfig
->dcb
.StopBits
= ONESTOPBIT
;
607 return ERROR_SUCCESS
;
610 RegCloseKey(hKeyReg
);
615 /***********************************************************************
616 * drvGetDefaultCommConfigA (SERIALUI.@)
618 DWORD WINAPI
drvGetDefaultCommConfigA(
619 LPCSTR lpszDevice
, LPCOMMCONFIG lpCommConfig
, LPDWORD lpdwSize
)
621 LPWSTR strW
= SERIALUI_strdup( lpszDevice
);
622 DWORD r
= drvGetDefaultCommConfigW( strW
, lpCommConfig
, lpdwSize
);
623 SERIALUI_strfree( strW
);