[PATCH 1/2] mountmgr: Create devices and registry entries for serial ports.

Alex Henrie alexhenrie24 at gmail.com
Sun Apr 16 23:23:09 CDT 2017


Fixes https://bugs.winehq.org/show_bug.cgi?id=11811

This patch also autodetects USB serial ports, making them "just work".

The user may override the port autodetection by creating entries in
HKLM\Software\Wine\Ports.

Signed-off-by: Alex Henrie <alexhenrie24 at gmail.com>
---
 dlls/mountmgr.sys/device.c   | 170 ++++++++++++++++++++++++++++++++++++++++++-
 dlls/mountmgr.sys/mountmgr.c |   4 +
 dlls/mountmgr.sys/mountmgr.h |   1 +
 dlls/ntdll/directory.c       |  30 --------
 4 files changed, 172 insertions(+), 33 deletions(-)

diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c
index 5003d4d2ea..744bb22383 100644
--- a/dlls/mountmgr.sys/device.c
+++ b/dlls/mountmgr.sys/device.c
@@ -42,6 +42,7 @@
 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
 
 #define MAX_DOS_DRIVES 26
+#define MAX_PORTS 256
 
 static const WCHAR drive_types[][8] =
 {
@@ -57,6 +58,8 @@ static const WCHAR drive_types[][8] =
 
 static const WCHAR drives_keyW[] = {'S','o','f','t','w','a','r','e','\\',
                                     'W','i','n','e','\\','D','r','i','v','e','s',0};
+static const WCHAR ports_keyW[] = {'S','o','f','t','w','a','r','e','\\',
+                                   'W','i','n','e','\\','P','o','r','t','s',0};
 
 struct disk_device
 {
@@ -91,6 +94,7 @@ static struct list drives_list = LIST_INIT(drives_list);
 static struct list volumes_list = LIST_INIT(volumes_list);
 
 static DRIVER_OBJECT *harddisk_driver;
+static DRIVER_OBJECT *serial_driver;
 
 static CRITICAL_SECTION device_section;
 static CRITICAL_SECTION_DEBUG critsect_debug =
@@ -101,16 +105,16 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
 };
 static CRITICAL_SECTION device_section = { &critsect_debug, -1, 0, 0, 0, 0 };
 
-static char *get_dosdevices_path( char **drive )
+static char *get_dosdevices_path( char **device )
 {
     const char *config_dir = wine_get_config_dir();
-    size_t len = strlen(config_dir) + sizeof("/dosdevices/a::");
+    size_t len = strlen(config_dir) + sizeof("/dosdevices/com256");
     char *path = HeapAlloc( GetProcessHeap(), 0, len );
     if (path)
     {
         strcpy( path, config_dir );
         strcat( path, "/dosdevices/a::" );
-        *drive = path + len - 4;
+        *device = path + len - sizeof("com256");
     }
     return path;
 }
@@ -987,3 +991,163 @@ NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *pa
 
     return STATUS_SUCCESS;
 }
