Cleaned up debug channels a bit.
[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 int
148 SCSI_LinuxSetTimeout( int fd, int timeout )
149 {
150         int retval;
151         TRACE("Setting timeout to %d jiffies\n", timeout);
152         retval=ioctl(fd,SG_SET_TIMEOUT,&timeout);
153         if(retval)
154         {
155                 WARN("Could not set timeout errno=%d!\n",errno);
156         }
157         return retval;
158         
159 }
160
161 /* This function takes care of the write/read to the linux sg device.
162  * It returns TRUE or FALSE and uses FILE_SetDosError() to convert
163  * UNIX errno to Windows GetLastError().  The reason for that is that
164  * several programs will check that error and we might as well set
165  * it here.  We also return the value of the read call in
166  * lpcbBytesReturned.
167  */
168 BOOL /* NOTE: This function SHOULD BLOCK */
169 SCSI_LinuxDeviceIo( int fd,
170                 struct sg_header * lpInBuffer, DWORD cbInBuffer,
171                 struct sg_header * lpOutBuffer, DWORD cbOutBuffer,
172                 LPDWORD lpcbBytesReturned )
173 {
174         DWORD dwBytes;
175         DWORD save_error;
176
177         TRACE("Writing to Liunx sg device\n");
178         dwBytes = write( fd, lpInBuffer, cbInBuffer );
179         if( dwBytes != cbInBuffer )
180         {
181                 FILE_SetDosError();
182                 save_error = GetLastError();
183                 WARN("Not enough bytes written to scsi device. bytes=%ld .. %ld\n", cbInBuffer, dwBytes );
184                 if( save_error == ERROR_NOT_ENOUGH_MEMORY )
185                         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");
186                 WARN("error= %ld\n", save_error );
187                 *lpcbBytesReturned = 0;
188                 return FALSE;
189         }
190         
191         TRACE("Reading reply from Linux sg device\n");
192         *lpcbBytesReturned = read( fd, lpOutBuffer, cbOutBuffer );
193         if( *lpcbBytesReturned != cbOutBuffer )
194         {
195                 FILE_SetDosError();
196                 save_error = GetLastError();
197                 WARN("Not enough bytes read from scsi device. bytes=%ld .. %ld\n", cbOutBuffer, *lpcbBytesReturned);
198                 WARN("error= %ld\n", save_error );
199                 return FALSE;
200         }
201         return TRUE;
202 }
203
204 /* Internal functions */
205 struct LinuxProcScsiDevice
206 {
207         int host;
208         int channel;
209         int target;
210         int lun;
211         char vendor[9];
212         char model[17];
213         char rev[5];
214         char type[33];
215         int ansirev;
216 };
217
218 static int
219 SCSI_getprocentry( FILE * procfile, struct LinuxProcScsiDevice * dev )
220 {
221         int result;
222         result = fscanf( procfile,
223                 "Host: scsi%d Channel: %d Id: %d Lun: %d\n",
224                 &dev->host,
225                 &dev->channel,
226                 &dev->target,
227                 &dev->lun );
228         if( result == EOF )
229                 return EOF;
230         if( result != 4 )
231                 return 0;
232         result = fscanf( procfile,
233                 "  Vendor: %8c Model: %16c Rev: %4c\n",
234                 dev->vendor,
235                 dev->model,
236                 dev->rev );
237         if( result != 3 )
238                 return 0;
239
240         result = fscanf( procfile,
241                 "  Type:   %32c ANSI SCSI revision: %d\n",
242                 dev->type,
243                 &dev->ansirev );
244         if( result != 2 )
245                 return 0;
246         /* Since we fscanf with %XXc instead of %s.. put a NULL at end */
247         dev->vendor[8] = 0;
248         dev->model[16] = 0;
249         dev->rev[4] = 0;
250         dev->type[32] = 0;
251
252         return 1;
253 }
254
255 static void
256 SCSI_printprocentry( const struct LinuxProcScsiDevice * dev )
257 {
258         TRACE( "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
259                 dev->host,
260                 dev->channel,
261                 dev->target,
262                 dev->lun );
263         TRACE( "  Vendor: %s Model: %s Rev: %s\n",
264                 dev->vendor,
265                 dev->model,
266                 dev->rev );
267         TRACE( "  Type:   %s ANSI SCSI revision: %02d\n",
268                 dev->type,
269                 dev->ansirev );
270 }
271
272 static void
273 SCSI_GetProcinfo()
274 /* I'll admit, this function is somewhat of a mess... it was originally
275  * designed to make some sort of linked list then I realized that
276  * HKEY_DYN_DATA would be a lot less messy
277  */
278 {
279         FILE * procfile = NULL;
280
281         int result = 0;
282
283         struct LinuxProcScsiDevice dev;
284
285         char idstr[20];
286         char devstr[20];
287
288         int devnum=0;
289         int num_ha = 0;
290
291         HKEY hkeyScsi;
292         DWORD disposition;
293
294         procfile = fopen( "/proc/scsi/scsi", "r" );
295         if( !procfile )
296         {
297                 ERR("Could not open /proc/scsi/scsi\n");
298                 return;
299         }
300
301         result = fscanf( procfile, "Attached devices: \n");
302         if( result != 0 )
303         {
304                 ERR("Incorrect /proc/scsi/scsi format");
305                 return;
306         }
307
308         if( RegCreateKeyExA(HKEY_DYN_DATA, KEYNAME_SCSI, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyScsi, &disposition ) != ERROR_SUCCESS )
309         {
310                 ERR("Could not create HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
311                 return;
312         }
313
314         /* Read info for one device */
315         while( (result = SCSI_getprocentry(procfile, &dev)) > 0 )
316         {
317                 /* Add to registry */
318
319                 sprintf(idstr, "h%02dc%02dt%02dd%02d", dev.host, dev.channel, dev.target, dev.lun);
320                 sprintf(devstr, "/dev/sg%c", 'a'+devnum);
321                 if( RegSetValueExA(hkeyScsi, idstr, 0, REG_SZ, devstr, strlen(devstr)+1 ) != ERROR_SUCCESS )
322                 {
323                         ERR("Could not set value HEKY_DYN_DATA\\%s\\%s\n",KEYNAME_SCSI, idstr);
324                 }
325
326                 /* Debug output */
327                 SCSI_printprocentry( &dev );
328
329                 /* FIXME: We *REALLY* need number of controllers.. not ha */
330                 /* num of hostadapters is highest ha + 1 */
331                 if( dev.host >= num_ha )
332                         num_ha = dev.host+1;
333                 devnum++;
334         } /* while(1) */
335         if( result != EOF )
336         {
337                 ERR("Incorrect /proc/scsi/scsi format");
338         }
339         fclose( procfile );
340         if( RegSetValueExA(hkeyScsi, NULL, 0, REG_DWORD, (LPBYTE)&num_ha, sizeof(num_ha) ) != ERROR_SUCCESS )
341         {
342                 ERR("Could not set value HEKY_DYN_DATA\\%s\n",KEYNAME_SCSI);
343         }
344         RegCloseKey(hkeyScsi);
345         return;
346 }
347