Added imports of kernel32.dll where necessary.
[wine] / dlls / winaspi / aspi.c
1 /**************************************************************************
2 ASPI routines
3 (C) 2000 David Elliott <dfe@netnitco.net>
4 Licensed under the WINE (X11) license
5 */
6
7 /* These routines are to be called from either WNASPI32 or WINASPI */
8
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.
17  */
18
19 /*
20 Registry format is currently:
21 HKEY_DYN_DATA
22         WineScsi
23                 (default)=number of host adapters
24                 hHHcCCtTTdDD=linux device name
25 */
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 <unistd.h>
32 #include <errno.h>
33
34 #include "debugtools.h"
35 #include "winreg.h"
36 #include "winerror.h"
37 #include "winescsi.h"
38 #include "file.h"
39
40 DEFAULT_DEBUG_CHANNEL(aspi);
41
42 /* Internal function prototypes */
43 static void
44 SCSI_GetProcinfo();
45
46 #ifdef linux
47 static void
48 SCSI_MapHCtoController();
49 #endif
50
51 /* Exported functions */
52 void
53 SCSI_Init()
54 {
55         /* For now we just call SCSI_GetProcinfo */
56         SCSI_GetProcinfo();
57 #ifdef linux
58         SCSI_MapHCtoController();
59 #endif
60 }
61
62 int
63 ASPI_GetNumControllers()
64 {
65         HKEY hkeyScsi;
66         HKEY hkeyControllerMap;
67         DWORD error;
68         DWORD type = REG_DWORD;
69         DWORD num_ha = 0;
70         DWORD cbData = sizeof(num_ha);
71
72         if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
73         {
74                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
75                 return 0;
76         }
77
78         if( (error=RegOpenKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, KEY_ALL_ACCESS, &hkeyControllerMap )) != ERROR_SUCCESS )
79         {
80                 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
81                 RegCloseKey(hkeyScsi);
82                 SetLastError(error);
83                 return 0;
84         }
85         if( RegQueryValueExA(hkeyControllerMap, NULL, NULL, &type, (LPBYTE)&num_ha, &cbData ) != ERROR_SUCCESS )
86         {
87                 ERR("Could not query value HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
88                 num_ha=0;
89         }
90         RegCloseKey(hkeyControllerMap);
91         RegCloseKey(hkeyScsi);
92         TRACE("Returning %ld host adapters\n", num_ha );
93         return num_ha;
94 }
95
96 BOOL
97 SCSI_GetDeviceName( int h, int c, int t, int d, LPSTR devstr, LPDWORD lpcbData )
98 {
99
100         char idstr[20];
101         HKEY hkeyScsi;
102         DWORD type;
103
104         if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
105         {
106                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
107                 return FALSE;
108         }
109
110
111         sprintf(idstr, "h%02dc%02dt%02dd%02d", h, c, t, d);
112
113         if( RegQueryValueExA(hkeyScsi, idstr, NULL, &type, devstr, lpcbData) != ERROR_SUCCESS )
114         {
115                 WARN("Could not query value HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
116                 RegCloseKey(hkeyScsi);
117                 return FALSE;
118         }
119         RegCloseKey(hkeyScsi);
120
121         TRACE("scsi %s: Device name: %s\n",idstr,devstr);
122         return TRUE;
123 }
124
125 /* SCSI_GetHCforController
126  * RETURNS
127  *      HIWORD: Host Adapter
128  *      LOWORD: Channel
129  */
130 DWORD
131 ASPI_GetHCforController( int controller )
132 {
133         DWORD hc = 0xFFFFFFFF;
134         char cstr[20];
135         DWORD error;
136         HKEY hkeyScsi;
137         HKEY hkeyControllerMap;
138         DWORD type = REG_DWORD;
139         DWORD cbData = sizeof(DWORD);
140         DWORD disposition;
141 #if 0
142         FIXME("Please fix to map each channel of each host adapter to the proper ASPI controller number!\n");
143         hc = (controller << 16);
144         return hc;
145 #endif
146         if( (error=RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition )) != ERROR_SUCCESS )
147         {
148                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
149                 SetLastError(error);
150                 return hc;
151         }
152         if( disposition != REG_OPENED_EXISTING_KEY )
153         {
154                 WARN("Created HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
155         }
156         if( (error=RegCreateKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyControllerMap, &disposition )) != ERROR_SUCCESS )
157         {
158                 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
159                 RegCloseKey(hkeyScsi);
160                 SetLastError(error);
161                 return hc;
162         }
163         if( disposition != REG_OPENED_EXISTING_KEY )
164         {
165                 WARN("Created HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
166         }
167
168         sprintf(cstr, "c%02d", controller);
169         if( (error=RegQueryValueExA( hkeyControllerMap, cstr, 0, &type, (LPBYTE)&hc, &cbData)) != ERROR_SUCCESS )
170         {
171                 ERR("Could not open HKEY_DYN_DATA\\%s\\%s\\%s, error=%lx\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP, cstr, error );
172                 SetLastError( error );
173         }
174         RegCloseKey(hkeyControllerMap);
175         RegCloseKey(hkeyScsi);
176         return hc;
177
178 };
179
180 int
181 SCSI_OpenDevice( int h, int c, int t, int d )
182 {
183         char devstr[20];
184         DWORD cbData = 20;
185         int fd = -1;
186
187         if(!SCSI_GetDeviceName( h, c, t, d, devstr, &cbData ))
188         {
189                 WARN("Could not get device name for h%02dc%02dt%02dd%02d\n", h, c, t, d);
190                 return -1;
191         }
192
193         TRACE("Opening device %s mode O_RDWR\n",devstr);
194         fd = open(devstr, O_RDWR);
195
196         if( fd < 0 )
197         {
198                 TRACE("open failed\n");
199                 FILE_SetDosError(); /* SetLastError() to errno */
200                 TRACE("GetLastError: %ld\n", GetLastError());
201         }
202         return fd;
203 }
204
205 #ifdef linux
206 /* SCSI_Fix_CMD_LEN
207  *      Checks to make sure the CMD_LEN is correct
208  */
209 void
210 SCSI_Fix_CMD_LEN(int fd, int cmd, int len)
211 {
212         int index=(cmd>>5)&7;
213
214         if (len!=scsi_command_size[index])
215         {
216                 TRACE("CDBLen for command %d claims to be %d, expected %d\n",
217                                 cmd, len, scsi_command_size[index]);
218                 ioctl(fd,SG_NEXT_CMD_LEN,&len);
219         }
220 }
221
222 int
223 SCSI_LinuxSetTimeout( int fd, int timeout )
224 {
225         int retval;
226         TRACE("Setting timeout to %d jiffies\n", timeout);
227         retval=ioctl(fd,SG_SET_TIMEOUT,&timeout);
228         if(retval)
229         {
230                 WARN("Could not set timeout errno=%d!\n",errno);
231         }
232         return retval;
233         
234 }
235
236 /* This function takes care of the write/read to the linux sg device.
237  * It returns TRUE or FALSE and uses FILE_SetDosError() to convert
238  * UNIX errno to Windows GetLastError().  The reason for that is that
239  * several programs will check that error and we might as well set
240  * it here.  We also return the value of the read call in
241  * lpcbBytesReturned.
242  */
243 BOOL /* NOTE: This function SHOULD BLOCK */
244 SCSI_LinuxDeviceIo( int fd,
245                 struct sg_header * lpInBuffer, DWORD cbInBuffer,
246                 struct sg_header * lpOutBuffer, DWORD cbOutBuffer,
247                 LPDWORD lpcbBytesReturned )
248 {
249         DWORD dwBytes;
250         DWORD save_error;
251
252         TRACE("Writing to Liunx sg device\n");
253         dwBytes = write( fd, lpInBuffer, cbInBuffer );
254         if( dwBytes != cbInBuffer )
255         {
256                 FILE_SetDosError();
257                 save_error = GetLastError();
258                 WARN("Not enough bytes written to scsi device. bytes=%ld .. %ld\n", cbInBuffer, dwBytes );
259                 if( save_error == ERROR_NOT_ENOUGH_MEMORY )
260                         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");
261                 WARN("error= %ld\n", save_error );
262                 *lpcbBytesReturned = 0;
263                 return FALSE;
264         }
265         
266         TRACE("Reading reply from Linux sg device\n");
267         *lpcbBytesReturned = read( fd, lpOutBuffer, cbOutBuffer );
268         if( *lpcbBytesReturned != cbOutBuffer )
269         {
270                 FILE_SetDosError();
271                 save_error = GetLastError();
272                 WARN("Not enough bytes read from scsi device. bytes=%ld .. %ld\n", cbOutBuffer, *lpcbBytesReturned);
273                 WARN("error= %ld\n", save_error );
274                 return FALSE;
275         }
276         return TRUE;
277 }
278
279 /* Internal functions */
280 struct LinuxProcScsiDevice
281 {
282         int host;
283         int channel;
284         int target;
285         int lun;
286         char vendor[9];
287         char model[17];
288         char rev[5];
289         char type[33];
290         int ansirev;
291 };
292
293 static int
294 SCSI_getprocentry( FILE * procfile, struct LinuxProcScsiDevice * dev )
295 {
296         int result;
297         result = fscanf( procfile,
298                 "Host: scsi%d Channel: %d Id: %d Lun: %d\n",
299                 &dev->host,
300                 &dev->channel,
301                 &dev->target,
302                 &dev->lun );
303         if( result == EOF )
304                 return EOF;
305         if( result != 4 )
306                 return 0;
307         result = fscanf( procfile,
308                 "  Vendor: %8c Model: %16c Rev: %4c\n",
309                 dev->vendor,
310                 dev->model,
311                 dev->rev );
312         if( result != 3 )
313                 return 0;
314
315         result = fscanf( procfile,
316                 "  Type:   %32c ANSI SCSI revision: %d\n",
317                 dev->type,
318                 &dev->ansirev );
319         if( result != 2 )
320                 return 0;
321         /* Since we fscanf with %XXc instead of %s.. put a NULL at end */
322         dev->vendor[8] = 0;
323         dev->model[16] = 0;
324         dev->rev[4] = 0;
325         dev->type[32] = 0;
326
327         return 1;
328 }
329
330 static void
331 SCSI_printprocentry( const struct LinuxProcScsiDevice * dev )
332 {
333         TRACE( "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
334                 dev->host,
335                 dev->channel,
336                 dev->target,
337                 dev->lun );
338         TRACE( "  Vendor: %s Model: %s Rev: %s\n",
339                 dev->vendor,
340                 dev->model,
341                 dev->rev );
342         TRACE( "  Type:   %s ANSI SCSI revision: %02d\n",
343                 dev->type,
344                 dev->ansirev );
345 }
346
347 static BOOL
348 SCSI_PutRegControllerMap( HKEY hkeyControllerMap, int num_controller, int ha, int chan)
349 {
350         DWORD error;
351         char cstr[20];
352         DWORD hc;
353         hc = (ha << 16) + chan;
354         sprintf(cstr, "c%02d", num_controller);
355         if( (error=RegSetValueExA( hkeyControllerMap, cstr, 0, REG_DWORD, (LPBYTE)&hc, sizeof(DWORD))) != ERROR_SUCCESS )
356         {
357                 ERR("Could not create HKEY_DYN_DATA\\%s\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP, cstr );
358         }
359         return error;
360 }
361
362 static void
363 SCSI_MapHCtoController()
364 {
365         HKEY hkeyScsi;
366         HKEY hkeyControllerMap;
367         DWORD disposition;
368
369         char idstr[20];
370         DWORD cbIdStr = 20;
371         int i = 0;
372         DWORD type = 0;
373         DWORD error;
374
375         DWORD num_controller = 0;
376         int last_ha = -1;
377         int last_chan = -1;
378
379         int ha = 0;
380         int chan = 0;
381
382         if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
383         {
384                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
385                 return;
386         }
387         if( disposition != REG_OPENED_EXISTING_KEY )
388         {
389                 WARN("Created HKEY_DYN_DATA\\%s\n",KEYNAME_SCSI);
390         }
391         if( RegCreateKeyExA(hkeyScsi, KEYNAME_SCSI_CONTROLLERMAP, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyControllerMap, &disposition ) != ERROR_SUCCESS )
392         {
393                 ERR("Could not create HKEY_DYN_DATA\\%s\\%s\n", KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
394                 RegCloseKey(hkeyScsi);
395                 return;
396         }
397         
398         for( i=0; (error=RegEnumValueA( hkeyScsi, i, idstr, &cbIdStr, NULL, &type, NULL, NULL )) == ERROR_SUCCESS; i++ )
399         {
400                 sscanf(idstr, "h%02dc%02dt%*02dd%*02d", &ha, &chan);
401                 if( last_ha < ha )
402                 {       /* Next HA */
403                         last_ha = ha;
404                         last_chan = chan;
405                         SCSI_PutRegControllerMap( hkeyControllerMap, num_controller, ha, chan);
406                         num_controller++;
407                 }
408                 else if( last_ha > ha )
409                 {
410                         FIXME("Expected registry to be sorted\n");
411                 }
412                 /* last_ha == ha */
413                 else if( last_chan < chan )
414                 {
415                         last_chan = chan;
416                         SCSI_PutRegControllerMap( hkeyControllerMap, num_controller, ha, chan);
417                         num_controller++;
418                 }
419                 else if( last_chan > chan )
420                 {
421                         FIXME("Expected registry to be sorted\n");
422                 }
423                 /* else last_ha == ha && last_chan == chan so do nothing */
424         }
425         /* Set (default) value to number of controllers */
426         if( RegSetValueExA(hkeyControllerMap, NULL, 0, REG_DWORD, (LPBYTE)&num_controller, sizeof(DWORD) ) != ERROR_SUCCESS )
427         {
428                 ERR("Could not set value HEKY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, KEYNAME_SCSI_CONTROLLERMAP);
429         }
430         RegCloseKey(hkeyControllerMap);
431         RegCloseKey(hkeyScsi);
432         return;
433 }
434 #endif
435
436 static void
437 SCSI_GetProcinfo()
438 /* I'll admit, this function is somewhat of a mess... it was originally
439  * designed to make some sort of linked list then I realized that
440  * HKEY_DYN_DATA would be a lot less messy
441  */
442 {
443 #ifdef linux
444         FILE * procfile = NULL;
445
446         int result = 0;
447
448         struct LinuxProcScsiDevice dev;
449
450         char idstr[20];
451         char devstr[20];
452
453         int devnum=0;
454         int num_ha = 0;
455
456         HKEY hkeyScsi;
457         DWORD disposition;
458
459         procfile = fopen( "/proc/scsi/scsi", "r" );
460         if( !procfile )
461         {
462                 ERR("Could not open /proc/scsi/scsi\n");
463                 return;
464         }
465
466         result = fscanf( procfile, "Attached devices: \n");
467         if( result != 0 )
468         {
469                 ERR("Incorrect /proc/scsi/scsi format");
470                 return;
471         }
472
473         if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
474         {
475                 ERR("Could not create HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
476                 return;
477         }
478
479         /* Read info for one device */
480         while( (result = SCSI_getprocentry(procfile, &dev)) > 0 )
481         {
482                 /* Add to registry */
483
484                 sprintf(idstr, "h%02dc%02dt%02dd%02d", dev.host, dev.channel, dev.target, dev.lun);
485                 sprintf(devstr, "/dev/sg%c", 'a'+devnum);
486                 if( RegSetValueExA(hkeyScsi, idstr, 0, REG_SZ, devstr, strlen(devstr)+1 ) != ERROR_SUCCESS )
487                 {
488                         ERR("Could not set value HEKY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
489                 }
490
491                 /* Debug output */
492                 SCSI_printprocentry( &dev );
493
494                 /* FIXME: We *REALLY* need number of controllers.. not ha */
495                 /* num of hostadapters is highest ha + 1 */
496                 if( dev.host >= num_ha )
497                         num_ha = dev.host+1;
498                 devnum++;
499         } /* while(1) */
500         if( result != EOF )
501         {
502                 ERR("Incorrect /proc/scsi/scsi format");
503         }
504         fclose( procfile );
505         if( RegSetValueExA(hkeyScsi, NULL, 0, REG_DWORD, (LPBYTE)&num_ha, sizeof(num_ha) ) != ERROR_SUCCESS )
506         {
507                 ERR("Could not set value HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
508         }
509         RegCloseKey(hkeyScsi);
510         return;
511 #endif
512 }