+
+
+/* create a serial or parallel port */
+static BOOL create_port_device( DRIVER_OBJECT *driver, int n, char *unix_path, char *dosdevices_path, char *p,
+                                HKEY wine_ports_key, HKEY windows_ports_key )
+{
+    static const WCHAR comW[] = {'C','O','M','%','u',0};
+    static const WCHAR device_serialW[] = {'\\','D','e','v','i','c','e','\\','S','e','r','i','a','l','%','u',0};
+    const WCHAR *dos_name_format, *nt_name_format, *reg_value_format;
+    WCHAR dos_name[7], reg_value[256];
+    DWORD type, size;
+    char override_path[256];
+    UNICODE_STRING nt_name;
+    DEVICE_OBJECT *dev_obj;
+    NTSTATUS status;
+
+    if (driver == serial_driver)
+    {
+        dos_name_format = comW;
+        nt_name_format = device_serialW;
+        reg_value_format = comW;
+    }
+    else
+    {
+        /* TODO: support parallel ports */
+    }
+
+    sprintfW( dos_name, dos_name_format, n );
+
+    /* check for override */
+    size = sizeof(reg_value);
+    if (RegQueryValueExW( wine_ports_key, dos_name, NULL, &type, (BYTE *)reg_value, &size ) == 0 && type == REG_SZ)
+    {
+        if (!WideCharToMultiByte( CP_UNIXCP, WC_ERR_INVALID_CHARS, reg_value, size/sizeof(WCHAR),
+                                  override_path, sizeof(override_path), NULL, NULL))
+            return FALSE;
+        unix_path = override_path;
+    }
+    if (!unix_path || !unix_path[0])
+        return FALSE;
+
+    /* create DOS device */
+    sprintf( p, "%u", n );
+    if (symlink( unix_path, dosdevices_path ) != 0)
+        return FALSE;
+
+    /* create NT device */
+    nt_name.MaximumLength = 32 * sizeof(WCHAR);
+    nt_name.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, nt_name.MaximumLength );
+    if (!nt_name.Buffer)
+        return FALSE;
+    sprintfW( nt_name.Buffer, nt_name_format, n - 1 );
+    nt_name.Length = strlenW( nt_name.Buffer ) * sizeof(WCHAR);
+    status = IoCreateDevice( driver, 0, &nt_name, 0, 0, FALSE, &dev_obj );
+    if (status != STATUS_SUCCESS)
+    {
+        FIXME( "IoCreateDevice %s got %x\n", debugstr_w(nt_name.Buffer), status );
+        RtlFreeUnicodeString( &nt_name );
+        return FALSE;
+    }
+    /* TODO: store information about the Unix device in the NT device */
+
+    /* create registry entry */
+    sprintfW( reg_value, reg_value_format, n );
+    RegSetValueExW( windows_ports_key, nt_name.Buffer, 0, REG_SZ,
+                    (BYTE *)reg_value, strlenW( reg_value ) * sizeof(WCHAR) );
+
+    return TRUE;
+}
+
+/* find and create serial or parallel ports */
+static void create_port_devices( DRIVER_OBJECT *driver )
+{
+    static const char *serial_search_paths[] = {
+#ifdef linux
+        "/dev/ttyS%u",
+        "/dev/ttyUSB%u",
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+        "/dev/cuau%u",
+#elif defined(__DragonFly__)
+        "/dev/cuaa%u",
+#else
+        "",
+#endif
+    };
+    static const WCHAR serialcomm_keyW[] = {'H','A','R','D','W','A','R','E','\\',
+                                            'D','E','V','I','C','E','M','A','P','\\',
+                                            'S','E','R','I','A','L','C','O','M','M',0};
+    const char **search_paths;
+    const WCHAR *windows_ports_key_name;
+    char *dosdevices_path, *p;
+    HKEY wine_ports_key = NULL, windows_ports_key = NULL;
+    char unix_path[256];
+    int i, j, n = 1;
+
+    if (!(dosdevices_path = get_dosdevices_path( &p )))
+        return;
+
+    if (driver == serial_driver)
+    {
+        p[0] = 'c';
+        p[1] = 'o';
+        p[2] = 'm';
+        search_paths = serial_search_paths;
+        windows_ports_key_name = serialcomm_keyW;
+    }
+    else
+    {
+        /* TODO: support parallel ports */
+    }
+    p += 3;
+
+    RegOpenKeyW( HKEY_LOCAL_MACHINE, ports_keyW, &wine_ports_key );
+    RegOpenKeyW( HKEY_LOCAL_MACHINE, windows_ports_key_name, &windows_ports_key );
+
+    /* remove old symlinks */
+    for (n = 1; n <= MAX_PORTS; n++)
+    {
+        sprintf( p, "%u", n );
+        if (unlink( dosdevices_path ) != 0 && !errno)
+            break;
+    }
+
+    /* look for ports in the usual places */
+    n = 1;
+    for (i = 0; i < sizeof(search_paths)/sizeof(search_paths[0]); i++)
+    {
+        for (j = 0; n <= MAX_PORTS; j++)
+        {
+            sprintf( unix_path, search_paths[i], j );
+            if (access( unix_path, F_OK ) != 0)
+                break;
+
+            create_port_device( driver, n, unix_path, dosdevices_path, p, wine_ports_key, windows_ports_key );
+            n++;
+        }
+    }
+
+    /* add any extra user-defined serial ports */
+    while (n <= MAX_PORTS)
+    {
+        if (!create_port_device( driver, n, NULL, dosdevices_path, p, wine_ports_key, windows_ports_key ))
+            break;
+        n++;
+    }
+
+    RegCloseKey( wine_ports_key );
+    RegCloseKey( windows_ports_key );
+}
+
+/* driver entry point for the serial port driver */
+NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
+{
+    serial_driver = driver;
+    /* TODO: fill in driver->MajorFunction */
+
+    create_port_devices( driver );
+
+    return STATUS_SUCCESS;
+}
diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c
index 10286dc2de..d40b8fca83 100644
--- a/dlls/mountmgr.sys/mountmgr.c
+++ b/dlls/mountmgr.sys/mountmgr.c
@@ -419,6 +419,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
     static const WCHAR device_mountmgrW[] = {'\\','D','e','v','i','c','e','\\','M','o','u','n','t','P','o','i','n','t','M','a','n','a','g','e','r',0};
     static const WCHAR link_mountmgrW[] = {'\\','?','?','\\','M','o','u','n','t','P','o','i','n','t','M','a','n','a','g','e','r',0};
     static const WCHAR harddiskW[] = {'\\','D','r','i','v','e','r','\\','H','a','r','d','d','i','s','k',0};
