Fixes for FreeBSD.
[wine] / dlls / winaspi / winaspi16.c
1 #include <stdlib.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <ldt.h>
7 #include <memory.h>
8 #include <unistd.h>
9 #include <callback.h>
10 #include "windows.h"
11 #include "aspi.h"
12 #include "winaspi.h"
13 #include "options.h"
14 #include "heap.h"
15 #include "debug.h"
16 #include "selectors.h"
17 #include "module.h"
18 #include "miscemu.h"
19
20
21 /* FIXME!
22  * 1) Residual byte length reporting not handled
23  * 2) Make this code re-entrant for multithreading
24  * 3) Only linux supported so far
25  */
26
27 #ifdef linux
28 static int
29 ASPI_OpenDevice16(SRB_ExecSCSICmd16 *prb)
30 {
31     int fd;
32     char        idstr[20];
33     char        device_str[50];
34     ASPI_DEVICE_INFO *curr;
35
36     /* search list of devices to see if we've opened it already.
37      * There is not an explicit open/close in ASPI land, so hopefully
38      * keeping a device open won't be a problem.
39      */
40
41     for (curr = ASPI_open_devices; curr; curr = curr->next) {
42         if (curr->hostId == prb->SRB_HaId &&
43             curr->target == prb->SRB_Target &&
44             curr->lun == prb->SRB_Lun) {
45             return curr->fd;
46         }
47     }
48
49     /* device wasn't cached, go ahead and open it */
50     sprintf(idstr, "scsi c%1dt%1dd%1d", prb->SRB_HaId, prb->SRB_Target, prb->SRB_Lun);
51
52     if (!PROFILE_GetWineIniString(idstr, "Device", "", device_str, sizeof(device_str))) {
53         TRACE(aspi, "Trying to open unlisted scsi device %s\n", idstr);
54         return -1;
55     }
56
57     TRACE(aspi, "Opening device %s=%s\n", idstr, device_str);
58
59     fd = open(device_str, O_RDWR);
60     if (fd == -1) {
61         int save_error = errno;
62         ERR(aspi, "Error opening device %s, errno=%d\n", device_str, save_error);
63         return -1;
64     }
65
66     /* device is now open */
67     curr = HeapAlloc( SystemHeap, 0, sizeof(ASPI_DEVICE_INFO) );
68     curr->fd = fd;
69     curr->hostId = prb->SRB_HaId;
70     curr->target = prb->SRB_Target;
71     curr->lun = prb->SRB_Lun;
72
73     /* insert new record at beginning of open device list */
74     curr->next = ASPI_open_devices;
75     ASPI_open_devices = curr;
76     return fd;
77 }
78
79
80 static void
81 ASPI_DebugPrintCmd(SRB_ExecSCSICmd16 *prb, UINT16 mode)
82 {
83   BYTE  cmd;
84   int   i;
85   BYTE *cdb;
86   BYTE *lpBuf = 0;
87   dbg_decl_str(aspi, 512);
88
89   switch (mode)
90   {
91       case ASPI_DOS:
92         /* translate real mode address */
93         if (prb->SRB_BufPointer)
94             lpBuf = (BYTE *)DOSMEM_MapRealToLinear((UINT32)prb->SRB_BufPointer);
95         break;
96       case ASPI_WIN16:
97         lpBuf = PTR_SEG_TO_LIN(prb->SRB_BufPointer);
98         break;
99   }
100
101   switch (prb->CDBByte[0]) {
102   case CMD_INQUIRY:
103     TRACE(aspi, "{\n");
104     TRACE(aspi, "\tEVPD: %d\n", prb->CDBByte[1] & 1);
105     TRACE(aspi, "\tLUN: %d\n", (prb->CDBByte[1] & 0xc) >> 1);
106     TRACE(aspi, "\tPAGE CODE: %d\n", prb->CDBByte[2]);
107     TRACE(aspi, "\tALLOCATION LENGTH: %d\n", prb->CDBByte[4]);
108     TRACE(aspi, "\tCONTROL: %d\n", prb->CDBByte[5]);
109     TRACE(aspi, "}\n");
110     break;
111   case CMD_SCAN_SCAN:
112     TRACE(aspi, "Transfer Length: %d\n", prb->CDBByte[4]);
113     break;
114   }
115
116   TRACE(aspi, "Host Adapter: %d\n", prb->SRB_HaId);
117   TRACE(aspi, "Flags: %d\n", prb->SRB_Flags);
118   if (TARGET_TO_HOST(prb)) {
119     TRACE(aspi, "\tData transfer: Target to host. Length checked.\n");
120   }
121   else if (HOST_TO_TARGET(prb)) {
122     TRACE(aspi, "\tData transfer: Host to target. Length checked.\n");
123   }
124   else if (NO_DATA_TRANSFERED(prb)) {
125     TRACE(aspi, "\tData transfer: none\n");
126   }
127   else {
128     WARN(aspi, "\tTransfer by scsi cmd. Length not checked\n");
129   }
130
131   TRACE(aspi, "\tResidual byte length reporting %s\n", prb->SRB_Flags & 0x4 ? "enabled" : "disabled");
132   TRACE(aspi, "\tLinking %s\n", prb->SRB_Flags & 0x2 ? "enabled" : "disabled");
133   TRACE(aspi, "\tPosting %s\n", prb->SRB_Flags & 0x1 ? "enabled" : "disabled");
134   TRACE(aspi, "Target: %d\n", prb->SRB_Target);
135   TRACE(aspi, "Lun: %d\n", prb->SRB_Lun);
136   TRACE(aspi, "BufLen: %ld\n", prb->SRB_BufLen);
137   TRACE(aspi, "SenseLen: %d\n", prb->SRB_SenseLen);
138   TRACE(aspi, "BufPtr: %lx (%p)\n", prb->SRB_BufPointer, lpBuf);
139   TRACE(aspi, "LinkPointer %lx\n", prb->SRB_Rsvd1);
140   TRACE(aspi, "CDB Length: %d\n", prb->SRB_CDBLen);
141   TRACE(aspi, "POST Proc: %lx\n", (DWORD) prb->SRB_PostProc);
142   cdb = &prb->CDBByte[0];
143   cmd = prb->CDBByte[0];
144   for (i = 0; i < prb->SRB_CDBLen; i++) {
145     if (i != 0) dsprintf(aspi, ",");
146     dsprintf(aspi, "%02x", *cdb++);
147   }
148   TRACE(aspi, "CDB buffer[%s]\n", dbg_str(aspi));
149 }
150
151 static void
152 ASPI_PrintSenseArea16(SRB_ExecSCSICmd16 *prb)
153 {
154   int   i;
155   BYTE *cdb;
156   dbg_decl_str(aspi, 512);
157
158   cdb = &prb->CDBByte[0];
159   for (i = 0; i < prb->SRB_SenseLen; i++) {
160     if (i) dsprintf(aspi, ",");
161     dsprintf(aspi, "%02x", *cdb++);
162   }
163   TRACE(aspi, "SenseArea[%s]\n", dbg_str(aspi));
164 }
165
166 static void
167 ASPI_DebugPrintResult(SRB_ExecSCSICmd16 *prb, UINT16 mode)
168 {
169   BYTE *lpBuf = 0;
170
171   switch (mode)
172   {
173       case ASPI_DOS:
174         /* translate real mode address */
175         if (prb->SRB_BufPointer)
176             lpBuf = (BYTE *)DOSMEM_MapRealToLinear((UINT32)prb->SRB_BufPointer);
177         break;
178       case ASPI_WIN16:
179         lpBuf = PTR_SEG_TO_LIN(prb->SRB_BufPointer);
180         break;
181   }
182
183   switch (prb->CDBByte[0]) {
184   case CMD_INQUIRY:
185     TRACE(aspi, "Vendor: '%s'\n", lpBuf + INQUIRY_VENDOR);
186     break;
187   case CMD_TEST_UNIT_READY:
188     ASPI_PrintSenseArea16(prb);
189     break;
190   }
191 }
192
193 static WORD
194 ASPI_ExecScsiCmd(DWORD ptrPRB, UINT16 mode)
195 {
196   SRB_ExecSCSICmd16 *lpPRB = 0;
197   struct sg_header *sg_hd, *sg_reply_hdr;
198   int   status;
199   BYTE *lpBuf = 0;
200   int   in_len, out_len;
201   int   error_code = 0;
202   int   fd;
203
204   switch (mode)
205   {
206       case ASPI_DOS:
207         if (ptrPRB)
208             lpPRB = (SRB_ExecSCSICmd16 *)DOSMEM_MapRealToLinear(ptrPRB);
209         break;
210       case ASPI_WIN16:
211         lpPRB = PTR_SEG_TO_LIN(ptrPRB);
212         break;
213   }
214
215   ASPI_DebugPrintCmd(lpPRB, mode);
216
217   fd = ASPI_OpenDevice16(lpPRB);
218   if (fd == -1) {
219       ERR(aspi, "Failed: could not open device. Device permissions !?\n");
220       lpPRB->SRB_Status = SS_ERR;
221       return SS_ERR;
222   }
223
224   sg_hd = NULL;
225   sg_reply_hdr = NULL;
226
227   lpPRB->SRB_Status = SS_PENDING;
228
229   switch (mode)
230   {
231       case ASPI_DOS:
232         /* translate real mode address */
233         if (ptrPRB)
234             lpBuf = (BYTE *)DOSMEM_MapRealToLinear((UINT32)lpPRB->SRB_BufPointer);
235         break;
236       case ASPI_WIN16:
237         lpBuf = PTR_SEG_TO_LIN(lpPRB->SRB_BufPointer);
238         break;
239   }
240
241   if (!lpPRB->SRB_CDBLen) {
242       WARN(aspi, "Failed: lpPRB->SRB_CDBLen = 0.\n");
243       lpPRB->SRB_Status = SS_ERR;
244       return SS_ERR;
245   }
246
247   /* build up sg_header + scsi cmd */
248   if (HOST_TO_TARGET(lpPRB)) {
249     /* send header, command, and then data */
250     in_len = SCSI_OFF + lpPRB->SRB_CDBLen + lpPRB->SRB_BufLen;
251     sg_hd = (struct sg_header *) malloc(in_len);
252     memset(sg_hd, 0, SCSI_OFF);
253     memcpy(sg_hd + 1, &lpPRB->CDBByte[0], lpPRB->SRB_CDBLen);
254     if (lpPRB->SRB_BufLen) {
255       memcpy(((BYTE *) sg_hd) + SCSI_OFF + lpPRB->SRB_CDBLen, lpBuf, lpPRB->SRB_BufLen);
256     }
257   }
258   else {
259     /* send header and command - no data */
260     in_len = SCSI_OFF + lpPRB->SRB_CDBLen;
261     sg_hd = (struct sg_header *) malloc(in_len);
262     memset(sg_hd, 0, SCSI_OFF);
263     memcpy(sg_hd + 1, &lpPRB->CDBByte[0], lpPRB->SRB_CDBLen);
264   }
265
266   if (TARGET_TO_HOST(lpPRB)) {
267     out_len = SCSI_OFF + lpPRB->SRB_BufLen;
268     sg_reply_hdr = (struct sg_header *) malloc(out_len);
269     memset(sg_reply_hdr, 0, SCSI_OFF);
270     sg_hd->reply_len = out_len;
271   }
272   else {
273     out_len = SCSI_OFF;
274     sg_reply_hdr = (struct sg_header *) malloc(out_len);
275     memset(sg_reply_hdr, 0, SCSI_OFF);
276     sg_hd->reply_len = out_len;
277   }
278
279   status = write(fd, sg_hd, in_len);
280   if (status < 0 || status != in_len) {
281       int myerror = errno;
282
283     WARN(aspi, "Not enough bytes written to scsi device bytes=%d .. %d\n", in_len, status);
284     if (status < 0) {
285         if (myerror == ENOMEM) {
286             MSG("ASPI: Linux generic scsi driver\n  You probably need to re-compile your kernel with a larger SG_BIG_BUFF value (sg.h)\n  Suggest 130560\n");
287         }
288         WARN(aspi, "errno: = %d\n", myerror);
289     }
290     goto error_exit;
291   }
292
293   status = read(fd, sg_reply_hdr, out_len);
294   if (status < 0 || status != out_len) {
295     WARN(aspi, "not enough bytes read from scsi device%d\n", status);
296     goto error_exit;
297   }
298
299   if (sg_reply_hdr->result != 0) {
300     error_code = sg_reply_hdr->result;
301     WARN(aspi, "reply header error (%d)\n", sg_reply_hdr->result);
302     goto error_exit;
303   }
304
305   if (TARGET_TO_HOST(lpPRB) && lpPRB->SRB_BufLen) {
306     memcpy(lpBuf, sg_reply_hdr + 1, lpPRB->SRB_BufLen);
307   }
308
309   /* copy in sense buffer to amount that is available in client */
310   if (lpPRB->SRB_SenseLen) {
311     int sense_len = lpPRB->SRB_SenseLen;
312     if (lpPRB->SRB_SenseLen > 16)
313       sense_len = 16;
314     memcpy(SENSE_BUFFER(lpPRB), &sg_reply_hdr->sense_buffer[0], sense_len);
315   }
316
317
318   lpPRB->SRB_Status = SS_COMP;
319   lpPRB->SRB_HaStat = HASTAT_OK;
320   lpPRB->SRB_TargStat = STATUS_GOOD;
321
322   /* now do posting */
323
324   if (ASPI_POSTING(lpPRB) && lpPRB->SRB_PostProc) {
325     TRACE(aspi, "Post Routine (%lx) called\n", (DWORD) lpPRB->SRB_PostProc);
326     switch (mode)
327     {
328       case ASPI_DOS:
329       {
330         SEGPTR spPRB = MapLS(lpPRB);
331
332         Callbacks->CallASPIPostProc(lpPRB->SRB_PostProc, spPRB);        
333         UnMapLS(spPRB);
334         break;
335       }
336       case ASPI_WIN16:
337         Callbacks->CallASPIPostProc(lpPRB->SRB_PostProc, ptrPRB);
338         break;
339     }
340   }
341
342   free(sg_reply_hdr);
343   free(sg_hd);
344   ASPI_DebugPrintResult(lpPRB, mode);
345   return SS_COMP;
346   
347 error_exit:
348   if (error_code == EBUSY) {
349       lpPRB->SRB_Status = SS_ASPI_IS_BUSY;
350       TRACE(aspi, "Device busy\n");
351   }
352   else {
353       WARN(aspi, "Failed\n");
354       lpPRB->SRB_Status = SS_ERR;
355   }
356
357   /* I'm not sure exactly error codes work here
358    * We probably should set lpPRB->SRB_TargStat, SRB_HaStat ?
359    */
360   WARN(aspi, "error_exit\n");
361   free(sg_reply_hdr);
362   free(sg_hd);
363   return lpPRB->SRB_Status;
364 }
365 #endif
366
367
368 /***********************************************************************
369  *             GetASPISupportInfo16   (WINASPI.1)
370  */
371
372 WORD WINAPI GetASPISupportInfo16()
373 {
374 #ifdef linux
375     TRACE(aspi, "GETASPISupportInfo16\n");
376     /* high byte SS_COMP - low byte number of host adapters.
377      * FIXME!!! The number of host adapters is incorrect.
378      * I'm not sure how to determine this under linux etc.
379      */
380     return ((SS_COMP << 8) | 1);
381 #else
382     return ((SS_COMP << 8) | 0);
383 #endif
384 }
385
386
387 DWORD ASPI_SendASPICommand(DWORD ptrSRB, UINT16 mode)
388 {
389 #ifdef linux
390   LPSRB16 lpSRB = 0;
391
392   switch (mode)
393   {
394       case ASPI_DOS:
395         if (ptrSRB)
396             lpSRB = (LPSRB16)DOSMEM_MapRealToLinear(ptrSRB);
397         break;
398       case ASPI_WIN16:
399         lpSRB = PTR_SEG_TO_LIN(ptrSRB);
400         break;
401   }
402
403   switch (lpSRB->common.SRB_Cmd) {
404   case SC_HA_INQUIRY:
405     lpSRB->inquiry.SRB_Status = SS_COMP;       /* completed successfully */
406     if (lpSRB->inquiry.SRB_55AASignature == 0x55aa) {
407         TRACE(aspi, "Extended request detected (Adaptec's ASPIxDOS).\nWe don't support it at the moment.\n");
408     }
409     lpSRB->inquiry.SRB_ExtBufferSize = 0x2000; /* bogus value */
410     lpSRB->inquiry.HA_Count = 1;               /* not always */
411     lpSRB->inquiry.HA_SCSI_ID = 7;             /* not always ID 7 */
412     strcat(lpSRB->inquiry.HA_ManagerId, "Wine ASPI16"); /* max 15 chars */
413     strcat(lpSRB->inquiry.HA_Identifier, "Wine host"); /* FIXME: return host
414 adapter name */
415     memset(lpSRB->inquiry.HA_Unique, 0, 16); /* default HA_Unique content */
416     lpSRB->inquiry.HA_Unique[6] = 0x02; /* Maximum Transfer Length (128K, Byte> 4-7) */
417     FIXME(aspi, "ASPI: Partially implemented SC_HA_INQUIRY for adapter %d.\n", lpSRB->inquiry.SRB_HaId);
418     return SS_COMP;
419   case SC_GET_DEV_TYPE:
420     FIXME(aspi, "Not implemented SC_GET_DEV_TYPE\n");
421     break;
422   case SC_EXEC_SCSI_CMD:
423     return ASPI_ExecScsiCmd((DWORD)ptrSRB, mode);
424     break;
425   case SC_RESET_DEV:
426     FIXME(aspi, "Not implemented SC_RESET_DEV\n");
427     break;
428   default:
429     WARN(aspi, "Unknown command %d\n", lpSRB->common.SRB_Cmd);
430   }
431   return SS_INVALID_SRB;
432 #else
433   return SS_INVALID_SRB;
434 #endif
435 }
436
437
438 /***********************************************************************
439  *             SendASPICommand16   (WINASPI.2)
440  */
441 WORD WINAPI SendASPICommand16(SEGPTR segptr_srb)
442 {
443 #ifdef linux
444     return ASPI_SendASPICommand(segptr_srb, ASPI_WIN16);
445 #else
446     return 0; 
447 #endif
448 }
449
450
451 /***********************************************************************
452  *             GetASPIDLLVersion16   (WINASPI.4)
453  */
454
455 DWORD WINAPI GetASPIDLLVersion16()
456 {
457 #ifdef linux
458         return (DWORD)2;
459 #else
460         return (DWORD)0;
461 #endif
462 }
463
464
465 void WINAPI ASPI_DOS_func(DWORD srb)
466 {
467        ASPI_SendASPICommand(srb, ASPI_DOS);
468 }
469
470
471 /* returns a real mode call address to ASPI_DOS_func() */
472 void ASPI_DOS_HandleInt(CONTEXT *context)
473 {
474 #ifdef linux
475        FARPROC16 DOS_func;
476        DWORD dos;
477        LPBYTE dosptr;
478
479        DOS_func = MODULE_GetWndProcEntry16("ASPI_DOS_func");
480        dos = GlobalDOSAlloc(5);
481        dosptr = (BYTE *)PTR_SEG_OFF_TO_LIN(LOWORD(dos), 0);
482        *dosptr++ = 0xea; /* ljmp */
483        *(FARPROC16 *)dosptr = DOS_func;
484
485        *(DWORD *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context))
486                = MAKELONG(0, HIWORD(dos)); /* real mode address */
487        RESET_CFLAG(context);
488        AX_reg(context) = CX_reg(context);
489 #else
490        SET_CFLAG(context);
491 #endif
492 }