Fixed opening of some drivers.
[wine.git] / dlls / winaspi / aspi.c
blob29b930d11e96a45a2c184fd4fd1de2bbf22c9ac2
1 /**************************************************************************
2 ASPI routines
3 (C) 2000 David Elliott <dfe@infinite-internet.net>
4 Licensed under the WINE (X11) license
5 */
7 /* These routines are to be called from either WNASPI32 or WINASPI */
9 /* FIXME:
10 * - Registry format is stupid for now.. fix that later
11 * - No way to override automatic /proc detection, maybe provide an
12 * HKEY_LOCAL_MACHINE\Software\Wine\Wine\Scsi regkey
13 * - Somewhat debating an #ifdef linux... technically all this code will
14 * run on another UNIX.. it will fail nicely.
15 * - Please add support for mapping multiple channels on host adapters to
16 * aspi controllers, e-mail me if you need help.
20 Registry format is currently:
21 HKEY_DYN_DATA
22 WineScsi
23 (default)=number of host adapters
24 hHHcCCtTTdDD=linux device name
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/ioctl.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
36 #include "debugtools.h"
37 #include "winreg.h"
38 #include "winerror.h"
39 #include "winescsi.h"
41 DEFAULT_DEBUG_CHANNEL(aspi);
43 /* Internal function prototypes */
44 static void
45 SCSI_GetProcinfo();
47 #ifdef linux
48 static void
49 SCSI_MapHCtoController();
50 #endif
52 static void set_last_error(void)
54 int save_errno = errno; /* errno gets overwritten by printf */
55 switch (errno)
57 case EAGAIN:
58 SetLastError( ERROR_SHARING_VIOLATION );
59 break;
60 case EBADF:
61 SetLastError( ERROR_INVALID_HANDLE );
62 break;
63 case ENOSPC:
64 SetLastError( ERROR_HANDLE_DISK_FULL );
65 break;
66 case EACCES:
67 case EPERM:
68 case EROFS:
69 SetLastError( ERROR_ACCESS_DENIED );
70 break;
71 case EBUSY:
72 SetLastError( ERROR_LOCK_VIOLATION );
73 break;
74 case ENOENT:
75 SetLastError( ERROR_FILE_NOT_FOUND );
76 break;
77 case EISDIR:
78 SetLastError( ERROR_CANNOT_MAKE );
79 break;
80 case ENFILE:
81 case EMFILE:
82 SetLastError( ERROR_NO_MORE_FILES );
83 break;
84 case EEXIST:
85 SetLastError( ERROR_FILE_EXISTS );
86 break;
87 case EINVAL:
88 case ESPIPE:
89 SetLastError( ERROR_SEEK );
90 break;
91 case ENOTEMPTY:
92 SetLastError( ERROR_DIR_NOT_EMPTY );
93 break;
94 case ENOEXEC:
95 SetLastError( ERROR_BAD_FORMAT );
96 break;
97 default:
98 WARN( "unknown file error: %s", strerror(save_errno) );
99 SetLastError( ERROR_GEN_FAILURE );
100 break;
102 errno = save_errno;
105 /* Exported functions */
106 void
107 SCSI_Init()
109 /* For now we just call SCSI_GetProcinfo */
110 SCSI_GetProcinfo();
111 #ifdef linux
112 SCSI_MapHCtoController();
113 #endif
117 ASPI_GetNumControllers()
119 HKEY hkeyScsi;
120 HKEY hkeyControllerMap;
121 DWORD error;
122 DWORD type = REG_DWORD;
123 DWORD num_ha = 0;
124 DWORD cbData = sizeof(num_ha);
126 if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
128 ERR("Could not open HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
129 return 0;
132 if( (error=RegOpenKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, KEY_ALL_ACCESS, &hkeyControllerMap )) != ERROR_SUCCESS )
134 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
135 RegCloseKey(hkeyScsi);
136 SetLastError(error);
137 return 0;
139 if( RegQueryValueExA(hkeyControllerMap, NULL, NULL, &type, (LPBYTE)&num_ha, &cbData ) != ERROR_SUCCESS )
141 ERR("Could not query value HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
142 num_ha=0;
144 RegCloseKey(hkeyControllerMap);
145 RegCloseKey(hkeyScsi);
146 TRACE("Returning %ld host adapters\n", num_ha );
147 return num_ha;
150 BOOL
151 SCSI_GetDeviceName( int h, int c, int t, int d, LPSTR devstr, LPDWORD lpcbData )
154 char idstr[20];
155 HKEY hkeyScsi;
156 DWORD type;
158 if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
160 ERR("Could not open HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
161 return FALSE;
165 sprintf(idstr, "h%02dc%02dt%02dd%02d", h, c, t, d);
167 if( RegQueryValueExA(hkeyScsi, idstr, NULL, &type, devstr, lpcbData) != ERROR_SUCCESS )
169 WARN("Could not query value HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
170 RegCloseKey(hkeyScsi);
171 return FALSE;
173 RegCloseKey(hkeyScsi);
175 TRACE("scsi %s: Device name: %s\n",idstr,devstr);
176 return TRUE;
179 /* SCSI_GetHCforController
180 * RETURNS
181 * HIWORD: Host Adapter
182 * LOWORD: Channel
184 DWORD
185 ASPI_GetHCforController( int controller )
187 DWORD hc = 0xFFFFFFFF;
188 char cstr[20];
189 DWORD error;
190 HKEY hkeyScsi;
191 HKEY hkeyControllerMap;
192 DWORD type = REG_DWORD;
193 DWORD cbData = sizeof(DWORD);
194 DWORD disposition;
195 #if 0
196 FIXME("Please fix to map each channel of each host adapter to the proper ASPI controller number!\n");
197 hc = (controller << 16);
198 return hc;
199 #endif
200 if( (error=RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition )) != ERROR_SUCCESS )
202 ERR("Could not open HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
203 SetLastError(error);
204 return hc;
206 if( disposition != REG_OPENED_EXISTING_KEY )
208 WARN("Created HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
210 if( (error=RegCreateKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyControllerMap, &disposition )) != ERROR_SUCCESS )
212 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
213 RegCloseKey(hkeyScsi);
214 SetLastError(error);
215 return hc;
217 if( disposition != REG_OPENED_EXISTING_KEY )
219 WARN("Created HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
222 sprintf(cstr, "c%02d", controller);
223 if( (error=RegQueryValueExA( hkeyControllerMap, cstr, 0, &type, (LPBYTE)&hc, &cbData)) != ERROR_SUCCESS )
225 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\\%s, error=%lx\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP, cstr, error );
226 SetLastError( error );
228 RegCloseKey(hkeyControllerMap);
229 RegCloseKey(hkeyScsi);
230 return hc;
235 SCSI_OpenDevice( int h, int c, int t, int d )
237 char devstr[20];
238 DWORD cbData = 20;
239 int fd = -1;
240 char dainbread_linux_hack = 1;
242 if(!SCSI_GetDeviceName( h, c, t, d, devstr, &cbData ))
244 WARN("Could not get device name for h%02dc%02dt%02dd%02d\n", h, c, t, d);
245 return -1;
248 linux_hack:
249 TRACE("Opening device %s mode O_RDWR\n",devstr);
250 fd = open(devstr, O_RDWR);
252 if( fd < 0 )
254 int len = strlen(devstr);
255 set_last_error(); /* SetLastError() to errno */
256 TRACE("Open failed (%s)\n", strerror(errno));
258 /* in case of "/dev/sgX", convert from sga to sg0
259 * and the other way around.
260 * FIXME: remove it if the distributions
261 * finally manage to agree on something.
262 * The best would probably be a complete
263 * rewrite of the Linux SCSI layer
264 * to use CAM + devfs :-) */
265 if ( (dainbread_linux_hack) &&
266 (len >= 3) &&
267 (devstr[len-3] == 's') && (devstr[len-2] == 'g') )
269 char *p = &devstr[len-1];
270 *p = (*p >= 'a') ? *p - 'a' + '0' : *p - '0' + 'a';
271 dainbread_linux_hack = 0;
272 TRACE("Retry with \"equivalent\" Linux device '%s'\n", devstr);
273 goto linux_hack;
276 return fd;
279 #ifdef linux
280 /* SCSI_Fix_CMD_LEN
281 * Checks to make sure the CMD_LEN is correct
283 void
284 SCSI_Fix_CMD_LEN(int fd, int cmd, int len)
286 int index=(cmd>>5)&7;
288 if (len!=scsi_command_size[index])
290 TRACE("CDBLen for command %d claims to be %d, expected %d\n",
291 cmd, len, scsi_command_size[index]);
292 ioctl(fd,SG_NEXT_CMD_LEN,&len);
297 SCSI_LinuxSetTimeout( int fd, int timeout )
299 int retval;
300 TRACE("Setting timeout to %d jiffies\n", timeout);
301 retval=ioctl(fd,SG_SET_TIMEOUT,&timeout);
302 if(retval)
304 WARN("Could not set timeout ! (%s)\n", strerror(errno));
306 return retval;
310 /* This function takes care of the write/read to the linux sg device.
311 * It returns TRUE or FALSE and uses set_last_error() to convert
312 * UNIX errno to Windows GetLastError(). The reason for that is that
313 * several programs will check that error and we might as well set
314 * it here. We also return the value of the read call in
315 * lpcbBytesReturned.
317 BOOL /* NOTE: This function SHOULD BLOCK */
318 SCSI_LinuxDeviceIo( int fd,
319 struct sg_header * lpInBuffer, DWORD cbInBuffer,
320 struct sg_header * lpOutBuffer, DWORD cbOutBuffer,
321 LPDWORD lpcbBytesReturned )
323 DWORD dwBytes;
324 DWORD save_error;
326 TRACE("Writing to Linux sg device\n");
327 dwBytes = write( fd, lpInBuffer, cbInBuffer );
328 if( dwBytes != cbInBuffer )
330 set_last_error();
331 save_error = GetLastError();
332 WARN("Not enough bytes written to scsi device. bytes=%ld .. %ld\n", cbInBuffer, dwBytes );
333 /* FIXME: set_last_error() never sets error to ERROR_NOT_ENOUGH_MEMORY... */
334 if( save_error == ERROR_NOT_ENOUGH_MEMORY )
335 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)");
336 WARN("error= %ld\n", save_error );
337 *lpcbBytesReturned = 0;
338 return FALSE;
341 TRACE("Reading reply from Linux sg device\n");
342 *lpcbBytesReturned = read( fd, lpOutBuffer, cbOutBuffer );
343 if( *lpcbBytesReturned != cbOutBuffer )
345 set_last_error();
346 save_error = GetLastError();
347 WARN("Not enough bytes read from scsi device. bytes=%ld .. %ld\n", cbOutBuffer, *lpcbBytesReturned);
348 WARN("error= %ld\n", save_error );
349 return FALSE;
351 return TRUE;
354 /* Internal functions */
355 struct LinuxProcScsiDevice
357 int host;
358 int channel;
359 int target;
360 int lun;
361 char vendor[9];
362 char model[17];
363 char rev[5];
364 char type[33];
365 int ansirev;
369 * we need to declare white spaces explicitly via %*1[ ],
370 * as there are very dainbread CD-ROM devices out there
371 * which have their manufacturer name blanked out via spaces,
372 * which confuses fscanf's parsing (skips all blank spaces)
374 static int
375 SCSI_getprocentry( FILE * procfile, struct LinuxProcScsiDevice * dev )
377 int result;
378 result = fscanf( procfile,
379 "Host:%*1[ ]scsi%d%*1[ ]Channel:%*1[ ]%d%*1[ ]Id:%*1[ ]%d%*1[ ]Lun:%*1[ ]%d\n",
380 &dev->host,
381 &dev->channel,
382 &dev->target,
383 &dev->lun );
384 if( result == EOF )
386 /* "end of entries" return, so no TRACE() here */
387 return EOF;
389 if( result != 4 )
391 ERR("bus id line scan count error\n");
392 return 0;
394 result = fscanf( procfile,
395 " Vendor:%*1[ ]%8c%*1[ ]Model:%*1[ ]%16c%*1[ ]Rev:%*1[ ]%4c\n",
396 dev->vendor,
397 dev->model,
398 dev->rev );
399 if( result != 3 )
401 ERR("model line scan count error\n");
402 return 0;
405 result = fscanf( procfile,
406 " Type:%*3[ ]%32c%*1[ ]ANSI%*1[ ]SCSI%*1[ ]revision:%*1[ ]%d\n",
407 dev->type,
408 &dev->ansirev );
409 if( result != 2 )
411 ERR("SCSI type line scan count error\n");
412 return 0;
414 /* Since we fscanf with %XXc instead of %s.. put a NULL at end */
415 dev->vendor[8] = 0;
416 dev->model[16] = 0;
417 dev->rev[4] = 0;
418 dev->type[32] = 0;
420 return 1;
423 static void
424 SCSI_printprocentry( const struct LinuxProcScsiDevice * dev )
426 TRACE( "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
427 dev->host,
428 dev->channel,
429 dev->target,
430 dev->lun );
431 TRACE( " Vendor: %s Model: %s Rev: %s\n",
432 dev->vendor,
433 dev->model,
434 dev->rev );
435 TRACE( " Type: %s ANSI SCSI revision: %02d\n",
436 dev->type,
437 dev->ansirev );
440 static BOOL
441 SCSI_PutRegControllerMap( HKEY hkeyControllerMap, int num_controller, int ha, int chan)
443 DWORD error;
444 char cstr[20];
445 DWORD hc;
446 hc = (ha << 16) + chan;
447 sprintf(cstr, "c%02d", num_controller);
448 if( (error=RegSetValueExA( hkeyControllerMap, cstr, 0, REG_DWORD, (LPBYTE)&hc, sizeof(DWORD))) != ERROR_SUCCESS )
450 ERR("Could not create HKEY_DYN_DATA\\%s\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP, cstr );
452 return error;
455 static void
456 SCSI_MapHCtoController()
458 HKEY hkeyScsi;
459 HKEY hkeyControllerMap;
460 DWORD disposition;
462 char idstr[20];
463 DWORD cbIdStr;
464 int i = 0;
465 DWORD type = 0;
466 DWORD error;
468 DWORD num_controller = 0;
469 int last_ha = -1;
470 int last_chan = -1;
472 int ha = 0;
473 int chan = 0;
475 if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
477 ERR("Could not open HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
478 return;
480 if( disposition != REG_OPENED_EXISTING_KEY )
482 WARN("Created HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
484 if( RegCreateKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyControllerMap, &disposition ) != ERROR_SUCCESS )
486 ERR("Could not create HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
487 RegCloseKey(hkeyScsi);
488 return;
491 for( i=0; cbIdStr = sizeof(idstr), (error=RegEnumValueA( hkeyScsi, i, idstr, &cbIdStr, NULL, &type, NULL, NULL )) == ERROR_SUCCESS; i++ )
493 if(idstr[0] == '\0') continue; /* skip the default value */
495 if(sscanf(idstr, "h%02dc%02dt%*02dd%*02d", &ha, &chan) != 2) {
496 ERR("incorrect reg. value %s\n", debugstr_a(idstr));
497 continue;
500 if( last_ha < ha )
501 { /* Next HA */
502 last_ha = ha;
503 last_chan = chan;
504 SCSI_PutRegControllerMap( hkeyControllerMap, num_controller, ha, chan);
505 num_controller++;
507 else if( last_ha > ha )
509 FIXME("Expected registry to be sorted\n");
511 /* last_ha == ha */
512 else if( last_chan < chan )
514 last_chan = chan;
515 SCSI_PutRegControllerMap( hkeyControllerMap, num_controller, ha, chan);
516 num_controller++;
518 else if( last_chan > chan )
520 FIXME("Expected registry to be sorted\n");
522 /* else last_ha == ha && last_chan == chan so do nothing */
524 /* Set (default) value to number of controllers */
525 if( RegSetValueExA(hkeyControllerMap, NULL, 0, REG_DWORD, (LPBYTE)&num_controller, sizeof(DWORD) ) != ERROR_SUCCESS )
527 ERR("Could not set value HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
529 RegCloseKey(hkeyControllerMap);
530 RegCloseKey(hkeyScsi);
531 return;
533 #endif
535 int SCSI_Linux_CheckDevices(void)
537 DIR *devdir;
538 struct dirent *dent = NULL;
540 devdir = opendir("/dev");
541 for (dent=readdir(devdir);dent;dent=readdir(devdir))
543 if (!(strncmp(dent->d_name, "sg", 2)))
544 break;
546 closedir(devdir);
548 if (dent == NULL)
550 MESSAGE("WARNING: You don't have any /dev/sgX generic scsi devices ! \"man MAKEDEV\" !\n");
551 return 0;
553 return 1;
556 static void
557 SCSI_GetProcinfo()
558 /* I'll admit, this function is somewhat of a mess... it was originally
559 * designed to make some sort of linked list then I realized that
560 * HKEY_DYN_DATA would be a lot less messy
563 #ifdef linux
564 static const char procname[] = "/proc/scsi/scsi";
565 FILE * procfile = NULL;
567 char read_line[40], read1[10] = "\0", read2[10] = "\0";
568 int result = 0;
570 struct LinuxProcScsiDevice dev;
572 char idstr[20];
573 char devstr[20];
575 int devnum=0;
576 int num_ha = 0;
578 HKEY hkeyScsi;
579 DWORD disposition;
581 /* Check whether user has generic scsi devices at all */
582 if (!(SCSI_Linux_CheckDevices()))
583 return;
585 procfile = fopen( procname, "r" );
586 if( !procfile )
588 ERR("Could not open %s\n", procname);
589 return;
592 fgets(read_line, 40, procfile);
593 sscanf( read_line, "Attached %9s %9s", read1, read2);
595 if(strcmp(read1, "devices:"))
597 ERR("Incorrect %s format\n", procname);
598 return;
601 if(!(strcmp(read2, "none")))
603 ERR("No devices found in %s. Make sure you loaded your SCSI driver or set up ide-scsi emulation for your IDE device if this app needs it !\n", procname);
604 return;
607 if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
609 ERR("Could not create HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
610 return;
613 /* Read info for one device */
614 while( (result = SCSI_getprocentry(procfile, &dev)) > 0 )
616 /* Add to registry */
618 sprintf(idstr, "h%02dc%02dt%02dd%02d", dev.host, dev.channel, dev.target, dev.lun);
619 sprintf(devstr, "/dev/sg%c", 'a'+devnum);
620 if( RegSetValueExA(hkeyScsi, idstr, 0, REG_SZ, devstr, strlen(devstr)+1 ) != ERROR_SUCCESS )
622 ERR("Could not set value HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
625 /* Debug output */
626 SCSI_printprocentry( &dev );
628 /* FIXME: We *REALLY* need number of controllers.. not ha */
629 /* num of hostadapters is highest ha + 1 */
630 if( dev.host >= num_ha )
631 num_ha = dev.host+1;
632 devnum++;
633 } /* while(1) */
634 if( result != EOF )
636 ERR("Sorry, incorrect %s format\n", procname);
638 fclose( procfile );
639 if( RegSetValueExA(hkeyScsi, NULL, 0, REG_DWORD, (LPBYTE)&num_ha, sizeof(num_ha) ) != ERROR_SUCCESS )
641 ERR("Could not set value HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
643 RegCloseKey(hkeyScsi);
644 return;
645 #endif