aspi.c 9.86 KB
Newer Older
1
/**************************************************************************
2 3
 * ASPI routines
 * Copyright (C) 2000 David Elliott <dfe@infinite-internet.net>
4
 * Copyright (C) 2005 Vitaliy Margolen
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 */
20 21 22 23 24 25 26 27 28 29 30 31

/* 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.
 */

Steven Edwards's avatar
Steven Edwards committed
32 33

#include "config.h"
34
#include "wine/port.h"
Steven Edwards's avatar
Steven Edwards committed
35

36
#include <stdio.h>
37
#include <stdarg.h>
38
#include <sys/types.h>
39
#ifdef HAVE_SYS_IOCTL_H
40
#include <sys/ioctl.h>
Steven Edwards's avatar
Steven Edwards committed
41
#endif
42
#include <fcntl.h>
43 44 45
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
46 47 48
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
49
#include <errno.h>
50
#include <string.h>
51

52 53
#include "windef.h"
#include "winbase.h"
54 55 56 57
#include "winreg.h"
#include "winerror.h"
#include "winescsi.h"

58
#include "wine/debug.h"
59
#include "wine/unicode.h"
60

61
WINE_DEFAULT_DEBUG_CHANNEL(aspi);
62

63
#ifdef linux
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
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:
110
        WARN( "unknown file error: %s\n", strerror(save_errno) );
111 112 113 114 115
        SetLastError( ERROR_GEN_FAILURE );
        break;
    }
    errno = save_errno;
}
116
#endif
117

118 119
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};

120
/* Exported functions */
121
int ASPI_GetNumControllers(void)
122
{
123 124
    HKEY hkeyScsi, hkeyPort;
    DWORD i = 0, numPorts, num_ha = 0;
125
    WCHAR wPortName[15];
126

127 128 129 130 131 132
    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;
    }
133
    while (RegEnumKeyW(hkeyScsi, i++, wPortName, sizeof(wPortName)/sizeof(wPortName[0])) == ERROR_SUCCESS)
134 135 136 137 138 139 140 141 142 143 144
    {
        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);
        }
    }
145

146
    RegCloseKey(hkeyScsi);
147
    TRACE("Returning %d host adapters\n", num_ha );
148
    return num_ha;
149 150
}

151
static BOOL SCSI_GetDeviceName( int h, int c, int t, int d, LPSTR devstr, LPDWORD lpcbData )
152
{
153 154 155
    char buffer[200];
    HKEY hkeyScsi;
    DWORD type;
156

157 158 159
    snprintf(buffer, sizeof(buffer), KEYNAME_SCSI, h, c, t, d);
    if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, buffer, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
    {
160
        TRACE("Could not open HKLM\\%s; device does not exist\n", buffer);
161 162
        return FALSE;
    }
163

164 165 166 167 168 169 170
    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);
171

172 173
    TRACE("Device name: %s\n", devstr);
    return TRUE;
174 175 176 177 178 179 180
}

/* SCSI_GetHCforController
 * RETURNS
 * 	HIWORD: Host Adapter
 * 	LOWORD: Channel
 */
181
DWORD ASPI_GetHCforController( int controller )
182
{
183 184 185
    HKEY hkeyScsi, hkeyPort;
    DWORD i = 0, numPorts;
    int num_ha = controller + 1;
186 187
    WCHAR wPortName[15];
    WCHAR wBusName[15];
188

189 190 191 192 193 194
    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;
    }
195
    while (RegEnumKeyW(hkeyScsi, i++, wPortName, sizeof(wPortName)/sizeof(wPortName[0])) == ERROR_SUCCESS)
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    {
        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;
    }

218
    if (RegEnumKeyW(hkeyPort, -num_ha, wBusName, sizeof(wBusName)/sizeof(wBusName[0])) != ERROR_SUCCESS)
219 220 221 222 223 224
    {
        ERR("Failed to enumerate keys\n");
        RegCloseKey(hkeyPort);
        return 0xFFFFFFFF;
    }
    RegCloseKey(hkeyPort);
225

226
    return (atoiW(&wPortName[9]) << 16) + atoiW(&wBusName[9]);
Eric Pouech's avatar
Eric Pouech committed
227
}
228

229
int SCSI_OpenDevice( int h, int c, int t, int d )
230
{
231 232 233
    char devstr[20];
    DWORD cbData = 20;
    int fd = -1;
234

235 236 237 238 239
    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;
    }
240

241 242
    TRACE("Opening device %s mode O_RDWR\n",devstr);
    fd = open(devstr, O_RDWR);
243 244 245 246
    if (fd == -1) {
        char *errstring = strerror(errno);
        ERR("Failed to open device %s: %s\n", devstr, errstring);
    }
247

248
    return fd;
249 250
}

251
#ifdef linux
252 253 254 255 256 257
/* SCSI_Fix_CMD_LEN
 * 	Checks to make sure the CMD_LEN is correct
 */
void
SCSI_Fix_CMD_LEN(int fd, int cmd, int len)
{
258 259 260 261 262 263 264
	/* This is what the linux kernel thinks.... */
	static const unsigned char scsi_command_size[8] =
	{
        	6, 10, 10, 12,
        	12, 12, 10, 10
	};

265 266 267 268 269 270 271 272 273 274
	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);
	}
}

275 276 277 278 279 280 281 282
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)
	{
283
		WARN("Could not set timeout ! (%s)\n", strerror(errno));
284 285
	}
	return retval;
286

287 288 289
}

/* This function takes care of the write/read to the linux sg device.
290
 * It returns TRUE or FALSE and uses set_last_error() to convert
291 292 293 294 295 296 297 298 299 300 301 302 303 304
 * 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;

305
	TRACE("Writing to Linux sg device\n");
306 307 308
	dwBytes = write( fd, lpInBuffer, cbInBuffer );
	if( dwBytes != cbInBuffer )
	{
309
		set_last_error();
310
		save_error = GetLastError();
311
		WARN("Not enough bytes written to scsi device. bytes=%d .. %d\n", cbInBuffer, dwBytes );
312
                /* FIXME: set_last_error() never sets error to ERROR_NOT_ENOUGH_MEMORY... */
313
		if( save_error == ERROR_NOT_ENOUGH_MEMORY )
314
			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");
315
		WARN("error= %d\n", save_error );
316 317 318
		*lpcbBytesReturned = 0;
		return FALSE;
	}
319

320 321 322 323
	TRACE("Reading reply from Linux sg device\n");
	*lpcbBytesReturned = read( fd, lpOutBuffer, cbOutBuffer );
	if( *lpcbBytesReturned != cbOutBuffer )
	{
324
		set_last_error();
325
		save_error = GetLastError();
326 327
		WARN("Not enough bytes read from scsi device. bytes=%d .. %d\n", cbOutBuffer, *lpcbBytesReturned);
		WARN("error= %d\n", save_error );
328 329 330 331 332
		return FALSE;
	}
	return TRUE;
}

333
static void SCSI_Linux_CheckDevices(void)
334 335 336 337 338 339 340 341 342 343 344 345 346 347
{
    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)
    {
348
	TRACE("WARNING: You don't have any /dev/sgX generic scsi devices ! \"man MAKEDEV\" !\n");
349
	return;
350 351
    }
}
352
#endif
353

354
void SCSI_Init(void)
355
{
356
#ifdef linux
357
    SCSI_Linux_CheckDevices();
358
#endif
359
}