Beginning of devices in win2k/nt mode for ipod support

Aric Stewart aric at codeweavers.com
Tue Aug 17 14:28:06 CDT 2004


Here is the chunk of work i have done on realllllly basic handling of 
USBSTOR device.

I doubt Alexandre will let this get into cvs yet... but i may be wrong 
so i will try submitting it here.

It allows the creation of the //./USBSTOR<...> device and 1 
DeviceIoControl call that iPodService calls.

Additional it handles the DeviceIoControl call that iPodService calls on
//./<driveletter>: that iPodService uses to look for the mounted iPod by
resolving the call down into SCSI ioctl calls that retreave the 
information that is needed

This is not really well structured (i am still advocating i want a 
usbstor.sys or equiviant dll) but is pretty well setup. It also will 
fail miserable on any other devices or DeviceIoControl calls. But is is 
sufficient for the iPod.

-aric
-------------- next part --------------
Index: dlls/kernel/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/kernel/Makefile.in,v
retrieving revision 1.114
diff -u -r1.114 Makefile.in
--- dlls/kernel/Makefile.in	11 Aug 2004 23:59:07 -0000	1.114
+++ dlls/kernel/Makefile.in	17 Aug 2004 19:22:13 -0000
@@ -26,6 +26,7 @@
 	console.c \
 	cpu.c \
 	debugger.c \
+	device.c \
 	dosmem.c \
 	editline.c \
 	environ.c \
Index: dlls/kernel/file.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/file.c,v
retrieving revision 1.24
diff -u -r1.24 file.c
--- dlls/kernel/file.c	9 Jul 2004 22:24:59 -0000	1.24
+++ dlls/kernel/file.c	17 Aug 2004 19:22:14 -0000
@@ -1243,7 +1243,11 @@
         }
         else if (filename[4])
         {
-            ret = VXD_Open( filename+4, access, sa );
+            if (!(GetVersion() & 0x80000000))  /* there are no VxDs on NT */
+                ret = Device_Open( filename+4, access, sa );
+            else
+                ret = VXD_Open( filename+4, access, sa );
+
             goto done;
         }
         else
Index: dlls/kernel/kernel_private.h
===================================================================
RCS file: /home/wine/wine/dlls/kernel/kernel_private.h,v
retrieving revision 1.20
diff -u -r1.20 kernel_private.h
--- dlls/kernel/kernel_private.h	14 May 2004 21:43:18 -0000	1.20
+++ dlls/kernel/kernel_private.h	17 Aug 2004 19:22:14 -0000
@@ -76,6 +76,13 @@
    (wine_ldt_is_system(sel) || (wine_ldt_copy.flags[LOWORD(sel) >> 3] & WINE_LDT_FLAGS_32BIT))
 
 extern HANDLE VXD_Open( LPCWSTR filename, DWORD access, LPSECURITY_ATTRIBUTES sa );
+extern HANDLE Device_Open( LPCWSTR filename, DWORD access, LPSECURITY_ATTRIBUTES sa );
+
+extern BOOL Device_DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode,
+                            LPVOID lpvInBuffer, DWORD cbInBuffer,
+                            LPVOID lpvOutBuffer, DWORD cbOutBuffer,
+                            LPDWORD lpcbBytesReturned,
+                            LPOVERLAPPED lpOverlapped);
 
 /* this structure is always located at offset 0 of the DGROUP segment */
 #include "pshpack1.h"
Index: dlls/kernel/vxd.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/vxd.c,v
retrieving revision 1.14
diff -u -r1.14 vxd.c
--- dlls/kernel/vxd.c	6 Jul 2004 21:31:24 -0000	1.14
+++ dlls/kernel/vxd.c	17 Aug 2004 19:22:14 -0000
@@ -36,6 +36,7 @@
 #include "winbase.h"
 #include "winreg.h"
 #include "winerror.h"
+#include "winioctl.h"
 #include "kernel_private.h"
 #include "wine/library.h"
 #include "wine/unicode.h"
