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 Sigbjrn 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