1 /*
2  * E-UAE - The portable Amiga Emulator
3  *
4  * SCSI layer back-end for AmigaOS hosts
5  *
6  * Copyright 2005-2006 Richard Drummond
7  *           2005 Sigbj￸rn Skj₩ret (68k/MorphOS device-scanning)
8  */
9 
10 #include "sysconfig.h"
11 #include "sysdeps.h"
12 
13 #include "options.h"
14 #include "include/memory_uae.h"
15 #include "threaddep/thread.h"
16 #include "blkdev.h"
17 #include "scsidev.h"
18 #include "sleep.h"
19 #include "gui.h"
20 
21 #include <devices/scsidisk.h>
22 #include <proto/exec.h>
23 
24 
25 //#define DEBUG_ME
26 #ifdef  DEBUG_ME
27 #define DEBUG_LOG    write_log
28 #else
29 #define DEBUG_LOG(...) { }
30 #endif
31 
32 #define MAX_DRIVES	16
33 
34 
35 struct IOContext {
36     struct MsgPort  *msgport;
37     struct IOStdReq *ioreq;
38     struct SCSICmd   cmd;
39     uae_u8          *scsibuf;
40 };
41 
42 /*
43  * Private per-device data
44  */
45 struct scsidevdata {
46     const char       *device;
47     int               unit;
48 /*
49  * Aargh! AmigaOS device I/O requires the task that opened the device
50  * be the task that sends I/O requests. Thus, since we want to be able
51  * to access each device from two tasks, we need to maintain two I/O
52  * contexts - one for each task.
53  *
54  * TODO: Rework the upper layers of the SCSI wrapper so we can get
55  * rid of this.
56  */
57     struct Task      *main_task;
58     struct IOContext  main_ioc;
59 
60     struct Task      *local_task;
61     struct IOContext  local_ioc;
62 };
63 
64 static struct scsidevdata drives[MAX_DRIVES];
65 static int total_drives;
66 
67 
open_iocontext(const char * device,int unit,struct IOContext * ioc)68 static int open_iocontext (const char *device, int unit, struct IOContext *ioc)
69 {
70     int error = -1;
71 
72     DEBUG_LOG ("SCSIDEV: Opening device '%s' unit %d...", device, unit);
73 
74     if ((ioc->msgport = CreateMsgPort ())) {
75 	if ((ioc->ioreq = (struct IOStdReq*) CreateIORequest (ioc->msgport, sizeof(struct IOStdReq)))) {
76 	    error = OpenDevice ((char *)device,
77 				unit,
78 				(struct IORequest*)ioc->ioreq,
79 				0);
80 	    if (error == 0) {
81 		ioc->scsibuf = AllocMem (DEVICE_SCSI_BUFSIZE, MEMF_ANY);
82 
83 		if (ioc->scsibuf) {
84 		    DEBUG_LOG ("Success\n");
85 
86 		    return 0;
87 		}
88 		CloseDevice ((struct IORequest *)ioc->ioreq);
89 	    }
90 	    DeleteIORequest ((struct IORequest *)ioc->ioreq);
91 	    ioc->ioreq = 0;
92 	}
93 	DeleteMsgPort (ioc->msgport);
94 	ioc->msgport = 0;
95     }
96 
97     DEBUG_LOG ("Failed. Error=%d\n", error);
98 
99     return error;
100 }
101 
close_iocontext(struct IOContext * ioc)102 static void close_iocontext (struct IOContext *ioc)
103 {
104     if (ioc->scsibuf) {
105 	FreeMem (ioc->scsibuf, DEVICE_SCSI_BUFSIZE);
106 	ioc->scsibuf = 0;
107     }
108 
109     if (ioc->ioreq) {
110 	CloseDevice ((struct IORequest *)ioc->ioreq);
111 	DeleteIORequest ((struct IORequest *)ioc->ioreq);
112 	ioc->ioreq = 0;
113     }
114 
115     if (ioc->msgport) {
116 	DeleteMsgPort (ioc->msgport);
117 	ioc->msgport = 0;
118     }
119 }
120 
get_iocontext(int unitnum)121 static struct IOContext *get_iocontext (int unitnum)
122 {
123     struct scsidevdata *sdd  = &drives[unitnum];
124     struct Task        *self = FindTask (NULL);
125     struct IOContext   *ioc  = NULL;
126 
127     if (self == sdd->main_task) {
128 	DEBUG_LOG ("SCSIDEV: Doing I/O in main task\n");
129 
130 	ioc = &(sdd->main_ioc);
131     } else if (self == sdd->local_task) {
132 	DEBUG_LOG ("SCSIDEV: Doing I/O in local task\n");
133 	ioc = &(sdd->local_ioc);
134     }
135 
136     return ioc;
137 }
138 
139 
add_device(const char * device,int unit)140 static int add_device (const char *device, int unit)
141 {
142    int result = 0;
143 
144    if (total_drives < MAX_DRIVES) {
145 	struct scsidevdata *sdd = &drives[total_drives];
146 
147 	memset (sdd, 0, sizeof (struct scsidevdata));
148 
149 	sdd->device    = my_strdup (device); // FIXME: This will leak.
150 	sdd->unit      = unit;
151 	sdd->main_task = FindTask (NULL);
152 
153 	total_drives++;
154 	result = 1;
155     }
156     return result;
157 }
158 
execscsicmd(struct IOContext * ioc,const uae_u8 * cmd_data,int cmd_len,uae_u8 * inbuf,int inlen,int * outlen)159 static int execscsicmd (struct IOContext *ioc, const uae_u8 *cmd_data, int cmd_len,
160 			uae_u8 *inbuf, int inlen, int *outlen)
161 {
162     struct IOStdReq  *ioreq = ioc->ioreq;
163     struct SCSICmd   *cmd   = &(ioc->cmd);
164     int error;
165 
166     if (ioc->ioreq == 0)
167 	return -1;
168 
169     memset (cmd, 0, sizeof (struct SCSICmd));
170 
171     ioreq->io_Length  = sizeof(struct SCSICmd);
172     ioreq->io_Data    = (APTR)cmd;
173     ioreq->io_Command = HD_SCSICMD;
174 
175     DEBUG_LOG ("SCSIDEV: execscicmd data=%08lx len=%d, inbuf=%08lx"\
176 	       " inlen=%d\n", cmd_data, cmd_len, inbuf, inlen);
177 
178     if (inbuf) {
179 	cmd->scsi_Data   = (UWORD*)inbuf;
180 	cmd->scsi_Length = inlen;
181 	cmd->scsi_Flags  = SCSIF_READ | SCSIF_AUTOSENSE;
182 	memset (inbuf, 0, inlen);
183     } else
184 	cmd->scsi_Flags  = SCSIF_WRITE | SCSIF_AUTOSENSE;
185 
186     cmd->scsi_Command   = (UBYTE*)cmd_data;
187     cmd->scsi_CmdLength = cmd_len;
188 
189     DEBUG_LOG ("SCSIDEV: sending command: 0x%2x\n", cmd->scsi_Command[0]);
190 
191 	gui_flicker_led (LED_CD, 0, 0);
192 
193     error = DoIO ((struct IORequest *)ioreq);
194 
195     DEBUG_LOG ("SCSIDEV: result: %d\n", error);
196     DEBUG_LOG ("SCSIDEV: actual: %d\n", cmd->scsi_Actual);
197 
198     if (outlen)
199 	*outlen = cmd->scsi_Actual;
200 
201     return error;
202 }
203 
execscsicmd_out(int unitnum,const uae_u8 * cmd_data,int cmd_len)204 static const uae_u8 *execscsicmd_out (int unitnum, const uae_u8 *cmd_data, int cmd_len)
205 {
206     int error;
207     struct IOContext *ioc = get_iocontext (unitnum);
208 
209     DEBUG_LOG ("SCSIDEV: unit=%d: execscsicmd_out\n", unitnum);
210 
211     error = execscsicmd (get_iocontext (unitnum), cmd_data, cmd_len, 0, 0, 0);
212 
213     if (error == 0)
214 	return cmd_data;
215 
216     return 0;
217 }
218 
execscsicmd_in(int unitnum,const uae_u8 * cmd_data,int cmd_len,int * outlen)219 static const uae_u8 *execscsicmd_in (int unitnum, const uae_u8 *cmd_data, int cmd_len, int *outlen)
220 {
221     int error;
222     struct IOContext *ioc = get_iocontext (unitnum);
223 
224     DEBUG_LOG ("SCSIDEV: unit=%d: execscsicmd_in\n", unitnum);
225 
226     error = execscsicmd (ioc, cmd_data, cmd_len, ioc->scsibuf, DEVICE_SCSI_BUFSIZE, outlen);
227 
228     if (error == 0)
229 	return ioc->scsibuf;
230 
231     return 0;
232 }
233 
execscsicmd_direct(int unitnum,uaecptr acmd)234 static int execscsicmd_direct (int unitnum, uaecptr acmd)
235 {
236     int sactual = 0;
237     struct IOContext *ioc   =  get_iocontext (unitnum);
238     struct IOStdReq  *ioreq =  ioc->ioreq;
239     struct SCSICmd   *cmd   = &(ioc->cmd);
240 
241     uaecptr scsi_data         = get_long (acmd + 0);
242     uae_u32 scsi_len          = get_long (acmd + 4);
243     uaecptr scsi_cmd          = get_long (acmd + 12);
244     int     scsi_cmd_len      = get_word (acmd + 16);
245     int     scsi_cmd_len_orig = scsi_cmd_len;
246     uae_u8  scsi_flags        = get_byte (acmd + 20);
247     uaecptr scsi_sense        = get_long (acmd + 22);
248     uae_u16 scsi_sense_len    = get_word (acmd + 26);
249     int     io_error          = 0;
250     int     parm;
251 
252     addrbank *bank_data    = &get_mem_bank (scsi_data);
253     addrbank *bank_cmd	   = &get_mem_bank (scsi_cmd);
254 
255     uae_u8   *scsi_datap;
256     uae_u8   *scsi_datap_org;
257 
258     DEBUG_LOG ("SCSIDEV: unit=%d: execscsicmd_direct\n", unitnum);
259 
260     /* do transfer directly to and from Amiga memory */
261     if (!bank_data || !bank_data->check (scsi_data, scsi_len))
262 	return -5; /* IOERR_BADADDRESS */
263 
264     memset (cmd, 0, sizeof (cmd));
265 
266     ioreq->io_Length  = sizeof(struct SCSICmd);
267     ioreq->io_Data    = (APTR)cmd;
268     ioreq->io_Command = HD_SCSICMD;
269 
270     scsi_datap = scsi_datap_org
271 	= scsi_len ? bank_data->xlateaddr (scsi_data) : 0;
272 
273     cmd->scsi_Data        = (UWORD*) scsi_datap;
274     cmd->scsi_Length      = scsi_len;
275     cmd->scsi_Flags       = scsi_flags;
276     cmd->scsi_Command     = bank_cmd->xlateaddr (scsi_cmd);
277     cmd->scsi_CmdLength   = scsi_cmd_len;
278     cmd->scsi_SenseData   = scsi_sense ? get_real_address (scsi_sense) : 0;
279     cmd->scsi_SenseLength = scsi_sense_len;
280 
281     DEBUG_LOG ("SCSIDEV: sending command: 0x%2x\n", cmd->scsi_Command[0]);
282 
283     io_error = DoIO ((struct IORequest *)ioreq);
284 
285     DEBUG_LOG ("SCSIDEV: error: %d actual %d\n", io_error, cmd->scsi_Actual);
286 
287 	gui_flicker_led (LED_CD, 0, 1);
288 
289     put_long (acmd + 8,  cmd->scsi_Actual);
290     put_word (acmd + 18, cmd->scsi_CmdActual);
291     put_byte (acmd + 21, cmd->scsi_Status);
292     put_byte (acmd + 28, cmd->scsi_SenseActual);
293 
294     return io_error;
295 }
296 
check_device(const char * device,int unit)297 static int check_device (const char *device, int unit)
298 {
299     int result = 0;
300     struct IOContext ioc;
301 
302 
303     if (open_iocontext (device, unit, &ioc) == 0) {
304 	const uae_u8 INQUIRY_CMD[6] = {0x12,0,0,0,36,0};
305 	uae_u8 inqbuf[36];
306 	int outlen;
307 
308 	memset (inqbuf, 0, sizeof inqbuf);
309 
310 	if (execscsicmd (&ioc, INQUIRY_CMD, sizeof INQUIRY_CMD, inqbuf, sizeof inqbuf, &outlen) == 0) {
311 	    int         type     =                 inqbuf[0] & 0x1F;
312 	    const char *vendor   = (const char *) &inqbuf[8];
313 	    const char *prod_id  = (const char *) &inqbuf[16];
314 	    const char *prod_rev = (const char *) &inqbuf[32];
315 
316 	    write_log ("%-16.16s %3d: '%-8.8s' '%-16.16s' '%-4.4s' %s\n",
317 		       device, unit, vendor, prod_id, prod_rev, type == 5 ? "CD-ROM" : "");
318 	    if (type == 5)
319 		result = 1;
320 
321 	    close_iocontext (&ioc);
322 	}
323     }
324 
325     return result;
326 }
327 
media_check(int unitnum)328 static int media_check (int unitnum)
329 {
330     int media;
331     const uae_u8 TEST_UNIT_READY_CMD[6] = {0,0,0,0,0,0};
332 
333     media = execscsicmd_out (unitnum, TEST_UNIT_READY_CMD, sizeof(TEST_UNIT_READY_CMD)) ? 1 : 0;
334 
335     DEBUG_LOG ("SCSIDEV: media check :%d\n", media);
336 
337     return media;
338 }
339 
340 
open_scsi_device(int unitnum)341 static int open_scsi_device (int unitnum)
342 {
343     int result = 0;
344 
345     DEBUG_LOG ("SCSIDEV: unit=%d: open_scsi_device\n", unitnum);
346 
347     if (unitnum < total_drives) {
348 	struct scsidevdata *sdd = &drives[unitnum];
349 	struct IOContext   *ioc = &(sdd->main_ioc);
350 
351 	if (ioc->ioreq) {
352 	    DEBUG_LOG ("Already open\n");
353 
354 	    result = 1;
355 	} else {
356 	    if (!open_iocontext (sdd->device, sdd->unit, ioc))
357 		result = 1;
358 	}
359     }
360 
361     return result;
362 }
363 
close_scsi_device(int unitnum)364 static void close_scsi_device (int unitnum)
365 {
366     DEBUG_LOG ("SCSIDEV: unit=%d: close_scsi_device\n", unitnum);
367 
368     if (unitnum < total_drives) {
369 	struct scsidevdata *sdd  = &drives[unitnum];
370 	struct Task        *self = FindTask (NULL);
371 
372 	if (self == sdd->main_task)
373 	    close_iocontext (&(sdd->main_ioc));
374     }
375 }
376 
open_device_thread(int unitnum)377 static int open_device_thread (int unitnum)
378 {
379     int result = 0;
380 
381     DEBUG_LOG ("SCSIDEV: unit=%d: open_device_thread\n", unitnum);
382 
383     if (unitnum < total_drives) {
384 	struct scsidevdata *sdd = &drives[unitnum];
385 	struct IOContext   *ioc = &(sdd->local_ioc);
386 
387 	if (ioc->ioreq) {
388 	    result = 1;
389 
390 	    if (sdd->local_task == FindTask (NULL)) {
391 		DEBUG_LOG ("Already open\n");
392 	    } else {
393 		write_log ("SCSIDEV: Warning: Orphaned I/O context.\n");
394 		open_iocontext (sdd->device, sdd->unit, ioc);
395 		sdd->local_task = FindTask (NULL);
396 	    }
397 	} else {
398 	    if (!open_iocontext (sdd->device, sdd->unit, ioc)) {
399 		result = 1;
400 		sdd->local_task = FindTask (NULL);
401 	    }
402 	}
403     }
404 
405     return result;
406 }
407 
close_device_thread(int unitnum)408 static void close_device_thread (int unitnum)
409 {
410     DEBUG_LOG ("SCSIDEV: unit=%d: close_device_thread\n", unitnum);
411 
412     if (unitnum < total_drives) {
413 	struct scsidevdata *sdd  = &drives[unitnum];
414 	struct Task        *self = FindTask (NULL);
415 
416 	if(sdd->local_ioc.ioreq) {
417 	    if (self == sdd->local_task) {
418 		close_iocontext (&(sdd->local_ioc));
419 		sdd->local_task = NULL;
420 	    } else
421 		write_log ("SCSIDEV: Warning: Failed to close local I/O context.\n");
422 	}
423     }
424 }
425 
close_scsi_bus(void)426 static void close_scsi_bus (void)
427 {
428     DEBUG_LOG ("SCSIDEV: close_scsi_bus\n");
429 
430     /* Not currently used */
431 }
432 
433 #ifdef __amigaos4__
find_devices(void)434 static int find_devices (void)
435 {
436     struct FileSystemData *filesys;
437     char cd_device[8];
438     int i;
439 
440     /* block 'please insert volume CDx: requesters */
441     struct Process *self = (struct Process *) FindTask (NULL);
442     APTR old_windptr = self->pr_WindowPtr;
443     self->pr_WindowPtr = (APTR)-1;
444 
445     /* Scan for device names of the form CDx: */
446     for (i=0; i<10; i++) {
447 	sprintf (cd_device, "CD%d:\n", i);
448 
449 	DEBUG_LOG ("Looking for %s\n", cd_device);
450 
451 	if ((filesys = GetDiskFileSystemData (cd_device))) {
452 	    if (check_device (filesys->fsd_DeviceName, filesys->fsd_DeviceUnit))
453 		add_device (filesys->fsd_DeviceName, filesys->fsd_DeviceUnit);
454 
455 	    FreeDiskFileSystemData (filesys);
456 	}
457     }
458 
459     self->pr_WindowPtr = old_windptr;
460 
461     return total_drives;
462 }
463 #else
464 #include <exec/lists.h>
465 #include <proto/dos.h>
466 #include <dos/dos.h>
467 #include <dos/dosextens.h>
468 #include <dos/filehandler.h>
469 #include <string.h>
470 
471 #ifndef NEWLIST
472 #define NEWLIST NewList
473 #endif
474 
475 #ifndef ADDTAIL
476 #define ADDTAIL AddTail
477 #endif
478 
479 struct MyDevNode
480 {
481     struct MinNode node;
482     UBYTE DevName[256];
483     ULONG DevUnit;
484 };
485 
find_devices(void)486 static int find_devices (void)
487 {
488     struct DosList *dl;
489     struct MyDevNode *mdn, *mdn2;
490     struct MinList mdl;
491 
492     NEWLIST ((struct List *)&mdl);
493     dl = LockDosList (LDF_DEVICES | LDF_READ);
494 
495     while ((dl = NextDosEntry (dl, LDF_DEVICES))) {
496 	struct DeviceNode *dn = (struct DeviceNode *) dl;
497 	struct FileSysStartupMsg *fs = BADDR (dn->dn_Startup);
498 
499 	if (TypeOfMem (fs)) {
500 	    UBYTE *devname = BADDR (fs->fssm_Device);
501 
502 	    if (TypeOfMem (devname) && devname[0] != 0) {
503 		mdn2 = AllocVec (sizeof(*mdn2), MEMF_PUBLIC);
504 
505 		for (mdn = (struct MyDevNode *)mdl.mlh_Head; mdn->node.mln_Succ;
506 		     mdn = (struct MyDevNode *)mdn->node.mln_Succ) {
507 
508 		    if (strncmp (mdn->DevName, devname+1, devname[0]) == 0
509 			&& mdn->DevUnit == fs->fssm_Unit) {
510 
511 			FreeVec (mdn2);
512 			mdn2 = NULL;
513 			break;
514 		    }
515 		}
516 
517 		if (mdn2) {
518 		    memcpy (mdn2->DevName, devname+1, devname[0]);
519 		    mdn2->DevName[devname[0]] = 0;
520 		    mdn2->DevUnit = fs->fssm_Unit;
521 		    ADDTAIL ((struct List *)&mdl, (struct Node *)&mdn2->node);
522 		}
523 	    }
524 	}
525     }
526 
527     UnLockDosList (LDF_DEVICES | LDF_READ);
528 
529     for (mdn  = (struct MyDevNode *)mdl.mlh_Head;
530 	 mdn2 = (struct MyDevNode *)mdn->node.mln_Succ; (mdn = mdn2)) {
531 
532 	if (check_device (mdn->DevName, mdn->DevUnit))
533 	    add_device (mdn->DevName, mdn->DevUnit);
534 
535 	FreeVec(mdn);
536     }
537 
538     return total_drives;
539 }
540 #endif
541 
open_scsi_bus(int flags)542 static int open_scsi_bus (int flags)
543 {
544     int num_devices = 0;
545 
546     DEBUG_LOG ("SCSIDEV: open_scsi_bus\n");
547 
548     num_devices = find_devices ();
549 
550     write_log ("SCSIDEV: %d device(s) found\n", num_devices);
551 
552     return num_devices;
553 }
554 
info_device(int unitnum,struct device_info * di)555 static struct device_info *info_device (int unitnum, struct device_info *di)
556 {
557     DEBUG_LOG ("SCSIDEV: unit=%d: info_device\n", unitnum);
558 
559     if (unitnum < total_drives) {
560 	struct scsidevdata *sdd = &drives[unitnum];
561 
562 	di->bus		    = 0;
563 	di->target	    = unitnum;
564 	di->lun		    = 0;
565 	di->media_inserted  = media_check (unitnum);
566 	di->write_protected = 1;
567 	di->bytespersector  = 2048;
568 	di->cylinders	    = 1;
569 	di->type	    = INQ_ROMD; /* We only support CD/DVD drives for now */
570 	di->id		    = unitnum + 1;
571 
572 	snprintf (di->label, 60, "%s:%d", sdd->device, sdd->unit);
573     } else
574 	di = 0;
575 
576     return di;
577 }
578 
check_isatapi(int unitnum)579 static int check_isatapi (int unitnum)
580 {
581     return 0; // FIXME
582 //    return drives[unitnum].isatapi;
583 }
584 
585 struct device_functions devicefunc_scsi_amiga = {
586     open_scsi_bus,
587     close_scsi_bus,
588     open_scsi_device,
589     close_scsi_device,
590     info_device,
591     execscsicmd_out,
592     execscsicmd_in,
593     execscsicmd_direct,
594     0, 0, 0, 0, 0, 0, 0,
595     check_isatapi,
596     open_device_thread,
597     close_device_thread
598 };
599