@@ -349,6 +350,11 @@
                                lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped );
         return FALSE;
     }
+
+    if ( HIWORD( dwIoControlCode ) == IOCTL_STORAGE_BASE)
+        return Device_DeviceIoControl(hDevice,dwIoControlCode, lpvInBuffer,
+                                      cbInBuffer, lpvOutBuffer, cbOutBuffer,
+                                      lpcbBytesReturned, lpOverlapped);
 
     /* Not a VxD, let ntdll handle it */
 
--- /dev/null	Thu Aug 30 15:30:55 2001
+++ dlls/kernel/device.c	Tue Aug 17 14:20:52 2004
@@ -0,0 +1,375 @@
+/*
+ * Win32 Device functions
+ *
+ * Copyright 2004 Codeweavers (Aric Stewart)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winerror.h"
+#include "winioctl.h"
+#include "ntddstor.h"
+#include "kernel_private.h"
+#include "wine/library.h"
+#include "wine/unicode.h"
+#include "wine/server.h"
+#include "wine/debug.h"
+
+/* linux includes */
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SCSI_SG_H
+#include <scsi/sg.h>
+#endif
+#ifdef HAVE_SCSI_SCSI_H
+#include <scsi/scsi.h>
+#endif
+#ifdef HAVE_SCSI_SCSI_IOCTL_H
+#include <scsi/scsi_ioctl.h>
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(device);
+
+
+#define MAX_DEVICE_MODULES 32
+#define SENSE_BUFF_LEN 32
+#define INQUIRY_CMD 0x12
+#define INQUIRY_CMDLEN 6
+#define RESPONCE_LEN 96
+#define DEF_TIMEOUT 60000
+
+
+static CRITICAL_SECTION device_section;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &device_section,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { 0, (DWORD)(__FILE__ ": device_section") }
+};
+static CRITICAL_SECTION device_section = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+
+/* scsi structs */
+
+typedef struct scsi_idlun_ {
+      int four_in_one;    /* 4 separate bytes of info
+                             compacted into 1 int */
+      int host_unique_id; /* distinguishes adapter cards from
+                             same supplier */
+} scsi_idlun;
+
+
+/*
+ * control what devices are openable 
+ */
+#define DEVICE_NAME_LEN 20
+typedef WCHAR device_name[DEVICE_NAME_LEN];
+static const device_name known_devices[] = {
+    {'U','S','B','S','T','O','R',0},
+    {0}
+    };
+
+struct device_module
+{
+    device_name  device;
+    HANDLE       handle;
+};
+
+static struct device_module device_modules[MAX_DEVICE_MODULES];
+
+
+static int get_device_fd (HANDLE handle, DWORD access)
+{
+    int fd, ret;
+    ret = wine_server_handle_to_fd( handle, access, &fd, NULL, NULL );
+    if (ret) SetLastError( RtlNtStatusToDosError(ret) );
+    return fd;
+}
+
+static inline void release_device_fd( HANDLE handle, int fd)
+{
+    wine_server_release_fd( handle, fd );
+}
+
+
+/* create a file handle to represent the device, Based on the VXD version */
+static HANDLE open_device_handle( LPCWSTR name )
+{
+    const char *dir = wine_get_server_dir();
+    int len;
+    HANDLE ret;
+    NTSTATUS status;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING nameW;
+    IO_STATUS_BLOCK io;
+
+    len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 );
+    nameW.Length = (len + 2 + strlenW(name)) * sizeof(WCHAR);
+    nameW.MaximumLength = nameW.Length + sizeof(WCHAR);
+    if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.Length )))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return 0;
+    }
+    MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer, len );
+    nameW.Buffer[len-1] = '/';
+    strcpyW( nameW.Buffer + len, name);
+
+    TRACE("file is %s\n",debugstr_w(nameW.Buffer));
+    attr.Length = sizeof(attr);
+    attr.RootDirectory = 0;
+    attr.Attributes = 0;
+    attr.ObjectName = &nameW;
+    attr.SecurityDescriptor = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    status = NtCreateFile( &ret, 0, &attr, &io, NULL, 0,
+                           FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF,
+                           FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 );
+    if (status)
+    {
+        ret = 0;
+        SetLastError( RtlNtStatusToDosError(status) );
+    }
+    RtlFreeUnicodeString( &nameW );
+    return ret;
+}
+
+/* load a Device and return a file handle to it */
+HANDLE Device_Open( LPCWSTR filenameW, DWORD access, SECURITY_ATTRIBUTES *sa )
+{
+    int i;
+    HANDLE handle;
+    device_name Name; 
+    LPWSTR ptr;
+
+    /* get the device name */
+    ptr = strchrW(filenameW,'#'); 
+    if (ptr)
+        i = min(DEVICE_NAME_LEN,ptr - filenameW);
+    else
+        i = DEVICE_NAME_LEN;
+
+    strncpyW(Name,filenameW,i);
+    Name[i] = 0;
+
+    TRACE("Trying to open device %s\n",debugstr_w(Name));
+    
+    /* check the device name agienst handled devices */
+    for (i = 0; known_devices[i][0]; i++)
+    {
+        if (strcmpiW(Name,known_devices[i])==0)
+            break;
+    }
+    if (known_devices[i][0]==0)
+    {
+        FIXME("Unhandled device %s\n",debugstr_w(Name));
+        return 0;
+    }
+
+    RtlEnterCriticalSection( &device_section );
+
+    for (i = 0; i < MAX_DEVICE_MODULES; i++)
+    {
+        if (strcmpiW(device_modules[i].device,Name) == 0)
+        {
+            handle = device_modules[i].handle;
+            goto done;  /* already registered */
+        }
+        if (!device_modules[i].handle)  /* new one, register it */
+        {
+            int fd;
+
+            /* get a file handle to the dummy file */
+            if (!(handle = open_device_handle( Name )))
+            {
+                goto done;
+            }
+            wine_server_handle_to_fd( handle, 0, &fd, NULL, NULL );
+            strcpyW(device_modules[i].device,Name);
+            device_modules[i].handle = handle;
+            wine_server_release_fd( handle, fd );
+            goto done;
+        }
+    }
+
+    ERR("too many open device modules, please report\n" );
+    CloseHandle( handle );
+    handle = 0;
+
+done:
+    RtlLeaveCriticalSection( &device_section );
+    if (!DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle, 0,
+                          (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle),
+                          DUP_HANDLE_SAME_ACCESS ))
+        handle = 0;
+    return handle;
+}
+
+/*
+ * This function is very incomplete. It totally is ONLY targeting the ipod
+ * device at this time.
+ */
+
+BOOL Device_DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode,
+                            LPVOID lpvInBuffer, DWORD cbInBuffer,
+                            LPVOID lpvOutBuffer, DWORD cbOutBuffer,
+                            LPDWORD lpcbBytesReturned,
+                            LPOVERLAPPED lpOverlapped)
+{
+    TRACE( "(%p,%lx,%p,%ld,%p,%ld,%p,%p)\n",
+           hDevice,dwIoControlCode,lpvInBuffer,cbInBuffer,
+           lpvOutBuffer,cbOutBuffer,lpcbBytesReturned,lpOverlapped );
+
+    if (dwIoControlCode == IOCTL_STORAGE_GET_DEVICE_NUMBER)
+    {
+        /* 
+         * Bad Codeweavers, hacking this function so that is simple returns some
+         * data instead of truly calling the ioctl functions to retreave the
+         * data. I will fix this soon. 
+         */
+        PSTORAGE_DEVICE_NUMBER dev = (PSTORAGE_DEVICE_NUMBER)lpvOutBuffer;
+       
+        dev->DeviceType = FILE_DEVICE_DISK;
+        dev->DeviceNumber = 1;
+        dev->PartitionNumber = 0; 
+        *lpcbBytesReturned = sizeof(STORAGE_DEVICE_NUMBER);
+    return TRUE;
+    }
+
+    if (dwIoControlCode == IOCTL_STORAGE_QUERY_PROPERTY)
+    {
+        /* 
+         * Bad codeweavers part II,  yes since i am really only intrested in 
+         * iPods right now, this will only work on usb devices. Because USB
+         * storage is based on scsi i can use the scsi ioctl functions to get
+         * the information i need
+         */
+        PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)lpvInBuffer;
+        PSTORAGE_DEVICE_DESCRIPTOR output =
+                                    (PSTORAGE_DEVICE_DESCRIPTOR)lpvOutBuffer;
+        int rc;
+        scsi_idlun  idlun,idlun2;
+        int error_count = 0;
+        int i = 0;
+        int sg_device = 0;
+        int fd;
+
+        struct sg_io_hdr io_hdr;
+        unsigned char sense[SENSE_BUFF_LEN];
+        char responce[RESPONCE_LEN];
+        unsigned char CommandBlock[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0,
+                                                      0, 0};
+
+        TRACE("INPUT: (%x, %x)\n",query->PropertyId, query->QueryType);
+
+        fd = get_device_fd(hDevice, 0);
+    
+        rc = ioctl(fd,SCSI_IOCTL_GET_IDLUN,&idlun);
+
+        release_device_fd(hDevice,fd);
+
+        if (rc != 0)
+        {
+            TRACE("SCSI_IOCTL_GET_IDLUN failed\n");
+            return FALSE; /* not a scsi device */
+        }
+
+        /* find the generic scsi device with the same scsi id */
+
+        while (error_count < 12)
+        {
+            char name[60];
+
+            sprintf(name,"/dev/sg%i",i);
+            TRACE("Trying device %s\n",name);
+          
+            sg_device = open(name,O_RDONLY|O_NONBLOCK);
+            if (sg_device>0)
+            {
+                memset(&idlun2,0,sizeof(idlun2));
+                ioctl(sg_device,SCSI_IOCTL_GET_IDLUN,&idlun2);
+                if ((idlun2.four_in_one == idlun.four_in_one) &&
+                   (idlun2.host_unique_id == idlun.host_unique_id))
+                    break;
+                else
+                    close(sg_device);
+            }
+            else
+                error_count ++;
+            i++;
+        }
+
+        if (!sg_device)
+        {
+            TRACE("No matching openable scsi generic device found\n");
+            return FALSE;
+        }
+
+        /* Prepare for the inquery call*/
+
+        CommandBlock[4] = (unsigned char)RESPONCE_LEN;
+        memset(responce,0,sizeof(responce));
+        memset(&io_hdr,0,sizeof(io_hdr));
+
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (CommandBlock);
+        io_hdr.mx_sb_len = sizeof(sense);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = sizeof(responce);
+        io_hdr.dxferp = responce;
+        io_hdr.cmdp = CommandBlock;
+        io_hdr.sbp = sense;
+        io_hdr.timeout = DEF_TIMEOUT;
+       
+        rc = ioctl(sg_device,SG_IO,&io_hdr);
+
+        close(sg_device);
+        if (rc)
+        {
+            ERR("ioctl SG_IO failed\n");
+            return FALSE;
+        }
+
+        memset(output,0,sizeof(output)+RESPONCE_LEN);
+        output->Version =sizeof(output);
+        output->Size = sizeof(output) + sizeof(responce);
+        output->BusType = BusTypeUsb;
+        output->RawPropertiesLength = sizeof(responce);
+        memcpy(&(output->RawDeviceProperties),responce,sizeof(responce));
+        *lpcbBytesReturned = sizeof(STORAGE_DEVICE_DESCRIPTOR)+sizeof(responce);
+
+        return TRUE;
+    }
+
+    return FALSE;
+}


More information about the wine-patches mailing list