Define __fastcall.
[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 /* Exported functions */
47 void
48 SCSI_Init()
49 {
50         /* For now we just call SCSI_GetProcinfo */
51         SCSI_GetProcinfo();
52 }
53
54 int
55 ASPI_GetNumControllers()
56 {
57         HKEY hkeyScsi;
58         DWORD type = REG_DWORD;
59         DWORD num_ha = 0;
60         DWORD cbData = sizeof(num_ha);
61
62         if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
63         {
64                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
65                 return 0;
66         }
67
68         if( RegQueryValueExA(hkeyScsi, NULL, NULL, &type, (LPBYTE)&num_ha, &cbData ) != ERROR_SUCCESS )
69         {
70                 ERR("Could not query value HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
71                 num_ha=0;
72         }
73         RegCloseKey(hkeyScsi);
74         FIXME("Please fix to return number of controllers\n");
75         TRACE("Returning %ld host adapters\n", num_ha );
76         return num_ha;
77 }
78
79 BOOL
80 SCSI_GetDeviceName( int h, int c, int t, int d, LPSTR devstr, LPDWORD lpcbData )
81 {
82
83         char idstr[20];
84         HKEY hkeyScsi;
85         DWORD type;
86
87         if( RegOpenKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, KEY_ALL_ACCESS, &hkeyScsi ) != ERROR_SUCCESS )
88         {
89                 ERR("Could not open HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
90                 return FALSE;
91         }
92
93
94         sprintf(idstr, "h%02dc%02dt%02dd%02d", h, c, t, d);
95
96         if( RegQueryValueExA(hkeyScsi, idstr, NULL, &type, devstr, lpcbData) != ERROR_SUCCESS )
97         {
98                 WARN("Could not query value HKEY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
99                 RegCloseKey(hkeyScsi);
100                 return FALSE;
101         }
102         RegCloseKey(hkeyScsi);
103
104         TRACE("scsi %s: Device name: %s\n",idstr,devstr);
105         return TRUE;
106 }
107
108 /* SCSI_GetHCforController
109  * RETURNS
110  *      HIWORD: Host Adapter
111  *      LOWORD: Channel
112  */
113 DWORD
114 ASPI_GetHCforController( int controller )
115 {
116         DWORD retval;
117         FIXME("Please fix to map each channel of each host adapter to the proper ASPI controller number!\n");
118         retval = (controller << 16);
119         return retval;
120 };
121
122 int
123 SCSI_OpenDevice( int h, int c, int t, int d )
124 {
125         char devstr[20];
126         DWORD cbData = 20;
127         int fd = -1;
128
129         if(!SCSI_GetDeviceName( h, c, t, d, devstr, &cbData ))
130         {
131                 WARN("Could not get device name for h%02dc%02dt%02dd%02d\n", h, c, t, d);
132                 return -1;
133         }
134
135         TRACE("Opening device %s mode O_RDWR\n",devstr);
136         fd = open(devstr, O_RDWR);
137
138         if( fd < 0 )
139         {
140                 TRACE("open failed\n");
141                 FILE_SetDosError(); /* SetLastError() to errno */
142                 TRACE("GetLastError: %ld\n", GetLastError());
143         }
144         return fd;
145 }
146
147 #ifdef linux
148 int
149 SCSI_LinuxSetTimeout( int fd, int timeout )
150 {
151         int retval;
152         TRACE("Setting timeout to %d jiffies\n", timeout);
153         retval=ioctl(fd,SG_SET_TIMEOUT,&timeout);
154         if(retval)
155         {
156                 WARN("Could not set timeout errno=%d!\n",errno);
157         }
158         return retval;
159         
160 }
161
162 /* This function takes care of the write/read to the linux sg device.
163  * It returns TRUE or FALSE and uses FILE_SetDosError() to convert
164  * UNIX errno to Windows GetLastError().  The reason for that is that
165  * several programs will check that error and we might as well set
166  * it here.  We also return the value of the read call in
167  * lpcbBytesReturned.
168  */
169 BOOL /* NOTE: This function SHOULD BLOCK */
170 SCSI_LinuxDeviceIo( int fd,
171                 struct sg_header * lpInBuffer, DWORD cbInBuffer,
172                 struct sg_header * lpOutBuffer, DWORD cbOutBuffer,
173                 LPDWORD lpcbBytesReturned )
174 {
175         DWORD dwBytes;
176         DWORD save_error;
177
178         TRACE("Writing to Liunx sg device\n");
179         dwBytes = write( fd, lpInBuffer, cbInBuffer );
180         if( dwBytes != cbInBuffer )
181         {
182                 FILE_SetDosError();
183                 save_error = GetLastError();
184                 WARN("Not enough bytes written to scsi device. bytes=%ld .. %ld\n", cbInBuffer, dwBytes );
185                 if( save_error == ERROR_NOT_ENOUGH_MEMORY )
186                         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");
187                 WARN("error= %ld\n", save_error );
188                 *lpcbBytesReturned = 0;
189                 return FALSE;
190         }
191         
192         TRACE("Reading reply from Linux sg device\n");
193         *lpcbBytesReturned = read( fd, lpOutBuffer, cbOutBuffer );
194         if( *lpcbBytesReturned != cbOutBuffer )
195         {
196                 FILE_SetDosError();
197                 save_error = GetLastError();
198                 WARN("Not enough bytes read from scsi device. bytes=%ld .. %ld\n", cbOutBuffer, *lpcbBytesReturned);
199                 WARN("error= %ld\n", save_error );
200                 return FALSE;
201         }
202         return TRUE;
203 }
204
205 /* Internal functions */
206 struct LinuxProcScsiDevice
207 {
208         int host;
209         int channel;
210         int target;
211         int lun;
212         char vendor[9];
213         char model[17];
214         char rev[5];
215         char type[33];
216         int ansirev;
217 };
218
219 static int
220 SCSI_getprocentry( FILE * procfile, struct LinuxProcScsiDevice * dev )
221 {
222         int result;
223         result = fscanf( procfile,
224                 "Host: scsi%d Channel: %d Id: %d Lun: %d\n",
225                 &dev->host,
226                 &dev->channel,
227                 &dev->target,
228                 &dev->lun );
229         if( result == EOF )
230                 return EOF;
231         if( result != 4 )
232                 return 0;
233         result = fscanf( procfile,
234                 "  Vendor: %8c Model: %16c Rev: %4c\n",
235                 dev->vendor,
236                 dev->model,
237                 dev->rev );
238         if( result != 3 )
239                 return 0;
240
241         result = fscanf( procfile,
242                 "  Type:   %32c ANSI SCSI revision: %d\n",
243                 dev->type,
244                 &dev->ansirev );
245         if( result != 2 )
246                 return 0;
247         /* Since we fscanf with %XXc instead of %s.. put a NULL at end */
248         dev->vendor[8] = 0;
249         dev->model[16] = 0;
250         dev->rev[4] = 0;
251         dev->type[32] = 0;
252
253         return 1;
254 }
255
256 static void
257 SCSI_printprocentry( const struct LinuxProcScsiDevice * dev )
258 {
259         TRACE( "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
260                 dev->host,
261                 dev->channel,
262                 dev->target,
263                 dev->lun );
264         TRACE( "  Vendor: %s Model: %s Rev: %s\n",
265                 dev->vendor,
266                 dev->model,
267                 dev->rev );
268         TRACE( "  Type:   %s ANSI SCSI revision: %02d\n",
269                 dev->type,
270                 dev->ansirev );
271 }
272 #endif
273
274 static void
275 SCSI_GetProcinfo()
276 /* I'll admit, this function is somewhat of a mess... it was originally
277  * designed to make some sort of linked list then I realized that
278  * HKEY_DYN_DATA would be a lot less messy
279  */
280 {
281 #ifdef linux
282         FILE * procfile = NULL;
283
284         int result = 0;
285
286         struct LinuxProcScsiDevice dev;
287
288         char idstr[20];
289         char devstr[20];
290
291         int devnum=0;
292         int num_ha = 0;
293
294         HKEY hkeyScsi;
295         DWORD disposition;
296
297         procfile = fopen( "/proc/scsi/scsi", "r" );
298         if( !procfile )
299         {
300                 ERR("Could not open /proc/scsi/scsi\n");
301                 return;
302         }
303
304         result = fscanf( procfile, "Attached devices: \n");
305         if( result != 0 )
306         {
307                 ERR("Incorrect /proc/scsi/scsi format");
308                 return;
309         }
310
311         if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
312         {
313                 ERR("Could not create HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
314                 return;
315         }
316
317         /* Read info for one device */
318         while( (result = SCSI_getprocentry(procfile, &dev)) > 0 )
319         {
320                 /* Add to registry */
321
322                 sprintf(idstr, "h%02dc%02dt%02dd%02d", dev.host, dev.channel, dev.target, dev.lun);
323                 sprintf(devstr, "/dev/sg%c", 'a'+devnum);
324                 if( RegSetValueExA(hkeyScsi, idstr, 0, REG_SZ, devstr, strlen(devstr)+1 ) != ERROR_SUCCESS )
325                 {
326                         ERR("Could not set value HEKY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
327                 }
328
329                 /* Debug output */
330                 SCSI_printprocentry( &dev );
331
332                 /* FIXME: We *REALLY* need number of controllers.. not ha */
333                 /* num of hostadapters is highest ha + 1 */
334                 if( dev.host >= num_ha )
335                         num_ha = dev.host+1;
336                 devnum++;
337         } /* while(1) */
338         if( result != EOF )
339         {
340                 ERR("Incorrect /proc/scsi/scsi format");
341         }
342         fclose( procfile );
343         if( RegSetValueExA(hkeyScsi, NULL, 0, REG_DWORD, (LPBYTE)&num_ha, sizeof(num_ha) ) != ERROR_SUCCESS )
344         {
345                 ERR("Could not set value HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
346         }
347         RegCloseKey(hkeyScsi);
348         return;
349 #endif
350 }