2000-03-08 20:41:49 +01:00
/**************************************************************************
2002-03-10 00:29:33 +01:00
* ASPI routines
* Copyright ( C ) 2000 David Elliott < dfe @ infinite - internet . net >
2005-08-10 12:52:22 +02:00
* Copyright ( C ) 2005 Vitaliy Margolen
2002-03-10 00:29:33 +01:00
*
* 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
2006-05-18 14:49:52 +02:00
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA
2002-03-10 00:29:33 +01:00
*/
2000-03-08 20:41:49 +01:00
/* These routines are to be called from either WNASPI32 or WINASPI */
/* FIXME:
* - No way to override automatic / proc detection , maybe provide an
* HKEY_LOCAL_MACHINE \ Software \ Wine \ Wine \ Scsi regkey
* - Somewhat debating an # ifdef linux . . . technically all this code will
* run on another UNIX . . it will fail nicely .
* - Please add support for mapping multiple channels on host adapters to
* aspi controllers , e - mail me if you need help .
*/
2002-05-21 20:33:56 +02:00
# include "config.h"
2005-08-03 23:25:10 +02:00
# include "wine/port.h"
2002-05-21 20:33:56 +02:00
2000-03-08 20:41:49 +01:00
# include <stdio.h>
2003-08-22 07:05:56 +02:00
# include <stdarg.h>
2000-03-08 20:41:49 +01:00
# include <sys/types.h>
2002-06-01 01:06:46 +02:00
# ifdef HAVE_SYS_IOCTL_H
2000-03-08 20:41:49 +01:00
# include <sys/ioctl.h>
2002-05-21 20:33:56 +02:00
# endif
2000-03-08 20:41:49 +01:00
# include <fcntl.h>
2008-10-14 00:37:25 +02:00
# ifdef HAVE_DIRENT_H
# include <dirent.h>
# endif
2002-08-17 02:43:16 +02:00
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
2000-03-08 20:41:49 +01:00
# include <errno.h>
2001-01-22 03:17:29 +01:00
# include <string.h>
2000-03-08 20:41:49 +01:00
2003-08-22 07:05:56 +02:00
# include "windef.h"
# include "winbase.h"
2000-03-08 20:41:49 +01:00
# include "winreg.h"
# include "winerror.h"
# include "winescsi.h"
2003-08-22 07:05:56 +02:00
# include "wine/debug.h"
2005-08-10 12:52:22 +02:00
# include "wine/unicode.h"
2003-08-22 07:05:56 +02:00
2002-03-10 00:29:33 +01:00
WINE_DEFAULT_DEBUG_CHANNEL ( aspi ) ;
2000-03-08 20:41:49 +01:00
2005-08-15 11:43:09 +02:00
# ifdef linux
2000-11-30 02:17:55 +01:00
static void set_last_error ( void )
{
int save_errno = errno ; /* errno gets overwritten by printf */
switch ( errno )
{
case EAGAIN :
SetLastError ( ERROR_SHARING_VIOLATION ) ;
break ;
case EBADF :
SetLastError ( ERROR_INVALID_HANDLE ) ;
break ;
case ENOSPC :
SetLastError ( ERROR_HANDLE_DISK_FULL ) ;
break ;
case EACCES :
case EPERM :
case EROFS :
SetLastError ( ERROR_ACCESS_DENIED ) ;
break ;
case EBUSY :
SetLastError ( ERROR_LOCK_VIOLATION ) ;
break ;
case ENOENT :
SetLastError ( ERROR_FILE_NOT_FOUND ) ;
break ;
case EISDIR :
SetLastError ( ERROR_CANNOT_MAKE ) ;
break ;
case ENFILE :
case EMFILE :
SetLastError ( ERROR_NO_MORE_FILES ) ;
break ;
case EEXIST :
SetLastError ( ERROR_FILE_EXISTS ) ;
break ;
case EINVAL :
case ESPIPE :
SetLastError ( ERROR_SEEK ) ;
break ;
case ENOTEMPTY :
SetLastError ( ERROR_DIR_NOT_EMPTY ) ;
break ;
case ENOEXEC :
SetLastError ( ERROR_BAD_FORMAT ) ;
break ;
default :
2001-05-09 19:31:31 +02:00
WARN ( " unknown file error: %s \n " , strerror ( save_errno ) ) ;
2000-11-30 02:17:55 +01:00
SetLastError ( ERROR_GEN_FAILURE ) ;
break ;
}
errno = save_errno ;
}
2005-08-15 11:43:09 +02:00
# endif
2000-11-30 02:17:55 +01:00
2005-08-10 12:52:22 +02:00
static const WCHAR wDevicemapScsi [ ] = { ' H ' , ' A ' , ' R ' , ' D ' , ' W ' , ' A ' , ' R ' , ' E ' , ' \\ ' , ' D ' , ' E ' , ' V ' , ' I ' , ' C ' , ' E ' , ' M ' , ' A ' , ' P ' , ' \\ ' , ' S ' , ' c ' , ' s ' , ' i ' , 0 } ;
2000-03-08 20:41:49 +01:00
/* Exported functions */
2007-03-10 22:00:47 +01:00
int ASPI_GetNumControllers ( void )
2000-03-08 20:41:49 +01:00
{
2005-08-10 12:52:22 +02:00
HKEY hkeyScsi , hkeyPort ;
DWORD i = 0 , numPorts , num_ha = 0 ;
2008-11-05 14:50:54 +01:00
WCHAR wPortName [ 15 ] ;
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , wDevicemapScsi , 0 ,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS , & hkeyScsi ) ! = ERROR_SUCCESS )
{
ERR ( " Could not open HKLM \\ %s \n " , debugstr_w ( wDevicemapScsi ) ) ;
return 0 ;
}
2008-10-12 19:50:20 +02:00
while ( RegEnumKeyW ( hkeyScsi , i + + , wPortName , sizeof ( wPortName ) / sizeof ( wPortName [ 0 ] ) ) = = ERROR_SUCCESS )
2005-08-10 12:52:22 +02:00
{
if ( RegOpenKeyExW ( hkeyScsi , wPortName , 0 , KEY_QUERY_VALUE , & hkeyPort ) = = ERROR_SUCCESS )
{
if ( RegQueryInfoKeyW ( hkeyPort , NULL , NULL , NULL , & numPorts , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL ) = = ERROR_SUCCESS )
{
num_ha + = numPorts ;
}
RegCloseKey ( hkeyPort ) ;
}
}
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
RegCloseKey ( hkeyScsi ) ;
2006-10-06 23:02:14 +02:00
TRACE ( " Returning %d host adapters \n " , num_ha ) ;
2005-08-10 12:52:22 +02:00
return num_ha ;
2000-03-08 20:41:49 +01:00
}
2005-08-10 12:52:22 +02:00
BOOL SCSI_GetDeviceName ( int h , int c , int t , int d , LPSTR devstr , LPDWORD lpcbData )
2000-03-08 20:41:49 +01:00
{
2005-08-10 12:52:22 +02:00
char buffer [ 200 ] ;
HKEY hkeyScsi ;
DWORD type ;
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
snprintf ( buffer , sizeof ( buffer ) , KEYNAME_SCSI , h , c , t , d ) ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , buffer , 0 , KEY_ALL_ACCESS , & hkeyScsi ) ! = ERROR_SUCCESS )
{
2007-11-01 08:07:50 +01:00
TRACE ( " Could not open HKLM \\ %s; device does not exist \n " , buffer ) ;
2005-08-10 12:52:22 +02:00
return FALSE ;
}
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
if ( RegQueryValueExA ( hkeyScsi , " UnixDeviceName " , NULL , & type , ( LPBYTE ) devstr , lpcbData ) ! = ERROR_SUCCESS )
{
WARN ( " Could not query value HKLM \\ %s \\ UnixDeviceName \n " , buffer ) ;
RegCloseKey ( hkeyScsi ) ;
return FALSE ;
}
RegCloseKey ( hkeyScsi ) ;
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
TRACE ( " Device name: %s \n " , devstr ) ;
return TRUE ;
2000-03-08 20:41:49 +01:00
}
/* SCSI_GetHCforController
* RETURNS
* HIWORD : Host Adapter
* LOWORD : Channel
*/
2005-08-10 12:52:22 +02:00
DWORD ASPI_GetHCforController ( int controller )
2000-03-08 20:41:49 +01:00
{
2005-08-10 12:52:22 +02:00
HKEY hkeyScsi , hkeyPort ;
DWORD i = 0 , numPorts ;
int num_ha = controller + 1 ;
2007-11-01 08:09:10 +01:00
WCHAR wPortName [ 15 ] ;
WCHAR wBusName [ 15 ] ;
2000-07-08 13:44:48 +02:00
2005-08-10 12:52:22 +02:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , wDevicemapScsi , 0 ,
KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS , & hkeyScsi ) ! = ERROR_SUCCESS )
{
ERR ( " Could not open HKLM \\ %s \n " , debugstr_w ( wDevicemapScsi ) ) ;
return 0xFFFFFFFF ;
}
2008-10-12 19:50:20 +02:00
while ( RegEnumKeyW ( hkeyScsi , i + + , wPortName , sizeof ( wPortName ) / sizeof ( wPortName [ 0 ] ) ) = = ERROR_SUCCESS )
2005-08-10 12:52:22 +02:00
{
if ( RegOpenKeyExW ( hkeyScsi , wPortName , 0 , KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS ,
& hkeyPort ) = = ERROR_SUCCESS )
{
if ( RegQueryInfoKeyW ( hkeyPort , NULL , NULL , NULL , & numPorts , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL ) = = ERROR_SUCCESS )
{
num_ha - = numPorts ;
if ( num_ha < = 0 ) break ;
}
else
RegCloseKey ( hkeyPort ) ;
}
}
RegCloseKey ( hkeyScsi ) ;
if ( num_ha > 0 )
{
ERR ( " Invalid controller(%d) \n " , controller ) ;
return 0xFFFFFFFF ;
}
2008-10-12 19:50:20 +02:00
if ( RegEnumKeyW ( hkeyPort , - num_ha , wBusName , sizeof ( wBusName ) / sizeof ( wBusName [ 0 ] ) ) ! = ERROR_SUCCESS )
2005-08-10 12:52:22 +02:00
{
ERR ( " Failed to enumerate keys \n " ) ;
RegCloseKey ( hkeyPort ) ;
return 0xFFFFFFFF ;
}
RegCloseKey ( hkeyPort ) ;
2000-07-08 13:44:48 +02:00
2007-11-01 08:09:10 +01:00
return ( atoiW ( & wPortName [ 9 ] ) < < 16 ) + atoiW ( & wBusName [ 9 ] ) ;
2005-12-12 11:54:19 +01:00
}
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
int SCSI_OpenDevice ( int h , int c , int t , int d )
2000-03-08 20:41:49 +01:00
{
2005-08-10 12:52:22 +02:00
char devstr [ 20 ] ;
DWORD cbData = 20 ;
int fd = - 1 ;
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
if ( ! SCSI_GetDeviceName ( h , c , t , d , devstr , & cbData ) )
{
WARN ( " Could not get device name for h%02dc%02dt%02dd%02d \n " , h , c , t , d ) ;
return - 1 ;
}
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
TRACE ( " Opening device %s mode O_RDWR \n " , devstr ) ;
fd = open ( devstr , O_RDWR ) ;
2007-11-01 08:07:18 +01:00
if ( fd = = - 1 ) {
char * errstring = strerror ( errno ) ;
ERR ( " Failed to open device %s: %s \n " , devstr , errstring ) ;
}
2000-03-08 20:41:49 +01:00
2005-08-10 12:52:22 +02:00
return fd ;
2000-03-08 20:41:49 +01:00
}
2000-03-12 21:19:23 +01:00
# ifdef linux
2000-07-15 17:15:31 +02:00
/* SCSI_Fix_CMD_LEN
* Checks to make sure the CMD_LEN is correct
*/
void
SCSI_Fix_CMD_LEN ( int fd , int cmd , int len )
{
2003-11-09 01:31:08 +01:00
/* This is what the linux kernel thinks.... */
static const unsigned char scsi_command_size [ 8 ] =
{
6 , 10 , 10 , 12 ,
12 , 12 , 10 , 10
} ;
2000-07-15 17:15:31 +02:00
int index = ( cmd > > 5 ) & 7 ;
if ( len ! = scsi_command_size [ index ] )
{
TRACE ( " CDBLen for command %d claims to be %d, expected %d \n " ,
cmd , len , scsi_command_size [ index ] ) ;
ioctl ( fd , SG_NEXT_CMD_LEN , & len ) ;
}
}
2000-03-08 20:41:49 +01:00
int
SCSI_LinuxSetTimeout ( int fd , int timeout )
{
int retval ;
TRACE ( " Setting timeout to %d jiffies \n " , timeout ) ;
retval = ioctl ( fd , SG_SET_TIMEOUT , & timeout ) ;
if ( retval )
{
2000-09-06 21:42:19 +02:00
WARN ( " Could not set timeout ! (%s) \n " , strerror ( errno ) ) ;
2000-03-08 20:41:49 +01:00
}
return retval ;
2002-06-01 01:06:46 +02:00
2000-03-08 20:41:49 +01:00
}
/* This function takes care of the write/read to the linux sg device.
2000-11-30 02:17:55 +01:00
* It returns TRUE or FALSE and uses set_last_error ( ) to convert
2000-03-08 20:41:49 +01:00
* UNIX errno to Windows GetLastError ( ) . The reason for that is that
* several programs will check that error and we might as well set
* it here . We also return the value of the read call in
* lpcbBytesReturned .
*/
BOOL /* NOTE: This function SHOULD BLOCK */
SCSI_LinuxDeviceIo ( int fd ,
struct sg_header * lpInBuffer , DWORD cbInBuffer ,
struct sg_header * lpOutBuffer , DWORD cbOutBuffer ,
LPDWORD lpcbBytesReturned )
{
DWORD dwBytes ;
DWORD save_error ;
2000-09-06 21:42:19 +02:00
TRACE ( " Writing to Linux sg device \n " ) ;
2000-03-08 20:41:49 +01:00
dwBytes = write ( fd , lpInBuffer , cbInBuffer ) ;
if ( dwBytes ! = cbInBuffer )
{
2000-11-30 02:17:55 +01:00
set_last_error ( ) ;
2000-03-08 20:41:49 +01:00
save_error = GetLastError ( ) ;
2006-10-06 23:02:14 +02:00
WARN ( " Not enough bytes written to scsi device. bytes=%d .. %d \n " , cbInBuffer , dwBytes ) ;
2000-11-30 02:17:55 +01:00
/* FIXME: set_last_error() never sets error to ERROR_NOT_ENOUGH_MEMORY... */
2000-03-08 20:41:49 +01:00
if ( save_error = = ERROR_NOT_ENOUGH_MEMORY )
2008-12-28 10:36:55 +01:00
MESSAGE ( " Your Linux kernel was not able to handle the amount of data sent to the scsi device. Try recompiling with a larger SG_BIG_BUFF value (kernel 2.0.x sg.h) \n " ) ;
2006-10-06 23:02:14 +02:00
WARN ( " error= %d \n " , save_error ) ;
2000-03-08 20:41:49 +01:00
* lpcbBytesReturned = 0 ;
return FALSE ;
}
2002-06-01 01:06:46 +02:00
2000-03-08 20:41:49 +01:00
TRACE ( " Reading reply from Linux sg device \n " ) ;
* lpcbBytesReturned = read ( fd , lpOutBuffer , cbOutBuffer ) ;
if ( * lpcbBytesReturned ! = cbOutBuffer )
{
2000-11-30 02:17:55 +01:00
set_last_error ( ) ;
2000-03-08 20:41:49 +01:00
save_error = GetLastError ( ) ;
2006-10-06 23:02:14 +02:00
WARN ( " Not enough bytes read from scsi device. bytes=%d .. %d \n " , cbOutBuffer , * lpcbBytesReturned ) ;
WARN ( " error= %d \n " , save_error ) ;
2000-03-08 20:41:49 +01:00
return FALSE ;
}
return TRUE ;
}
2005-08-10 12:52:22 +02:00
static void SCSI_Linux_CheckDevices ( void )
2000-09-06 21:42:19 +02:00
{
DIR * devdir ;
struct dirent * dent = NULL ;
devdir = opendir ( " /dev " ) ;
for ( dent = readdir ( devdir ) ; dent ; dent = readdir ( devdir ) )
{
if ( ! ( strncmp ( dent - > d_name , " sg " , 2 ) ) )
break ;
}
closedir ( devdir ) ;
if ( dent = = NULL )
{
2002-02-22 22:21:09 +01:00
TRACE ( " WARNING: You don't have any /dev/sgX generic scsi devices ! \" man MAKEDEV \" ! \n " ) ;
2005-08-10 12:52:22 +02:00
return ;
2000-09-06 21:42:19 +02:00
}
}
2005-07-06 21:12:04 +02:00
# endif
2000-09-06 21:42:19 +02:00
2005-08-10 12:52:22 +02:00
void SCSI_Init ( void )
2000-03-08 20:41:49 +01:00
{
2000-03-19 13:42:31 +01:00
# ifdef linux
2005-08-10 12:52:22 +02:00
SCSI_Linux_CheckDevices ( ) ;
2000-03-12 21:19:23 +01:00
# endif
2000-03-19 13:42:31 +01:00
}