+    static const WCHAR driver_serialW[] = {'\\','D','r','i','v','e','r','\\','S','e','r','i','a','l',0};
     static const WCHAR devicemapW[] = {'H','A','R','D','W','A','R','E','\\','D','E','V','I','C','E','M','A','P',0};
     static const WCHAR parallelW[] = {'P','A','R','A','L','L','E','L',' ','P','O','R','T','S',0};
     static const WCHAR serialW[] = {'S','E','R','I','A','L','C','O','M','M',0};
@@ -463,5 +464,8 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
     initialize_dbus();
     initialize_diskarbitration();
 
+    RtlInitUnicodeString( &nameW, driver_serialW );
+    IoCreateDriver( &nameW, serial_driver_entry );
+
     return status;
 }
diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h
index 2f0db62c87..4ef36a1be7 100644
--- a/dlls/mountmgr.sys/mountmgr.h
+++ b/dlls/mountmgr.sys/mountmgr.h
@@ -57,6 +57,7 @@ extern NTSTATUS add_dos_device( int letter, const char *udi, const char *device,
 extern NTSTATUS remove_dos_device( int letter, const char *udi ) DECLSPEC_HIDDEN;
 extern NTSTATUS query_dos_device( int letter, enum device_type *type, char **device, char **mount_point ) DECLSPEC_HIDDEN;
 extern NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN;
+extern NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN;
 
 /* mount point functions */
 
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index b129b87699..8c87a57725 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -435,35 +435,6 @@ static void flush_dir_queue(void)
 
 
 /***********************************************************************
- *           get_default_com_device
- *
- * Return the default device to use for serial ports.
- */
-static char *get_default_com_device( int num )
-{
-    char *ret = NULL;
-
-    if (num < 1 || num > 256) return NULL;
-#ifdef linux
-    ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/ttyS256") );
-    if (!ret) return NULL;
-    sprintf( ret, "/dev/ttyS%d", num - 1 );
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuau256") );
-    if (!ret) return NULL;
-    sprintf( ret, "/dev/cuau%d", num - 1 );
-#elif defined(__DragonFly__)
-    ret = RtlAllocateHeap( GetProcessHeap(), 0, sizeof("/dev/cuaa256") );
-    if (!ret) return NULL;
-    sprintf( ret, "/dev/cuaa%d", num - 1 );
-#else
-    FIXME( "no known default for device com%d\n", num );
-#endif
-    return ret;
-}
-
-
-/***********************************************************************
  *           get_default_lpt_device
  *
  * Return the default device to use for parallel ports.
@@ -2505,7 +2476,6 @@ static NTSTATUS get_dos_device( const WCHAR *name, UINT name_len, ANSI_STRING *u
             dev[2] = 0;  /* remove last ':' to get the drive mount point symlink */
             new_name = get_default_drive_device( unix_name );
         }
-        else if (!strncmp( dev, "com", 3 )) new_name = get_default_com_device( atoi(dev + 3 ));
         else if (!strncmp( dev, "lpt", 3 )) new_name = get_default_lpt_device( atoi(dev + 3 ));
 
         if (!new_name) break;
-- 
2.12.0




More information about the wine-patches mailing list