Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsi-dos.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* @(#)scsi-dos.c       1.11 03/12/10 Copyright 1998-2003 J. Schilling, 2003 Alex Kopylov reanimatolog@yandex.ru */
14 /*
15  *      Interface for the MS-DOS ASPI managers.
16  *      You need ASPI manager installed in your config.sys:
17  *              aspi*.sys for SCSI devices
18  *              oakaspi.sys for ATAPI devices
19  *              usbaspi.sys for USB devices
20  *
21  *      Made from Win32 ASPI library template.
22  *
23  *      Warning: you may change this source, but if you do that
24  *      you need to change the _usal_version and _usal_auth* string below.
25  *      You may not return "schily" for an SCG_AUTHOR request anymore.
26  *      Choose your name instead of "schily" and make clear that the version
27  *      string is related to a modified source.
28  *
29  *      Copyright (c) 1998-2003 J. Schilling
30  *      Copyright (c) 1999 A.L. Faber for the first implementation
31  *                         of this interface.
32  *      Copyright (c) 2003 Alex Kopylov reanimatolog@yandex.ru
33  */
34 /*
35  * This program is free software; you can redistribute it and/or modify
36  * it under the terms of the GNU General Public License version 2
37  * as published by the Free Software Foundation.
38  *
39  * This program is distributed in the hope that it will be useful,
40  * but WITHOUT ANY WARRANTY; without even the implied warranty of
41  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42  * GNU General Public License for more details.
43  *
44  * You should have received a copy of the GNU General Public License along with
45  * this program; see the file COPYING.  If not, write to the Free Software
46  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
47  */
48
49 #include <dos.h>
50 #include <dpmi.h>
51 #include <go32.h>
52 #include <usal/aspi-dos.h>
53
54 /*
55  *      Warning: you may change this source, but if you do that
56  *      you need to change the _usal_version and _usal_auth* string below.
57  *      You may not return "schily" for an SCG_AUTHOR request anymore.
58  *      Choose your name instead of "schily" and make clear that the version
59  *      string is related to a modified source.
60  */
61 static  char    _usal_trans_version[] = "scsi-dos.c-1.11";      /* The version for this transport*/
62
63 #define MAX_SCG 8
64 #define MAX_TGT 8
65 #define MAX_LUN 8
66
67 struct usal_local {
68         int     dummy;
69 };
70 #define usallocal(p)    ((struct usal_local *)((p)->local))
71
72 #define MAX_DMA_DOS     (63L*1024L)
73
74 static  BYTE    busses          = 1;
75 static  DWORD   SCSIMgrEntry    = 0;
76 static  int     AspiLoaded      = 0;
77
78 static  BOOL    _callback_flag;
79 static  _go32_dpmi_seginfo _callback_info;
80 static  _go32_dpmi_registers _callback_regs;
81
82 static  BOOL    SCSIMgrOpen(SCSI *);
83 static  void    SCSIMgrClose(void);
84 static  int     SCSIMgrSendSRB(SRB *, time_t);
85 static  void    SCSIMgrCallBack(_go32_dpmi_registers *regs);
86
87 static char *
88 usalo_version(SCSI *usalp, int what)
89 {
90         if (usalp != (SCSI *)0) {
91                 switch (what) {
92
93                 case SCG_VERSION:
94                         return (_usal_trans_version);
95                 case SCG_AUTHOR:
96                         return (_usal_auth_cdrkit);
97                 case SCG_SCCS_ID:
98                         return (__sccsid);
99                 }
100         }
101         return ((char *)0);
102 }
103
104 static int
105 usalo_help(SCSI *usalp, FILE *f)
106 {
107         __usal_help(f, "ASPI", "Generic transport independent SCSI",
108                 "", "bus,target,lun", "1,2,0", TRUE, FALSE);
109         return (0);
110 }
111
112 static int
113 usalo_open(SCSI *usalp, char *device)
114 {
115         int     busno   = usal_scsibus(usalp);
116         int     tgt     = usal_target(usalp);
117         int     tlun    = usal_lun(usalp);
118
119         if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
120                 errno = EINVAL;
121                 if (usalp->errstr)
122                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
123                                 "Illegal value for busno, target or lun '%d,%d,%d'",
124                                 busno, tgt, tlun);
125                 return (-1);
126         }
127
128         if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) {
129                 errno = EINVAL;
130                 if (usalp->errstr)
131                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
132                                 "Open by 'devname' not supported on this OS");
133                 return (-1);
134         }
135
136         /*
137          *  Check if variables are within the range
138          */
139         if (tgt >= 0 && tgt >= 0 && tlun >= 0) {
140                 /*
141                  * This is the non -scanbus case.
142                  */
143                 ;
144         } else if (tgt != -1 || tgt != -1 || tlun != -1) {
145                 errno = EINVAL;
146                 return (-1);
147         }
148
149         if (usalp->local == NULL) {
150                 usalp->local = malloc(sizeof (struct usal_local));
151                 if (usalp->local == NULL)
152                         return (0);
153         }
154         /*
155          * Try to open ASPI-Router
156          */
157         if (!SCSIMgrOpen(usalp))
158                 return (-1);
159
160         /*
161          * More than we have ...
162          */
163         if (busno >= busses) {
164                 SCSIMgrClose();
165                 return (-1);
166         }
167
168         /*
169          * Install Exit Function which closes the ASPI-Router
170          */
171         atexit(SCSIMgrClose);
172
173         /*
174          * Success after all
175          */
176         return (1);
177 }
178
179 static int
180 usalo_close(SCSI *usalp)
181 {
182         SCSIMgrClose();
183         return (0);
184 }
185
186 static long
187 usalo_maxdma(SCSI *usalp, long amt)
188 {
189         return (MAX_DMA_DOS);
190 }
191
192 static void *
193 usalo_getbuf(SCSI *usalp, long amt)
194 {
195         if (usalp->debug > 0) {
196                 fprintf((FILE *)usalp->errfile,
197                                 "usalo_getbuf: %ld bytes\n", amt);
198         }
199         usalp->bufbase = malloc((size_t)(amt));
200         return (usalp->bufbase);
201 }
202
203 static void
204 usalo_freebuf(SCSI *usalp)
205 {
206         if (usalp->bufbase)
207                 free(usalp->bufbase);
208         usalp->bufbase = NULL;
209 }
210
211 static __SBOOL
212 usalo_havebus(SCSI *usalp, int busno)
213 {
214         if (busno < 0 || busno >= busses)
215                 return (FALSE);
216
217         return (TRUE);
218 }
219
220 static int
221 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
222 {
223         if (busno < 0 || busno >= busses ||
224             tgt < 0 || tgt >= MAX_TGT ||
225             tlun < 0 || tlun >= MAX_LUN)
226                 return (-1);
227
228         /*
229          * Return fake
230          */
231         return (1);
232 }
233
234 static int
235 usalo_initiator_id(SCSI *usalp)
236 {
237         return (-1);
238 }
239
240 static int
241 usalo_isatapi(SCSI *usalp)
242 {
243         return (-1);
244 }
245
246 static int
247 usalo_reset(SCSI *usalp, int what)
248 {
249         errno = EINVAL;
250         return (-1);
251 }
252
253 static int
254 usalo_send(SCSI *usalp)
255 {
256         struct usal_cmd *sp = usalp->scmd;
257         SRB             Srb;
258         int             dos_memory_data_seg;
259         int             dos_memory_data_sel;
260         DWORD           dos_memory_data_ptr;
261
262         /*
263          * Check if ASPI library is loaded
264          */
265         if (!SCSIMgrEntry) {
266                 errmsgno(EX_BAD, "error in usalo_send: ASPI driver not loaded.\n");
267                 sp->error = SCG_FATAL;
268                 return (-1);
269         }
270
271         if (usalp->fd < 0) {
272                 sp->error = SCG_FATAL;
273                 return (0);
274         }
275
276         /*
277          * Initialize variables
278          */
279         sp->error               = SCG_NO_ERROR;
280         sp->sense_count         = 0;
281         sp->u_scb.cmd_scb[0]    = 0;
282         sp->resid               = 0;
283
284         memset(&Srb, 0, sizeof (Srb));
285
286         switch (sp->cdb_len) {
287
288         case 6:
289                 movebytes(&sp->cdb, &(Srb.Type.ExecSCSICmd.CmdLen._6.CDBByte), sp->cdb_len);
290                 break;
291         case 10:
292                 movebytes(&sp->cdb, &(Srb.Type.ExecSCSICmd.CmdLen._10.CDBByte), sp->cdb_len);
293                 break;
294         case 12:
295                 movebytes(&sp->cdb, &(Srb.Type.ExecSCSICmd.CmdLen._12.CDBByte), sp->cdb_len);
296                 break;
297         default:
298                 sp->error = SCG_FATAL;
299                 sp->ux_errno = EINVAL;
300                 fprintf((FILE *)usalp->errfile,
301                         "Unsupported sp->cdb_len: %u. Fatal error in usalo_send, exiting...\n", sp->cdb_len);
302                 return (-1);
303         }
304
305         if ((dos_memory_data_seg = __dpmi_allocate_dos_memory((sp->size>>4)+1, &dos_memory_data_sel)) == -1) {
306                 sp->error = SCG_FATAL;
307                 return (-1);
308         }
309         dos_memory_data_ptr = dos_memory_data_seg<<16;
310
311         _dosmemputb(sp->addr, sp->size, dos_memory_data_seg<<4);
312
313         Srb.Cmd = SC_EXEC_SCSI_CMD;
314         Srb.HaId = usal_scsibus(usalp);
315         Srb.Type.ExecSCSICmd.Target = usal_target(usalp);
316         Srb.Type.ExecSCSICmd.Lun = usal_lun(usalp);
317         Srb.Type.ExecSCSICmd.BufLen = sp->size;
318         Srb.Type.ExecSCSICmd.BufPointer = (void *)dos_memory_data_ptr;
319         Srb.Type.ExecSCSICmd.CDBLen = sp->cdb_len;
320         if (sp->sense_len <= (SENSE_LEN+2))
321                 Srb.Type.ExecSCSICmd.SenseLen = sp->sense_len;
322         else
323                 Srb.Type.ExecSCSICmd.SenseLen = (SENSE_LEN+2);
324
325         /*
326          * Enable SCSIMgrCallBack()
327          */
328         Srb.Flags |= SRB_POSTING;
329         Srb.Type.ExecSCSICmd.PostProc = (void *)(_callback_info.rm_offset|(_callback_info.rm_segment<<16));
330
331         /*
332          * Do we receive data from this ASPI command?
333          */
334         if (sp->flags & SCG_RECV_DATA) {
335
336                 Srb.Flags |= SRB_DIR_IN;
337         } else {
338                 /*
339                  * Set direction to output
340                  */
341                 if (sp->size > 0) {
342                         Srb.Flags |= SRB_DIR_OUT;
343                 }
344         }
345
346         SCSIMgrSendSRB(&Srb, usalp->scmd->timeout);
347         Srb.Type.ExecSCSICmd.BufPointer = sp->addr;
348         _dosmemgetb(dos_memory_data_seg<<4, sp->size, sp->addr);
349         __dpmi_free_dos_memory(dos_memory_data_sel);
350
351         if (Srb.Status == SS_PENDING) { /* Check if we got a timeout*/
352                 if (usalp->debug > 0) {
353                         fprintf((FILE *)usalp->errfile,
354                                         "Timeout....\n");
355                 }
356                         sp->error = SCG_TIMEOUT;
357                 return (1);             /* Return error             */
358         }
359
360         /*
361          * Check ASPI command status
362          */
363         if (Srb.Status != SS_COMP) {
364
365                 if (usalp->debug > 0) {
366                         fprintf((FILE *)usalp->errfile,
367                                 "Error in usalo_send: Srb.Status is 0x%x\n", Srb.Status);
368                 }
369
370                 switch (Srb.Status) {
371
372                         case SS_COMP:                   /* 0x01 SRB completed without error */
373                                 sp->error = SCG_NO_ERROR;
374                                 sp->ux_errno = 0;
375                                 break;
376
377                         case SS_PENDING:                /* 0x00 SRB being processed         */
378                         case SS_ABORTED:                /* 0x02 SRB aborted                 */
379                         case SS_ABORT_FAIL:             /* 0x03 Unable to abort SRB         */
380                         default:
381                                 sp->error = SCG_RETRYABLE;
382                                 sp->ux_errno = EIO;
383                                 break;
384
385                         case SS_ERR:                    /* 0x04 SRB completed with error    */
386                                 /*
387                                  * If the SCSI Status byte is != 0, we definitely could send
388                                  * the command to the target. We signal NO transport error.
389                                  */
390                                 sp->error = SCG_NO_ERROR;
391                                 sp->ux_errno = EIO;
392                                 if (Srb.Type.ExecSCSICmd.TargStat)
393                                         break;
394
395                         case SS_INVALID_CMD:            /* 0x80 Invalid ASPI command        */
396                         case SS_INVALID_HA:             /* 0x81 Invalid host adapter number */
397                         case SS_NO_DEVICE:              /* 0x82 SCSI device not installed   */
398                                 sp->error = SCG_FATAL;
399                                 sp->ux_errno = EINVAL;
400                                 break;
401                 }
402
403                 sp->sense_count = Srb.Type.ExecSCSICmd.SenseLen;
404                 if (sp->sense_count > sp->sense_len)
405                         sp->sense_count = sp->sense_len;
406
407                 memset(&sp->u_sense.Sense, 0x00, sizeof (sp->u_sense.Sense));
408
409                 switch (sp->cdb_len) {
410
411                 case 6:
412                         memcpy(&sp->u_sense.Sense, Srb.Type.ExecSCSICmd.CmdLen._6.SenseArea, sp->sense_count);
413                         break;
414                 case 10:
415                         memcpy(&sp->u_sense.Sense, Srb.Type.ExecSCSICmd.CmdLen._10.SenseArea, sp->sense_count);
416                         break;
417                 case 12:
418                         memcpy(&sp->u_sense.Sense, Srb.Type.ExecSCSICmd.CmdLen._12.SenseArea, sp->sense_count);
419                         break;
420                 }
421                 sp->u_scb.cmd_scb[0] = Srb.Type.ExecSCSICmd.TargStat;
422
423                 if (usalp->debug > 0) {
424                         fprintf((FILE *)usalp->errfile,
425                                 "Mapped to: error %d errno: %d\n", sp->error, sp->ux_errno);
426                 }
427                 return (1);
428         }
429
430         return (0);
431 }
432
433 static BOOL
434 SCSIMgrOpen(SCSI *usalp)
435 {
436         int             hSCSIMgr;
437         int             dos_memory_seg;
438         int             dos_memory_sel;
439         __dpmi_regs     _regs;
440         SRB             Srb;
441
442         if (SCSIMgrEntry) {
443                 AspiLoaded++;
444                 return (TRUE);
445         }
446
447         /*
448          * Open "SCSIMRG$"
449          */
450         if (!_dos_open("SCSIMGR$", 0, &hSCSIMgr)) {
451
452                 /* Alloc 16 bytes DOS memory */
453                 if ((dos_memory_seg = __dpmi_allocate_dos_memory(1, &dos_memory_sel)) != -1) {
454
455                         /* Look for SCSIMgr entry point */
456                         memset(&_regs, 0, sizeof (_regs));
457                         _regs.x.ax = 0x4402;
458                         _regs.x.bx = hSCSIMgr;
459                         _regs.x.cx = 0x0004;
460                         _regs.x.ds = dos_memory_seg;
461                         _regs.x.dx = 0x0000;
462                         if (!__dpmi_simulate_real_mode_interrupt(0x21,  &_regs)) {
463                                 _dosmemgetb(dos_memory_seg<<4, 4, &SCSIMgrEntry);
464                         }
465
466                         /* Free DOS memory */
467                         __dpmi_free_dos_memory(dos_memory_sel);
468                 }
469
470                 /* Close "SCSIMRG$" */
471                 _dos_close(hSCSIMgr);
472
473                 /* Allocate real mode callback */
474                 _callback_info.pm_offset = (unsigned long)&SCSIMgrCallBack;
475                 if (_go32_dpmi_allocate_real_mode_callback_retf(&_callback_info, &_callback_regs) == -1) {
476                         fprintf((FILE *)usalp->errfile, "Cannot allocate callback address!\n");
477                         SCSIMgrEntry = 0;
478                 }
479         }
480
481         /* SCSIMgr entry point founded? */
482         if (!SCSIMgrEntry) {
483                 fprintf((FILE *)usalp->errfile, "Cannot open ASPI manager! Try to get one from http://bootcd.narod.ru/\n");
484                 return (FALSE);
485         }
486
487         memset(&Srb, 0, sizeof (Srb));
488         Srb.Cmd = SC_HA_INQUIRY;
489
490         SCSIMgrSendSRB(&Srb, 10);
491
492         if (usalp->debug) {
493                 fprintf((FILE *)usalp->errfile, "Status : %ld\n", Srb.Status);
494                 fprintf((FILE *)usalp->errfile, "hacount: %d\n", Srb.Type.HAInquiry.Count);
495                 fprintf((FILE *)usalp->errfile, "SCSI id: %d\n", Srb.Type.HAInquiry.SCSI_ID);
496                 fprintf((FILE *)usalp->errfile, "Manager: '%.16s'\n", Srb.Type.HAInquiry.ManagerId);
497                 fprintf((FILE *)usalp->errfile, "Identif: '%.16s'\n", Srb.Type.HAInquiry.Identifier);
498                 usal_prbytes("Unique:", Srb.Type.HAInquiry.Unique, 16);
499         }
500
501         AspiLoaded++;
502         return (TRUE);
503 }
504
505 static void
506 SCSIMgrClose()
507 {
508         if (--AspiLoaded > 0)
509                 return;
510         if (SCSIMgrEntry) {
511                 _go32_dpmi_free_real_mode_callback(&_callback_info);
512                 SCSIMgrEntry = 0;
513         }
514 }
515
516 static int
517 SCSIMgrSendSRB(SRB *Srb, time_t timeout)
518 {
519         int             dos_memory_srb_seg;
520         int             dos_memory_srb_sel;
521         DWORD           dos_memory_srb_ptr;
522         struct timeval  st;
523         struct timeval  cr;
524         __dpmi_regs     _regs;
525
526         /* Alloc DOS memory */
527         if ((dos_memory_srb_seg = __dpmi_allocate_dos_memory((sizeof (SRB) >> 4) + 1, &dos_memory_srb_sel)) == -1) {
528                 Srb->Status = SS_NO_DEVICE; /* ??? fatal error */
529                 return (Srb->Status);
530         }
531         dos_memory_srb_ptr = dos_memory_srb_seg<<16;
532
533         /* Copy SRB to DOS memory */
534         _dosmemputb((void *)Srb, sizeof (SRB), dos_memory_srb_seg<<4);
535
536         /* Reset callback flag */
537         _callback_flag = 0;
538
539         /* Call SCSIMgr */
540         memset(&_regs, 0, sizeof (_regs));
541         _regs.x.ip = SCSIMgrEntry & 0xffff;
542         _regs.x.cs = SCSIMgrEntry >> 16;
543         __dpmi_simulate_real_mode_procedure_retf_stack(&_regs, 2, &dos_memory_srb_ptr);
544
545         /* Wait 'timeout' seconds for Srb->Status != SS_PENDING */
546         gettimeofday(&st, NULL);
547         do {
548                 _dosmemgetb(dos_memory_srb_seg << 4, sizeof (SRB), (void *)Srb);
549                 gettimeofday(&cr, NULL);
550         } while ((Srb->Status == SS_PENDING) && (cr.tv_sec - st.tv_sec < timeout));
551
552         /* Free DOS memory */
553         __dpmi_free_dos_memory(dos_memory_srb_sel);
554
555         return (Srb->Status);
556 }
557
558 static void
559 SCSIMgrCallBack(_go32_dpmi_registers *regs)
560 {
561         _callback_flag = 1;
562 }