2 * Dynamic devices support
4 * Copyright 2006 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #ifdef HAVE_SYS_IOCTL_H
31 # include <sys/ioctl.h>
34 #define NONAMELESSUNION
41 #include "wine/library.h"
42 #include "wine/list.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
48 #define MAX_DOS_DRIVES 26
51 static const WCHAR drive_types
[][8] =
53 { 0 }, /* DEVICE_UNKNOWN */
54 { 0 }, /* DEVICE_HARDDISK */
55 {'h','d',0}, /* DEVICE_HARDDISK_VOL */
56 {'f','l','o','p','p','y',0}, /* DEVICE_FLOPPY */
57 {'c','d','r','o','m',0}, /* DEVICE_CDROM */
58 {'c','d','r','o','m',0}, /* DEVICE_DVD */
59 {'n','e','t','w','o','r','k',0}, /* DEVICE_NETWORK */
60 {'r','a','m','d','i','s','k',0} /* DEVICE_RAMDISK */
63 static const WCHAR drives_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
64 'W','i','n','e','\\','D','r','i','v','e','s',0};
65 static const WCHAR ports_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
66 'W','i','n','e','\\','P','o','r','t','s',0};
67 static const WCHAR scsi_keyW
[] = {'H','A','R','D','W','A','R','E','\\','D','E','V','I','C','E','M','A','P','\\','S','c','s','i',0};
68 static const WCHAR scsi_port_keyW
[] = {'S','c','s','i',' ','P','o','r','t',' ','%','d',0};
69 static const WCHAR scsi_bus_keyW
[] = {'S','c','s','i',' ','B','u','s',' ','%','d',0};
70 static const WCHAR target_id_keyW
[] = {'T','a','r','g','e','t',' ','I','d',' ','%','d',0};
71 static const WCHAR lun_keyW
[] = {'L','o','g','i','c','a','l',' ','U','n','i','t',' ','I','d',' ','%','d',0};
72 static const WCHAR devnameW
[] = {'D','e','v','i','c','e','N','a','m','e',0};
76 enum device_type type
; /* drive type */
77 DEVICE_OBJECT
*dev_obj
; /* disk device allocated for this volume */
78 UNICODE_STRING name
; /* device name */
79 UNICODE_STRING symlink
; /* device symlink if any */
80 STORAGE_DEVICE_NUMBER devnum
; /* device number info */
81 char *unix_device
; /* unix device path */
82 char *unix_mount
; /* unix mount point path */
87 struct list entry
; /* entry in volumes list */
88 struct disk_device
*device
; /* disk device */
89 char *udi
; /* unique identifier for dynamic volumes */
90 unsigned int ref
; /* ref count */
91 GUID guid
; /* volume uuid */
92 struct mount_point
*mount
; /* Volume{xxx} mount point */
97 struct list entry
; /* entry in drives list */
98 struct volume
*volume
; /* volume for this drive */
99 int drive
; /* drive letter (0 = A: etc.) */
100 struct mount_point
*mount
; /* DosDevices mount point */
103 static struct list drives_list
= LIST_INIT(drives_list
);
104 static struct list volumes_list
= LIST_INIT(volumes_list
);
106 static DRIVER_OBJECT
*harddisk_driver
;
107 static DRIVER_OBJECT
*serial_driver
;
108 static DRIVER_OBJECT
*parallel_driver
;
110 static CRITICAL_SECTION device_section
;
111 static CRITICAL_SECTION_DEBUG critsect_debug
=
113 0, 0, &device_section
,
114 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
115 0, 0, { (DWORD_PTR
)(__FILE__
": device_section") }
117 static CRITICAL_SECTION device_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
119 static char *get_dosdevices_path( char **device
)
121 const char *config_dir
= wine_get_config_dir();
122 size_t len
= strlen(config_dir
) + sizeof("/dosdevices/com256");
123 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
126 strcpy( path
, config_dir
);
127 strcat( path
, "/dosdevices/a::" );
128 *device
= path
+ len
- sizeof("com256");
133 static char *strdupA( const char *str
)
137 if (!str
) return NULL
;
138 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0, strlen(str
) + 1 ))) strcpy( ret
, str
);
142 static const GUID
*get_default_uuid( int letter
)
146 guid
.Data4
[7] = 'A' + letter
;
150 /* read a Unix symlink; returned buffer must be freed by caller */
151 static char *read_symlink( const char *path
)
158 if (!(buffer
= RtlAllocateHeap( GetProcessHeap(), 0, size
)))
160 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
163 ret
= readlink( path
, buffer
, size
);
166 RtlFreeHeap( GetProcessHeap(), 0, buffer
);
174 RtlFreeHeap( GetProcessHeap(), 0, buffer
);
179 /* update a symlink if it changed; return TRUE if updated */
180 static void update_symlink( const char *path
, const char *dest
, const char *orig_dest
)
184 if (!orig_dest
|| strcmp( orig_dest
, dest
))
187 symlink( dest
, path
);
193 /* send notification about a change to a given drive */
194 static void send_notify( int drive
, int code
)
196 DEV_BROADCAST_VOLUME info
;
198 info
.dbcv_size
= sizeof(info
);
199 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
200 info
.dbcv_reserved
= 0;
201 info
.dbcv_unitmask
= 1 << drive
;
202 info
.dbcv_flags
= DBTF_MEDIA
;
203 BroadcastSystemMessageW( BSF_FORCEIFHUNG
|BSF_QUERY
, NULL
,
204 WM_DEVICECHANGE
, code
, (LPARAM
)&info
);
207 /* create the disk device for a given volume */
208 static NTSTATUS
create_disk_device( enum device_type type
, struct disk_device
**device_ret
)
210 static const WCHAR harddiskvolW
[] = {'\\','D','e','v','i','c','e',
211 '\\','H','a','r','d','d','i','s','k','V','o','l','u','m','e','%','u',0};
212 static const WCHAR harddiskW
[] = {'\\','D','e','v','i','c','e','\\','H','a','r','d','d','i','s','k','%','u',0};
213 static const WCHAR cdromW
[] = {'\\','D','e','v','i','c','e','\\','C','d','R','o','m','%','u',0};
214 static const WCHAR floppyW
[] = {'\\','D','e','v','i','c','e','\\','F','l','o','p','p','y','%','u',0};
215 static const WCHAR ramdiskW
[] = {'\\','D','e','v','i','c','e','\\','R','a','m','d','i','s','k','%','u',0};
216 static const WCHAR cdromlinkW
[] = {'\\','?','?','\\','C','d','R','o','m','%','u',0};
217 static const WCHAR physdriveW
[] = {'\\','?','?','\\','P','h','y','s','i','c','a','l','D','r','i','v','e','%','u',0};
221 const WCHAR
*format
= NULL
;
222 const WCHAR
*link_format
= NULL
;
224 DEVICE_OBJECT
*dev_obj
;
225 struct disk_device
*device
;
230 case DEVICE_HARDDISK
:
231 case DEVICE_NETWORK
: /* FIXME */
233 link_format
= physdriveW
;
235 case DEVICE_HARDDISK_VOL
:
236 format
= harddiskvolW
;
237 first
= 1; /* harddisk volumes start counting from 1 */
245 link_format
= cdromlinkW
;
252 name
.MaximumLength
= (strlenW(format
) + 10) * sizeof(WCHAR
);
253 name
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, name
.MaximumLength
);
254 for (i
= first
; i
< 32; i
++)
256 sprintfW( name
.Buffer
, format
, i
);
257 name
.Length
= strlenW(name
.Buffer
) * sizeof(WCHAR
);
258 status
= IoCreateDevice( harddisk_driver
, sizeof(*device
), &name
, 0, 0, FALSE
, &dev_obj
);
259 if (status
!= STATUS_OBJECT_NAME_COLLISION
) break;
263 device
= dev_obj
->DeviceExtension
;
264 device
->dev_obj
= dev_obj
;
267 device
->unix_device
= NULL
;
268 device
->unix_mount
= NULL
;
269 device
->symlink
.Buffer
= NULL
;
273 UNICODE_STRING symlink
;
275 symlink
.MaximumLength
= (strlenW(link_format
) + 10) * sizeof(WCHAR
);
276 if ((symlink
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, symlink
.MaximumLength
)))
278 sprintfW( symlink
.Buffer
, link_format
, i
);
279 symlink
.Length
= strlenW(symlink
.Buffer
) * sizeof(WCHAR
);
280 if (!IoCreateSymbolicLink( &symlink
, &name
)) device
->symlink
= symlink
;
288 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
289 device
->devnum
.DeviceNumber
= i
;
290 device
->devnum
.PartitionNumber
= ~0u;
293 device
->devnum
.DeviceType
= FILE_DEVICE_CD_ROM
;
294 device
->devnum
.DeviceNumber
= i
;
295 device
->devnum
.PartitionNumber
= ~0u;
298 device
->devnum
.DeviceType
= FILE_DEVICE_DVD
;
299 device
->devnum
.DeviceNumber
= i
;
300 device
->devnum
.PartitionNumber
= ~0u;
303 case DEVICE_HARDDISK
:
304 case DEVICE_NETWORK
: /* FIXME */
305 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
306 device
->devnum
.DeviceNumber
= i
;
307 device
->devnum
.PartitionNumber
= 0;
309 case DEVICE_HARDDISK_VOL
:
310 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
311 device
->devnum
.DeviceNumber
= 0;
312 device
->devnum
.PartitionNumber
= i
;
315 *device_ret
= device
;
316 TRACE( "created device %s\n", debugstr_w(name
.Buffer
) );
320 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(name
.Buffer
), status
);
321 RtlFreeUnicodeString( &name
);
326 /* delete the disk device for a given drive */
327 static void delete_disk_device( struct disk_device
*device
)
329 TRACE( "deleting device %s\n", debugstr_w(device
->name
.Buffer
) );
330 if (device
->symlink
.Buffer
)
332 IoDeleteSymbolicLink( &device
->symlink
);
333 RtlFreeUnicodeString( &device
->symlink
);
335 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_device
);
336 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_mount
);
337 RtlFreeUnicodeString( &device
->name
);
338 IoDeleteDevice( device
->dev_obj
);
341 /* grab another reference to a volume */
342 static struct volume
*grab_volume( struct volume
*volume
)
348 /* release a volume and delete the corresponding disk device when refcount is 0 */
349 static unsigned int release_volume( struct volume
*volume
)
351 unsigned int ret
= --volume
->ref
;
355 TRACE( "%s udi %s\n", debugstr_guid(&volume
->guid
), debugstr_a(volume
->udi
) );
356 assert( !volume
->udi
);
357 list_remove( &volume
->entry
);
358 if (volume
->mount
) delete_mount_point( volume
->mount
);
359 delete_disk_device( volume
->device
);
360 RtlFreeHeap( GetProcessHeap(), 0, volume
);
365 /* set the volume udi */
366 static void set_volume_udi( struct volume
*volume
, const char *udi
)
370 assert( !volume
->udi
);
371 /* having a udi means the HAL side holds an extra reference */
372 if ((volume
->udi
= strdupA( udi
))) grab_volume( volume
);
374 else if (volume
->udi
)
376 RtlFreeHeap( GetProcessHeap(), 0, volume
->udi
);
378 release_volume( volume
);
382 /* create a disk volume */
383 static NTSTATUS
create_volume( const char *udi
, enum device_type type
, struct volume
**volume_ret
)
385 struct volume
*volume
;
388 if (!(volume
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*volume
) )))
389 return STATUS_NO_MEMORY
;
391 if (!(status
= create_disk_device( type
, &volume
->device
)))
393 if (udi
) set_volume_udi( volume
, udi
);
394 list_add_tail( &volumes_list
, &volume
->entry
);
395 *volume_ret
= grab_volume( volume
);
397 else RtlFreeHeap( GetProcessHeap(), 0, volume
);
402 /* create the disk device for a given volume */
403 static NTSTATUS
create_dos_device( struct volume
*volume
, const char *udi
, int letter
,
404 enum device_type type
, struct dos_drive
**drive_ret
)
406 struct dos_drive
*drive
;
409 if (!(drive
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*drive
) ))) return STATUS_NO_MEMORY
;
410 drive
->drive
= letter
;
415 if (udi
) set_volume_udi( volume
, udi
);
416 drive
->volume
= grab_volume( volume
);
417 status
= STATUS_SUCCESS
;
419 else status
= create_volume( udi
, type
, &drive
->volume
);
421 if (status
== STATUS_SUCCESS
)
423 list_add_tail( &drives_list
, &drive
->entry
);
426 else RtlFreeHeap( GetProcessHeap(), 0, drive
);
431 /* delete the disk device for a given drive */
432 static void delete_dos_device( struct dos_drive
*drive
)
434 list_remove( &drive
->entry
);
435 if (drive
->mount
) delete_mount_point( drive
->mount
);
436 release_volume( drive
->volume
);
437 RtlFreeHeap( GetProcessHeap(), 0, drive
);
440 /* find a volume that matches the parameters */
441 static struct volume
*find_matching_volume( const char *udi
, const char *device
,
442 const char *mount_point
, enum device_type type
)
444 struct volume
*volume
;
445 struct disk_device
*disk_device
;
447 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
451 /* when we have a udi we only match drives added manually */
452 if (udi
&& volume
->udi
) continue;
453 /* and when we don't have a udi we only match dynamic drives */
454 if (!udi
&& !volume
->udi
) continue;
456 disk_device
= volume
->device
;
457 if (disk_device
->type
!= type
) continue;
458 if (device
&& disk_device
->unix_device
)
460 if (strcmp( device
, disk_device
->unix_device
)) continue;
463 if (mount_point
&& disk_device
->unix_mount
)
465 if (strcmp( mount_point
, disk_device
->unix_mount
)) continue;
468 if (!match
) continue;
469 TRACE( "found matching volume %s for device %s mount %s type %u\n",
470 debugstr_guid(&volume
->guid
), debugstr_a(device
), debugstr_a(mount_point
), type
);
471 return grab_volume( volume
);
476 /* change the information for an existing volume */
477 static NTSTATUS
set_volume_info( struct volume
*volume
, struct dos_drive
*drive
, const char *device
,
478 const char *mount_point
, enum device_type type
, const GUID
*guid
)
481 unsigned int id_len
= 0;
482 struct disk_device
*disk_device
= volume
->device
;
485 if (type
!= disk_device
->type
)
487 if ((status
= create_disk_device( type
, &disk_device
))) return status
;
490 delete_mount_point( volume
->mount
);
491 volume
->mount
= NULL
;
493 if (drive
&& drive
->mount
)
495 delete_mount_point( drive
->mount
);
498 delete_disk_device( volume
->device
);
499 volume
->device
= disk_device
;
503 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_device
);
504 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_mount
);
506 disk_device
->unix_device
= strdupA( device
);
507 disk_device
->unix_mount
= strdupA( mount_point
);
509 if (guid
&& memcmp( &volume
->guid
, guid
, sizeof(volume
->guid
) ))
511 volume
->guid
= *guid
;
514 delete_mount_point( volume
->mount
);
515 volume
->mount
= NULL
;
520 volume
->mount
= add_volume_mount_point( disk_device
->dev_obj
, &disk_device
->name
, &volume
->guid
);
521 if (drive
&& !drive
->mount
)
522 drive
->mount
= add_dosdev_mount_point( disk_device
->dev_obj
, &disk_device
->name
, drive
->drive
);
524 if (disk_device
->unix_mount
)
526 id
= disk_device
->unix_mount
;
527 id_len
= strlen( disk_device
->unix_mount
) + 1;
529 if (volume
->mount
) set_mount_point_id( volume
->mount
, id
, id_len
);
530 if (drive
&& drive
->mount
) set_mount_point_id( drive
->mount
, id
, id_len
);
532 return STATUS_SUCCESS
;
535 /* change the drive letter or volume for an existing drive */
536 static void set_drive_info( struct dos_drive
*drive
, int letter
, struct volume
*volume
)
538 if (drive
->drive
!= letter
)
540 if (drive
->mount
) delete_mount_point( drive
->mount
);
542 drive
->drive
= letter
;
544 if (drive
->volume
!= volume
)
546 if (drive
->mount
) delete_mount_point( drive
->mount
);
548 grab_volume( volume
);
549 release_volume( drive
->volume
);
550 drive
->volume
= volume
;
554 static inline BOOL
is_valid_device( struct stat
*st
)
556 #if defined(linux) || defined(__sun__)
557 return S_ISBLK( st
->st_mode
);
559 /* disks are char devices on *BSD */
560 return S_ISCHR( st
->st_mode
);
564 /* find or create a DOS drive for the corresponding device */
565 static int add_drive( const char *device
, enum device_type type
)
569 struct stat dev_st
, drive_st
;
570 int drive
, first
, last
, avail
= 0;
572 if (stat( device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return -1;
574 if (!(path
= get_dosdevices_path( &p
))) return -1;
576 memset( in_use
, 0, sizeof(in_use
) );
598 for (drive
= first
; drive
< last
; drive
++)
600 if (in_use
[drive
]) continue; /* already checked */
602 if (stat( path
, &drive_st
) == -1)
604 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
609 /* if mount point symlink doesn't exist either, it's available */
610 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
614 else in_use
[drive
] = 1;
619 if (!is_valid_device( &drive_st
)) continue;
620 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
625 /* try to use the one we found */
628 if (symlink( device
, path
) != -1) goto done
;
629 /* failed, retry the search */
635 HeapFree( GetProcessHeap(), 0, path
);
639 /* create devices for mapped drives */
640 static void create_drive_devices(void)
642 char *path
, *p
, *link
, *device
;
643 struct dos_drive
*drive
;
644 struct volume
*volume
;
647 enum device_type drive_type
;
648 WCHAR driveW
[] = {'a',':',0};
650 if (!(path
= get_dosdevices_path( &p
))) return;
651 if (RegOpenKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &drives_key
)) drives_key
= 0;
653 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
657 if (!(link
= read_symlink( path
))) continue;
659 device
= read_symlink( path
);
661 drive_type
= i
< 2 ? DEVICE_FLOPPY
: DEVICE_HARDDISK_VOL
;
665 DWORD j
, type
, size
= sizeof(buffer
);
668 if (!RegQueryValueExW( drives_key
, driveW
, NULL
, &type
, (BYTE
*)buffer
, &size
) &&
671 for (j
= 0; j
< ARRAY_SIZE(drive_types
); j
++)
672 if (drive_types
[j
][0] && !strcmpiW( buffer
, drive_types
[j
] ))
677 if (drive_type
== DEVICE_FLOPPY
&& i
>= 2) drive_type
= DEVICE_HARDDISK
;
681 volume
= find_matching_volume( NULL
, device
, link
, drive_type
);
682 if (!create_dos_device( volume
, NULL
, i
, drive_type
, &drive
))
684 /* don't reset uuid if we used an existing volume */
685 const GUID
*guid
= volume
? NULL
: get_default_uuid(i
);
686 set_volume_info( drive
->volume
, drive
, device
, link
, drive_type
, guid
);
690 RtlFreeHeap( GetProcessHeap(), 0, link
);
691 RtlFreeHeap( GetProcessHeap(), 0, device
);
693 if (volume
) release_volume( volume
);
695 RegCloseKey( drives_key
);
696 RtlFreeHeap( GetProcessHeap(), 0, path
);
699 /* open the "Logical Unit" key for a given SCSI address */
700 static HKEY
get_scsi_device_lun_key( SCSI_ADDRESS
*scsi_addr
)
703 HKEY scsi_key
, port_key
, bus_key
, target_key
, lun_key
;
705 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE
, scsi_keyW
, 0, KEY_READ
|KEY_WRITE
, &scsi_key
)) return NULL
;
707 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_port_keyW
, scsi_addr
->PortNumber
);
708 if (RegCreateKeyExW( scsi_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &port_key
, NULL
)) return NULL
;
709 RegCloseKey( scsi_key
);
711 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_bus_keyW
, scsi_addr
->PathId
);
712 if (RegCreateKeyExW( port_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &bus_key
, NULL
)) return NULL
;
713 RegCloseKey( port_key
);
715 snprintfW( dataW
, ARRAY_SIZE( dataW
), target_id_keyW
, scsi_addr
->TargetId
);
716 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return NULL
;
717 RegCloseKey( bus_key
);
719 snprintfW( dataW
, ARRAY_SIZE( dataW
), lun_keyW
, scsi_addr
->Lun
);
720 if (RegCreateKeyExW( target_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &lun_key
, NULL
)) return NULL
;
721 RegCloseKey( target_key
);
726 /* fill in the "Logical Unit" key for a given SCSI address */
727 void create_scsi_entry( SCSI_ADDRESS
*scsi_addr
, UINT init_id
, const char *driver
, UINT type
, const char *model
, const UNICODE_STRING
*dev
)
729 static UCHAR tape_no
= 0;
730 static const WCHAR tapeW
[] = {'T','a','p','e','%','d',0};
731 static const WCHAR init_id_keyW
[] = {'I','n','i','t','i','a','t','o','r',' ','I','d',' ','%','d',0};
732 static const WCHAR driverW
[] = {'D','r','i','v','e','r',0};
733 static const WCHAR bus_time_scanW
[] = {'F','i','r','s','t','B','u','s','T','i','m','e','S','c','a','n','I','n','M','s',0};
734 static const WCHAR typeW
[] = {'T','y','p','e',0};
735 static const WCHAR identW
[] = {'I','d','e','n','t','i','f','i','e','r',0};
747 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE
, scsi_keyW
, 0, KEY_READ
|KEY_WRITE
, &scsi_key
)) return;
749 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_port_keyW
, scsi_addr
->PortNumber
);
750 if (RegCreateKeyExW( scsi_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &port_key
, NULL
)) return;
751 RegCloseKey( scsi_key
);
753 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, driver
, strlen(driver
)+1);
754 RegSetValueExW( port_key
, driverW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
756 RegSetValueExW( port_key
, bus_time_scanW
, 0, REG_DWORD
, (const BYTE
*)&value
, sizeof(value
));
760 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_bus_keyW
, scsi_addr
->PathId
);
761 if (RegCreateKeyExW( port_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &bus_key
, NULL
)) return;
762 RegCloseKey( port_key
);
764 snprintfW( dataW
, ARRAY_SIZE( dataW
), init_id_keyW
, init_id
);
765 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
766 RegCloseKey( target_key
);
768 snprintfW( dataW
, ARRAY_SIZE( dataW
), target_id_keyW
, scsi_addr
->TargetId
);
769 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
770 RegCloseKey( bus_key
);
772 snprintfW( dataW
, ARRAY_SIZE( dataW
), lun_keyW
, scsi_addr
->Lun
);
773 if (RegCreateKeyExW( target_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &lun_key
, NULL
)) return;
774 RegCloseKey( target_key
);
778 case SCSI_DISK_PERIPHERAL
: data
= "DiskPeripheral"; break;
779 case SCSI_TAPE_PERIPHERAL
: data
= "TapePeripheral"; break;
780 case SCSI_PRINTER_PERIPHERAL
: data
= "PrinterPeripheral"; break;
781 case SCSI_WORM_PERIPHERAL
: data
= "WormPeripheral"; break;
782 case SCSI_CDROM_PERIPHERAL
: data
= "CdRomPeripheral"; break;
783 case SCSI_SCANNER_PERIPHERAL
: data
= "ScannerPeripheral"; break;
784 case SCSI_OPTICAL_DISK_PERIPHERAL
: data
= "OpticalDiskPeripheral"; break;
785 case SCSI_MEDIUM_CHANGER_PERIPHERAL
: data
= "MediumChangerPeripheral"; break;
786 case SCSI_COMMS_PERIPHERAL
: data
= "CommunicationsPeripheral"; break;
787 case SCSI_ASC_GRAPHICS_PERIPHERAL
:
788 case SCSI_ASC_GRAPHICS2_PERIPHERAL
: data
= "ASCPrePressGraphicsPeripheral"; break;
789 case SCSI_ARRAY_PERIPHERAL
: data
= "ArrayPeripheral"; break;
790 case SCSI_ENCLOSURE_PERIPHERAL
: data
= "EnclosurePeripheral"; break;
791 case SCSI_REDUCED_DISK_PERIPHERAL
: data
= "RBCPeripheral"; break;
792 case SCSI_CARD_READER_PERIPHERAL
: data
= "CardReaderPeripheral"; break;
793 case SCSI_BRIDGE_PERIPHERAL
: data
= "BridgePeripheral"; break;
794 case SCSI_OBJECT_STORAGE_PERIPHERAL
: /* Object-based storage devices */
795 case SCSI_DRIVE_CONTROLLER_PERIPHERAL
: /* Automation/drive controllers */
796 case SCSI_REDUCED_CDROM_PERIPHERAL
: /* Reduced-commands MM devices */
797 case SCSI_PROCESSOR_PERIPHERAL
: /* Processor devices (considered to be "Other" by Windows) */
798 default: data
= "OtherPeripheral"; break;
800 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, data
, strlen(data
)+1);
801 RegSetValueExW( lun_key
, typeW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
803 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, model
, strlen(model
)+1);
804 RegSetValueExW( lun_key
, identW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
808 WCHAR
*buffer
= memchrW( dev
->Buffer
+1, '\\', dev
->Length
)+1;
809 ULONG length
= dev
->Length
- (buffer
- dev
->Buffer
)*sizeof(WCHAR
);
810 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)buffer
, length
);
812 else if (type
== SCSI_TAPE_PERIPHERAL
)
814 snprintfW( dataW
, ARRAY_SIZE( dataW
), tapeW
, tape_no
++ );
815 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)dataW
, strlenW( dataW
) );
818 RegCloseKey( lun_key
);
821 /* set the "DeviceName" for a given SCSI address */
822 void set_scsi_device_name( SCSI_ADDRESS
*scsi_addr
, const UNICODE_STRING
*dev
)
828 lun_key
= get_scsi_device_lun_key( scsi_addr
);
829 buffer
= memchrW( dev
->Buffer
+1, '\\', dev
->Length
)+1;
830 length
= dev
->Length
- (buffer
- dev
->Buffer
)*sizeof(WCHAR
);
831 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)buffer
, length
);
833 RegCloseKey( lun_key
);
837 /* create a new disk volume */
838 NTSTATUS
add_volume( const char *udi
, const char *device
, const char *mount_point
,
839 enum device_type type
, const GUID
*guid
)
841 struct volume
*volume
;
842 NTSTATUS status
= STATUS_SUCCESS
;
844 TRACE( "adding %s device %s mount %s type %u uuid %s\n", debugstr_a(udi
),
845 debugstr_a(device
), debugstr_a(mount_point
), type
, debugstr_guid(guid
) );
847 EnterCriticalSection( &device_section
);
848 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
849 if (volume
->udi
&& !strcmp( udi
, volume
->udi
))
851 grab_volume( volume
);
855 /* udi not found, search for a non-dynamic volume */
856 if ((volume
= find_matching_volume( udi
, device
, mount_point
, type
))) set_volume_udi( volume
, udi
);
857 else status
= create_volume( udi
, type
, &volume
);
860 if (!status
) status
= set_volume_info( volume
, NULL
, device
, mount_point
, type
, guid
);
861 if (volume
) release_volume( volume
);
862 LeaveCriticalSection( &device_section
);
866 /* create a new disk volume */
867 NTSTATUS
remove_volume( const char *udi
)
869 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
870 struct volume
*volume
;
872 EnterCriticalSection( &device_section
);
873 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
875 if (!volume
->udi
|| strcmp( udi
, volume
->udi
)) continue;
876 set_volume_udi( volume
, NULL
);
877 status
= STATUS_SUCCESS
;
880 LeaveCriticalSection( &device_section
);
885 /* create a new dos drive */
886 NTSTATUS
add_dos_device( int letter
, const char *udi
, const char *device
,
887 const char *mount_point
, enum device_type type
, const GUID
*guid
,
888 UNICODE_STRING
*devname
)
892 NTSTATUS status
= STATUS_SUCCESS
;
893 struct dos_drive
*drive
, *next
;
894 struct volume
*volume
;
897 if (!(path
= get_dosdevices_path( &p
))) return STATUS_NO_MEMORY
;
899 EnterCriticalSection( &device_section
);
900 volume
= find_matching_volume( udi
, device
, mount_point
, type
);
902 if (letter
== -1) /* auto-assign a letter */
904 letter
= add_drive( device
, type
);
907 status
= STATUS_OBJECT_NAME_COLLISION
;
911 LIST_FOR_EACH_ENTRY_SAFE( drive
, next
, &drives_list
, struct dos_drive
, entry
)
913 if (drive
->volume
->udi
&& !strcmp( udi
, drive
->volume
->udi
)) goto found
;
914 if (drive
->drive
== letter
) delete_dos_device( drive
);
917 else /* simply reset the device symlink */
919 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
920 if (drive
->drive
== letter
) break;
923 if (&drive
->entry
== &drives_list
) update_symlink( path
, device
, NULL
);
926 update_symlink( path
, device
, drive
->volume
->device
->unix_device
);
927 delete_dos_device( drive
);
931 if ((status
= create_dos_device( volume
, udi
, letter
, type
, &drive
))) goto done
;
934 if (!guid
&& !volume
) guid
= get_default_uuid( letter
);
935 if (!volume
) volume
= grab_volume( drive
->volume
);
936 set_drive_info( drive
, letter
, volume
);
937 p
[0] = 'a' + drive
->drive
;
939 update_symlink( path
, mount_point
, volume
->device
->unix_mount
);
940 set_volume_info( volume
, drive
, device
, mount_point
, type
, guid
);
942 TRACE( "added device %c: udi %s for %s on %s type %u\n",
943 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
944 wine_dbgstr_a(mount_point
), type
);
946 /* hack: force the drive type in the registry */
947 if (!RegCreateKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &hkey
))
949 const WCHAR
*type_name
= drive_types
[type
];
950 WCHAR name
[] = {'a',':',0};
952 name
[0] += drive
->drive
;
953 if (!type_name
[0] && type
== DEVICE_HARDDISK
) type_name
= drive_types
[DEVICE_FLOPPY
];
955 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type_name
,
956 (strlenW(type_name
) + 1) * sizeof(WCHAR
) );
958 RegDeleteValueW( hkey
, name
);
962 if (udi
) notify
= drive
->drive
;
964 if (devname
) *devname
= volume
->device
->name
;
967 if (volume
) release_volume( volume
);
968 LeaveCriticalSection( &device_section
);
969 RtlFreeHeap( GetProcessHeap(), 0, path
);
970 if (notify
!= -1) send_notify( notify
, DBT_DEVICEARRIVAL
);
974 /* remove an existing dos drive, by letter or udi */
975 NTSTATUS
remove_dos_device( int letter
, const char *udi
)
977 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
979 struct dos_drive
*drive
;
983 EnterCriticalSection( &device_section
);
984 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
988 if (!drive
->volume
->udi
) continue;
989 if (strcmp( udi
, drive
->volume
->udi
)) continue;
990 set_volume_udi( drive
->volume
, NULL
);
992 else if (drive
->drive
!= letter
) continue;
994 if ((path
= get_dosdevices_path( &p
)))
996 p
[0] = 'a' + drive
->drive
;
999 RtlFreeHeap( GetProcessHeap(), 0, path
);
1002 /* clear the registry key too */
1003 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &hkey
))
1005 WCHAR name
[] = {'a',':',0};
1006 name
[0] += drive
->drive
;
1007 RegDeleteValueW( hkey
, name
);
1008 RegCloseKey( hkey
);
1011 if (udi
&& drive
->volume
->device
->unix_mount
) notify
= drive
->drive
;
1013 delete_dos_device( drive
);
1014 status
= STATUS_SUCCESS
;
1017 LeaveCriticalSection( &device_section
);
1018 if (notify
!= -1) send_notify( notify
, DBT_DEVICEREMOVECOMPLETE
);
1022 /* query information about an existing dos drive, by letter or udi */
1023 NTSTATUS
query_dos_device( int letter
, enum device_type
*type
, char **device
, char **mount_point
)
1025 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
1026 struct dos_drive
*drive
;
1027 struct disk_device
*disk_device
;
1029 EnterCriticalSection( &device_section
);
1030 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1032 if (drive
->drive
!= letter
) continue;
1033 disk_device
= drive
->volume
->device
;
1034 if (type
) *type
= disk_device
->type
;
1035 if (device
) *device
= strdupA( disk_device
->unix_device
);
1036 if (mount_point
) *mount_point
= strdupA( disk_device
->unix_mount
);
1037 status
= STATUS_SUCCESS
;
1040 LeaveCriticalSection( &device_section
);
1044 static void query_property(IRP
*irp
)
1046 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1047 STORAGE_PROPERTY_QUERY
*query
= irp
->AssociatedIrp
.SystemBuffer
;
1049 if (!irp
->AssociatedIrp
.SystemBuffer
1050 || irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(STORAGE_PROPERTY_QUERY
))
1052 irp
->IoStatus
.u
.Status
= STATUS_INVALID_PARAMETER
;
1056 /* Try to persuade application not to check property */
1057 if (query
->QueryType
== PropertyExistsQuery
)
1059 irp
->IoStatus
.u
.Status
= STATUS_NOT_SUPPORTED
;
1063 switch (query
->PropertyId
)
1065 case StorageDeviceProperty
:
1067 STORAGE_DEVICE_DESCRIPTOR
*descriptor
;
1069 if (!irp
->UserBuffer
1070 || irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(STORAGE_DESCRIPTOR_HEADER
))
1071 irp
->IoStatus
.u
.Status
= STATUS_INVALID_PARAMETER
;
1072 else if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(STORAGE_DEVICE_DESCRIPTOR
))
1074 descriptor
= irp
->UserBuffer
;
1075 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1076 descriptor
->Size
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1077 irp
->IoStatus
.Information
= sizeof(STORAGE_DESCRIPTOR_HEADER
);
1078 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1082 FIXME( "Faking StorageDeviceProperty data\n" );
1084 memset( irp
->UserBuffer
, 0, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1085 descriptor
= irp
->UserBuffer
;
1086 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1087 descriptor
->Size
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1088 descriptor
->DeviceType
= FILE_DEVICE_DISK
;
1089 descriptor
->DeviceTypeModifier
= 0;
1090 descriptor
->RemovableMedia
= FALSE
;
1091 descriptor
->CommandQueueing
= FALSE
;
1092 descriptor
->VendorIdOffset
= 0;
1093 descriptor
->ProductIdOffset
= 0;
1094 descriptor
->ProductRevisionOffset
= 0;
1095 descriptor
->SerialNumberOffset
= 0;
1096 descriptor
->BusType
= BusTypeScsi
;
1097 descriptor
->RawPropertiesLength
= 0;
1099 irp
->IoStatus
.Information
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1100 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1106 FIXME( "Unsupported property %#x\n", query
->PropertyId
);
1107 irp
->IoStatus
.u
.Status
= STATUS_NOT_SUPPORTED
;
1112 /* handler for ioctls on the harddisk device */
1113 static NTSTATUS WINAPI
harddisk_ioctl( DEVICE_OBJECT
*device
, IRP
*irp
)
1115 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1116 struct disk_device
*dev
= device
->DeviceExtension
;
1118 TRACE( "ioctl %x insize %u outsize %u\n",
1119 irpsp
->Parameters
.DeviceIoControl
.IoControlCode
,
1120 irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
,
1121 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1123 EnterCriticalSection( &device_section
);
1125 switch(irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
1127 case IOCTL_DISK_GET_DRIVE_GEOMETRY
:
1130 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1132 info
.Cylinders
.QuadPart
= 10000;
1133 info
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
1134 info
.TracksPerCylinder
= 255;
1135 info
.SectorsPerTrack
= 63;
1136 info
.BytesPerSector
= 512;
1137 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
1138 irp
->IoStatus
.Information
= len
;
1139 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1142 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
:
1144 DISK_GEOMETRY_EX info
;
1145 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1147 FIXME("The DISK_PARTITION_INFO and DISK_DETECTION_INFO structures will not be filled\n");
1149 info
.Geometry
.Cylinders
.QuadPart
= 10000;
1150 info
.Geometry
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
1151 info
.Geometry
.TracksPerCylinder
= 255;
1152 info
.Geometry
.SectorsPerTrack
= 63;
1153 info
.Geometry
.BytesPerSector
= 512;
1154 info
.DiskSize
.QuadPart
= info
.Geometry
.Cylinders
.QuadPart
* info
.Geometry
.TracksPerCylinder
*
1155 info
.Geometry
.SectorsPerTrack
* info
.Geometry
.BytesPerSector
;
1157 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
1158 irp
->IoStatus
.Information
= len
;
1159 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1162 case IOCTL_STORAGE_GET_DEVICE_NUMBER
:
1164 DWORD len
= min( sizeof(dev
->devnum
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1166 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &dev
->devnum
, len
);
1167 irp
->IoStatus
.Information
= len
;
1168 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1171 case IOCTL_CDROM_READ_TOC
:
1172 irp
->IoStatus
.u
.Status
= STATUS_INVALID_DEVICE_REQUEST
;
1174 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
:
1176 DWORD len
= min( 32, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1178 FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" );
1179 memset( irp
->AssociatedIrp
.SystemBuffer
, 0, len
);
1180 irp
->IoStatus
.Information
= len
;
1181 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
1184 case IOCTL_STORAGE_QUERY_PROPERTY
:
1185 query_property( irp
);
1189 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
1190 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
1191 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
1192 irp
->IoStatus
.u
.Status
= STATUS_NOT_SUPPORTED
;
1197 LeaveCriticalSection( &device_section
);
1198 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
1199 return STATUS_SUCCESS
;
1202 /* driver entry point for the harddisk driver */
1203 NTSTATUS WINAPI
harddisk_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1205 struct disk_device
*device
;
1207 harddisk_driver
= driver
;
1208 driver
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = harddisk_ioctl
;
1210 /* create a harddisk0 device that isn't assigned to any drive */
1211 create_disk_device( DEVICE_HARDDISK
, &device
);
1213 create_drive_devices();
1215 return STATUS_SUCCESS
;
1219 /* create a serial or parallel port */
1220 static BOOL
create_port_device( DRIVER_OBJECT
*driver
, int n
, const char *unix_path
,
1221 const char *dosdevices_path
, HKEY windows_ports_key
)
1223 static const WCHAR comW
[] = {'C','O','M','%','u',0};
1224 static const WCHAR lptW
[] = {'L','P','T','%','u',0};
1225 static const WCHAR device_serialW
[] = {'\\','D','e','v','i','c','e','\\','S','e','r','i','a','l','%','u',0};
1226 static const WCHAR device_parallelW
[] = {'\\','D','e','v','i','c','e','\\','P','a','r','a','l','l','e','l','%','u',0};
1227 static const WCHAR dosdevices_comW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','%','u',0};
1228 static const WCHAR dosdevices_auxW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','A','U','X',0};
1229 static const WCHAR dosdevices_lptW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','%','u',0};
1230 static const WCHAR dosdevices_prnW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','P','R','N',0};
1231 const WCHAR
*dos_name_format
, *nt_name_format
, *reg_value_format
, *symlink_format
, *default_device
;
1232 WCHAR dos_name
[7], reg_value
[256], nt_buffer
[32], symlink_buffer
[32];
1233 UNICODE_STRING nt_name
, symlink_name
, default_name
;
1234 DEVICE_OBJECT
*dev_obj
;
1237 if (driver
== serial_driver
)
1239 dos_name_format
= comW
;
1240 nt_name_format
= device_serialW
;
1241 reg_value_format
= comW
;
1242 symlink_format
= dosdevices_comW
;
1243 default_device
= dosdevices_auxW
;
1247 dos_name_format
= lptW
;
1248 nt_name_format
= device_parallelW
;
1249 reg_value_format
= dosdevices_lptW
;
1250 symlink_format
= dosdevices_lptW
;
1251 default_device
= dosdevices_prnW
;
1254 sprintfW( dos_name
, dos_name_format
, n
);
1256 /* create DOS device */
1257 unlink( dosdevices_path
);
1258 if (symlink( unix_path
, dosdevices_path
) != 0)
1261 /* create NT device */
1262 sprintfW( nt_buffer
, nt_name_format
, n
- 1 );
1263 RtlInitUnicodeString( &nt_name
, nt_buffer
);
1264 status
= IoCreateDevice( driver
, 0, &nt_name
, 0, 0, FALSE
, &dev_obj
);
1265 if (status
!= STATUS_SUCCESS
)
1267 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(nt_name
.Buffer
), status
);
1270 sprintfW( symlink_buffer
, symlink_format
, n
);
1271 RtlInitUnicodeString( &symlink_name
, symlink_buffer
);
1272 IoCreateSymbolicLink( &symlink_name
, &nt_name
);
1275 RtlInitUnicodeString( &default_name
, default_device
);
1276 IoCreateSymbolicLink( &default_name
, &symlink_name
);
1279 /* TODO: store information about the Unix device in the NT device */
1281 /* create registry entry */
1282 sprintfW( reg_value
, reg_value_format
, n
);
1283 RegSetValueExW( windows_ports_key
, nt_name
.Buffer
, 0, REG_SZ
,
1284 (BYTE
*)reg_value
, (strlenW( reg_value
) + 1) * sizeof(WCHAR
) );
1289 /* find and create serial or parallel ports */
1290 static void create_port_devices( DRIVER_OBJECT
*driver
)
1292 static const char *serial_search_paths
[] = {
1297 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1299 #elif defined(__DragonFly__)
1304 static const char *parallel_search_paths
[] = {
1310 static const WCHAR serialcomm_keyW
[] = {'H','A','R','D','W','A','R','E','\\',
1311 'D','E','V','I','C','E','M','A','P','\\',
1312 'S','E','R','I','A','L','C','O','M','M',0};
1313 static const WCHAR parallel_ports_keyW
[] = {'H','A','R','D','W','A','R','E','\\',
1314 'D','E','V','I','C','E','M','A','P','\\',
1315 'P','A','R','A','L','L','E','L',' ','P','O','R','T','S',0};
1316 static const WCHAR comW
[] = {'C','O','M'};
1317 static const WCHAR lptW
[] = {'L','P','T'};
1318 const char **search_paths
;
1319 const WCHAR
*windows_ports_key_name
;
1320 char *dosdevices_path
, *p
;
1321 HKEY wine_ports_key
= NULL
, windows_ports_key
= NULL
;
1322 char unix_path
[256];
1323 const WCHAR
*port_prefix
;
1324 WCHAR reg_value
[256];
1325 BOOL used
[MAX_PORTS
];
1327 DWORD port_len
, type
, size
;
1330 if (!(dosdevices_path
= get_dosdevices_path( &p
)))
1333 if (driver
== serial_driver
)
1338 search_paths
= serial_search_paths
;
1339 windows_ports_key_name
= serialcomm_keyW
;
1347 search_paths
= parallel_search_paths
;
1348 windows_ports_key_name
= parallel_ports_keyW
;
1353 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, ports_keyW
, 0, NULL
, 0,
1354 KEY_QUERY_VALUE
, NULL
, &wine_ports_key
, NULL
);
1355 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, windows_ports_key_name
, 0, NULL
, REG_OPTION_VOLATILE
,
1356 KEY_ALL_ACCESS
, NULL
, &windows_ports_key
, NULL
);
1358 /* add user-defined serial ports */
1359 memset(used
, 0, sizeof(used
));
1362 port_len
= ARRAY_SIZE(port
);
1363 size
= sizeof(reg_value
);
1364 if (RegEnumValueW( wine_ports_key
, i
, port
, &port_len
, NULL
,
1365 &type
, (BYTE
*)reg_value
, &size
) != ERROR_SUCCESS
)
1367 if (type
!= REG_SZ
|| strncmpiW( port
, port_prefix
, 3 ))
1370 n
= atolW( port
+ 3 );
1371 if (n
< 1 || n
>= MAX_PORTS
)
1374 if (!WideCharToMultiByte( CP_UNIXCP
, WC_ERR_INVALID_CHARS
, reg_value
, size
/sizeof(WCHAR
),
1375 unix_path
, sizeof(unix_path
), NULL
, NULL
))
1379 sprintf( p
, "%u", n
);
1380 create_port_device( driver
, n
, unix_path
, dosdevices_path
, windows_ports_key
);
1383 /* look for ports in the usual places */
1385 while (n
<= MAX_PORTS
&& used
[n
- 1]) n
++;
1386 for (i
= 0; search_paths
[i
]; i
++)
1388 for (j
= 0; n
<= MAX_PORTS
; j
++)
1390 sprintf( unix_path
, search_paths
[i
], j
);
1391 if (access( unix_path
, F_OK
) != 0)
1394 sprintf( p
, "%u", n
);
1395 create_port_device( driver
, n
, unix_path
, dosdevices_path
, windows_ports_key
);
1397 while (n
<= MAX_PORTS
&& used
[n
- 1]) n
++;
1401 RegCloseKey( wine_ports_key
);
1402 RegCloseKey( windows_ports_key
);
1403 HeapFree( GetProcessHeap(), 0, dosdevices_path
);
1406 /* driver entry point for the serial port driver */
1407 NTSTATUS WINAPI
serial_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1409 serial_driver
= driver
;
1410 /* TODO: fill in driver->MajorFunction */
1412 create_port_devices( driver
);
1414 return STATUS_SUCCESS
;
1417 /* driver entry point for the parallel port driver */
1418 NTSTATUS WINAPI
parallel_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1420 parallel_driver
= driver
;
1421 /* TODO: fill in driver->MajorFunction */
1423 create_port_devices( driver
);
1425 return STATUS_SUCCESS
;