1  /*
2   * E-UAE - The portable Amiga Emulator
3   *
4   * SCSI layer back-end for Linux hosts
5   *
6   * Copyright 2006 Jochen Becher
7   * Copyright 2006 Richard Drummond
8   *
9   */
10 
11 
12 #include "sysconfig.h"
13 #include "sysdeps.h"
14 
15 #include "options.h"
16 #include "../include/memory_uae.h"
17 #include "blkdev.h"
18 #include "scsidev.h"
19 #include "gui.h"
20 
21 #ifdef SCSIEMU_LINUX_IOCTL
22 
23 #include <linux/cdrom.h>
24 #include <sys/ioctl.h>
25 
26 #define DEBUG_ME
27 #ifdef  DEBUG_ME
28 # define DEBUG_LOG write_log
29 #else
30 # define DEBUG_LOG(...) { }
31 #endif
32 
33 struct scsidevdata {
34     char *name;
35     int fd;
36     int isatapi;
37     uae_u8 buf[DEVICE_SCSI_BUFSIZE];
38 };
39 
40 #define MAX_SCSI_DRIVES 16
41 static struct scsidevdata drives[MAX_SCSI_DRIVES];
42 static int total_drives = 0;
43 
44 static uae_u8 *execscsicmd_in_ioctl (int unitnum, uae_u8 *cmd_data, int cmd_len, int *outlen);
45 
46 /*
47  * this little piece of magic from Toni Wilen is needed to detect
48  * ATAPI devices.
49  */
is_atapi_drive(int unitnum)50 static int is_atapi_drive (int unitnum)
51 {
52     static uae_u8 cmd[6] = {0x12, 0, 0, 0, 36, 0}; /* INQUIRY */
53     uae_u8 out[36];
54     int outlen = sizeof (out);
55     uae_u8 *p = execscsicmd_in_ioctl (unitnum, cmd, sizeof (cmd), &outlen);
56     if (!p) {
57 	DEBUG_LOG ("SCSIDEV: Inquiry command failed; unit %d is not ATAPI drive\n", unitnum);
58 	return 0;
59     }
60     if (outlen >= 2 && (p[0] & 31) == 5 && (p[2] & 7) == 0) {
61 	DEBUG_LOG ("SCSIDEV: unit %d is ATAPI drive\n", unitnum);
62 	return 1;
63     }
64     DEBUG_LOG ("SCSIDEV: unit %d is not ATAPI drive\n", unitnum);
65     return 0;
66 }
67 
open_scsi_bus_ioctl(int flags)68 static int open_scsi_bus_ioctl (int flags)
69 {
70     DEBUG_LOG ("SCSIDEV: open_scsi_bus_ioctl\n");
71     /* TODO: scan everything / bus for drives; not just one drive */
72     total_drives = 1;
73     drives[0].name = currprefs.scsi_device;
74     drives[0].fd = -1;
75     drives[0].isatapi = 0;
76     return 1;
77 }
78 
close_scsi_bus_ioctl(void)79 static void close_scsi_bus_ioctl (void)
80 {
81     DEBUG_LOG ("SCSIDEV: close_scsi_bus_ioctl\n");
82 }
83 
open_scsi_device_ioctl(int unitnum,const char * dummy1,int dummy2)84 static int open_scsi_device_ioctl (int unitnum, const char* dummy1, int dummy2)
85 {
86     int result = 0;
87 
88     DEBUG_LOG ("SCSIDEV: unit = %d: open_scsi_device_ioctl\n", unitnum);
89     if (unitnum < total_drives) {
90 	struct scsidevdata *sdd = &drives[unitnum];
91 
92 	DEBUG_LOG ("SCSIDEV: unit = %d, name = %s, fd = %d\n", unitnum, sdd->name, sdd->fd);
93 
94 	if (sdd->fd == -1) {
95 	    if ((sdd->fd = open (sdd->name, O_RDONLY|O_NONBLOCK)) != -1) {
96 		DEBUG_LOG ("SCSIDEV: Successfully opened drive %s\n", sdd->name);
97 		sdd->isatapi = is_atapi_drive (unitnum);
98 		result = 1;
99 	    } else {
100 		write_log ("SCSIDEV: Failed to open drive %s\n", sdd->name);
101 	    }
102 	} else {
103 	    /* already open */
104 	    DEBUG_LOG ("SCSIDEV: unit %d is already opened.\n", unitnum);
105 	    result = 1;
106 	}
107     } else {
108 	DEBUG_LOG ("SCSIDEV: bad unit number %d\n", unitnum);
109     }
110     return result;
111 }
112 
close_scsi_device_ioctl(int unitnum)113 static void close_scsi_device_ioctl (int unitnum)
114 {
115     struct scsidevdata *sdd;
116 
117     DEBUG_LOG ("SCSIDEV: unit = %d: close_scsi_device_ioctl\n", unitnum);
118     if (unitnum >= total_drives) {
119 	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
120 	return;
121     }
122     sdd = &drives[unitnum];
123     if (sdd->fd != -1) {
124 	close (sdd->fd);
125 	sdd->fd = -1;
126     }
127 }
128 
media_check(struct scsidevdata * sdd)129 static int media_check (struct scsidevdata *sdd)
130 {
131     if (ioctl (sdd->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK)
132 	return 1;
133     else
134 	return 0;
135 }
136 
execscsicmd_out_ioctl(int unitnum,uae_u8 * cmd_data,int cmd_len)137 static uae_u8 *execscsicmd_out_ioctl (int unitnum, uae_u8 *cmd_data, int cmd_len)
138 {
139     struct scsidevdata *sdd;
140     struct cdrom_generic_command cmd;
141     int io_error;
142 
143     DEBUG_LOG ("SCSIDEV: unit = %d, execscsicmd_out_ioctl\n", unitnum);
144     if (unitnum >= total_drives) {
145 	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
146 	return 0;
147     }
148     sdd = &drives[unitnum];
149     if (cmd_len > CDROM_PACKET_SIZE) {
150 	DEBUG_LOG ("SCSIDEV: cmd_len too large (%d)\n", cmd_len);
151 	return 0;
152     }
153     memcpy (cmd.cmd, cmd_data, cmd_len);
154     cmd.buffer = 0;
155     cmd.buflen = 0;
156     cmd.stat = 0;
157     cmd.sense = 0;
158     cmd.data_direction = CGC_DATA_WRITE;
159     cmd.quiet = 0;
160     cmd.timeout = 80*60;
161 
162 	gui_flicker_led (LED_CD, 0, 1);
163 
164     io_error = ioctl (sdd->fd, CDROM_SEND_PACKET, &cmd);
165     DEBUG_LOG ("SCSIDEV: error: %d, stat: %d\n", io_error, cmd.stat);
166     if (io_error != 0) {
167 	DEBUG_LOG ("SCSIDEV: errno: %d, %s\n", errno, strerror (errno));
168 	return 0;
169     }
170     return cmd_data;
171 }
172 
execscsicmd_in_ioctl(int unitnum,uae_u8 * cmd_data,int cmd_len,int * outlen)173 static uae_u8 *execscsicmd_in_ioctl (int unitnum, uae_u8 *cmd_data, int cmd_len, int *outlen)
174 {
175     struct scsidevdata *sdd;
176     struct cdrom_generic_command cmd;
177     int io_error;
178 
179     DEBUG_LOG ("SCSIDEV: unit = %d, execscsicmd_in_ioctl\n", unitnum);
180     if (unitnum >= total_drives) {
181 	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
182 	return 0;
183     }
184     sdd = &drives[unitnum];
185     if (cmd_len > CDROM_PACKET_SIZE) {
186 	DEBUG_LOG ("SCSIDEV: cmd_len too large (%d)\n", cmd_len);
187 	return 0;
188     }
189     memcpy (cmd.cmd, cmd_data, cmd_len);
190     cmd.buffer = sdd->buf;
191     cmd.buflen = sizeof (sdd->buf);
192     cmd.stat = 0;
193     cmd.sense = 0;
194     cmd.data_direction = CGC_DATA_READ;
195     cmd.quiet = 0;
196     cmd.timeout = 80*60;
197 
198         gui_flicker_led (LED_CD, 0, 1);
199 
200     io_error = ioctl (sdd->fd, CDROM_SEND_PACKET, &cmd);
201     DEBUG_LOG ("SCSIDEV: error: %d, stat: %d\n", io_error, cmd.stat);
202     if (io_error != 0) {
203 	DEBUG_LOG ("SCSIDEV: errno: %d, %s\n", errno, strerror (errno));
204 	return 0;
205     }
206     if (outlen) {
207 	*outlen = cmd.buflen;
208     }
209     return sdd->buf;
210 }
211 
execscsicmd_direct_ioctl(int unitnum,struct amigascsi * ascsi)212 static int execscsicmd_direct_ioctl (int unitnum, struct amigascsi* ascsi)
213 {
214     struct scsidevdata *sdd;
215     struct cdrom_generic_command cmd;
216     struct request_sense sense;
217     uaecptr acmd = VALUE_TO_PTR(ascsi);
218 
219     uaecptr scsi_data         = get_long (acmd + 0);
220     uae_u32 scsi_len          = get_long (acmd + 4);
221     uaecptr scsi_cmd          = get_long (acmd + 12);
222     int     scsi_cmd_len      = get_word (acmd + 16);
223     uae_u8  scsi_flags        = get_byte (acmd + 20);
224     uae_u8  scsi_status       = get_byte (acmd + 21);
225     uaecptr scsi_sense        = get_long (acmd + 22);
226     uae_u16 scsi_sense_len    = get_word (acmd + 26);
227 
228     int io_error;
229     unsigned int senselen;
230     int parm, i;
231 
232     addrbank *bank_data    = &get_mem_bank (scsi_data);
233     addrbank *bank_cmd	   = &get_mem_bank (scsi_cmd);
234     addrbank *bank_sense   = &get_mem_bank (scsi_sense);
235 
236     uae_u8   *scsi_datap;
237     uae_u8   *scsi_datap_org;
238 
239     DEBUG_LOG ("SCSIDEV: unit = %d: execscsicmd_direct_ioctl\n", unitnum);
240     DEBUG_LOG ("SCSIDEV: scsi_len = %d, scsi_cmd_len = %d, scsi_sense_len = %d, scsi_flags = %x\n",
241 	       scsi_len, scsi_cmd_len, scsi_sense_len, scsi_flags);
242 
243     if (unitnum >= total_drives) {
244 	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
245 	return -1; /* TODO: better error code */
246     }
247     sdd = &drives[unitnum];
248 
249     /* do transfer directly to and from Amiga memory */
250     if (!bank_data || !bank_data->check (scsi_data, scsi_len)) {
251 	DEBUG_LOG ("SCSIDEV: illegal Amiga memory buffer\n");
252 	return -5; /* IOERR_BADADDRESS */
253     }
254 
255     if (scsi_cmd_len > CDROM_PACKET_SIZE) {
256 	DEBUG_LOG ("SCSIDEV: scsi_cmd_len too large (%d)\n", scsi_cmd_len);
257 	return -5; /* TODO: better code */
258     }
259 
260     scsi_datap = scsi_datap_org = (scsi_len ? bank_data->xlateaddr (scsi_data) : 0);
261 
262     memcpy (cmd.cmd, bank_cmd->xlateaddr (scsi_cmd), scsi_cmd_len);
263     cmd.buffer = scsi_datap;
264     cmd.buflen = scsi_len;
265     cmd.stat = scsi_status;
266     if (sdd->isatapi) {
267 	scsi_atapi_fixup_pre (cmd.cmd, &scsi_cmd_len, &scsi_datap,
268 			      &scsi_len, &parm);
269     }
270     senselen = (scsi_flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
271 		      (scsi_flags & 2) ? scsi_sense_len : /* SCSIF_AUTOSENSE */
272 		      0;
273     cmd.sense = senselen > 0 ? &sense : 0;
274     cmd.data_direction = (scsi_flags & 1) ? CGC_DATA_READ : CGC_DATA_WRITE;
275     cmd.quiet = 0;
276     cmd.timeout = 80*60;
277 
278         gui_flicker_led (LED_CD, 0, 1);
279 
280     io_error = ioctl (sdd->fd, CDROM_SEND_PACKET, &cmd);
281 
282     DEBUG_LOG ("SCSIDEV: error: %d, stat: %d\n", io_error, cmd.stat);
283 
284     if (cmd.stat != 0) {
285 	unsigned int n;
286 
287 	io_error = 45;  /* HFERR_BadStatus */
288 	put_byte (acmd + 8, 0);
289 	put_byte (acmd + 18, 0 /*scsi_cmd_len */);
290 	put_byte (acmd + 21, cmd.stat);
291 	DEBUG_LOG ("SCSIDEV: bad status\n");
292 	n = cmd.sense ? cmd.sense->add_sense_len + 7 : 0;
293 	if (senselen > n) {
294 	    if (scsi_sense)
295 		memset (bank_sense->xlateaddr (scsi_sense), 0, senselen);
296 	    senselen = n;
297 	}
298 	DEBUG_LOG ("SCSIDEV: senselen = %d\n", senselen);
299 	if (scsi_sense && cmd.sense && senselen > 0) {
300 	    memcpy (bank_sense->xlateaddr (scsi_sense), cmd.sense, senselen);
301 	}
302 	put_byte (acmd + 28, senselen);
303     } else {
304 	put_byte (acmd + 28, 0);
305 	if (scsi_sense && senselen > 0) {
306 	    memset (bank_sense->xlateaddr (scsi_sense), 0, senselen);
307 	}
308 	if (io_error == 0) {
309 	    if (sdd->isatapi) {
310 		scsi_atapi_fixup_post (cmd.cmd, scsi_cmd_len,
311 				       scsi_datap_org, scsi_datap,
312 				       &scsi_len, parm);
313 	    }
314 	    put_long (acmd + 8,  scsi_len);
315 	    put_word (acmd + 18, scsi_cmd_len);
316 	    put_byte (acmd + 21, cmd.stat);
317 	    io_error = 0;
318 	} else {
319 	    DEBUG_LOG ("SCSIDEV: errno: %d, %s\n", errno, strerror (errno));
320 	    put_long (acmd + 8,  0);
321 	    put_word (acmd + 18, 0);
322 	    put_byte (acmd + 21, cmd.stat);
323 	    io_error = 20; /* TODO: Map errors */
324 	}
325     }
326     if (scsi_datap != scsi_datap_org)
327 	xfree (scsi_datap);
328     return io_error;
329 }
330 
info_device_ioctl(int unitnum,struct device_info * di,int dummy1,int dummy2)331 static struct device_info *info_device_ioctl (int unitnum, struct device_info *di, int dummy1, int dummy2)
332 {
333     DEBUG_LOG ("SCSIDEV: unit = %d: info_device_ioctl\n", unitnum);
334 
335     if (unitnum < total_drives) {
336 	struct scsidevdata *sdd = &drives[unitnum];
337 
338 	media_check (sdd);
339 
340 	di->bus		    = 0;
341 	di->target	    = unitnum;
342 	di->lun		    = 0;
343 	di->media_inserted  = media_check (sdd);
344 	di->write_protected = 1;
345 	di->bytespersector  = 2048;
346 	di->cylinders	    = 1;
347 	di->type	    = INQ_ROMD; /* We only support CD/DVD drives for now */
348 	di->unitnum	    = unitnum + 1;
349 	/* TODO: Create a more informative device label */
350 	sprintf (di->label, "[%s]", sdd->name);
351     } else {
352 	di = 0;
353     }
354 
355     return di;
356 }
357 
pause_ioctl(int unitnum,int paused)358 static int pause_ioctl (int unitnum, int paused)
359 {
360     DEBUG_LOG ("SCSIDEV: unit = %d, pause_ioctl not implemented\n", unitnum);
361     return 0;
362 }
363 
stop_ioctl(int unitnum)364 static int stop_ioctl (int unitnum)
365 {
366     DEBUG_LOG ("SCSIDEV: unit = %d, stop_ioctl not implemented\n", unitnum);
367     return 0;
368 }
369 
play_ioctl(int unitnum,int startmsf,int endmsf,int scan,play_status_callback pStatus,play_subchannel_callback pSubC)370 static int play_ioctl (int unitnum, int startmsf, int endmsf, int scan,
371 					play_status_callback pStatus, play_subchannel_callback pSubC)
372 {
373     DEBUG_LOG ("SCSIDEV: unit = %d, play_ioctl not implemented\n", unitnum);
374     return 0;
375 }
376 
qcode_ioctl(int unitnum,uae_u8 * buf,int offset)377 static int qcode_ioctl (int unitnum, uae_u8 *buf, int offset)
378 {
379     DEBUG_LOG ("SCSIDEV: unit = %d, qcode_ioctl not implemented\n", unitnum);
380     return 0;
381 }
382 
toc_ioctl(int unitnum,struct cd_toc_head * cth)383 static int toc_ioctl (int unitnum, struct cd_toc_head* cth)
384 {
385     DEBUG_LOG ("SCSIDEV: unit = %d, toc_ioctl not implemented\n", unitnum);
386     return 0;
387 }
388 
read_ioctl(int unitnum,uae_u8 * buf,int offset,int length)389 static int read_ioctl (int unitnum, uae_u8 *buf, int offset, int length)
390 {
391     DEBUG_LOG ("SCSIDEV: unit = %d, read_ioctl not implemented\n", unitnum);
392     return 0;
393 }
394 
write_ioctl(int unitnum,uae_u8 * buf,int offset,int length)395 static int write_ioctl (int unitnum, uae_u8 *buf, int offset, int length)
396 {
397     DEBUG_LOG ("SCSIDEV: unit = %d, write_ioctl not implemented\n", unitnum);
398     return 0;
399 }
400 
check_isatapi_ioctl(int unitnum)401 static int check_isatapi_ioctl (int unitnum)
402 {
403     DEBUG_LOG ("SCSIDEV: unit = %d, check_isatapi_ioctl\n", unitnum);
404     if (unitnum >= total_drives) {
405 	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
406 	return 0;
407     }
408     return drives[unitnum].isatapi;
409 }
410 
411 struct device_functions devicefunc_scsi_linux_ioctl = {
412 	NULL, /* name */
413     open_scsi_bus_ioctl, /* open_bus_func     openbus; */
414     close_scsi_bus_ioctl, /* close_bus_func    closebus; */
415     open_scsi_device_ioctl, /* open_device_func  opendev; */
416     close_scsi_device_ioctl, /* close_device_func closedev; */
417     info_device_ioctl, /* info_device_func  info; */
418 
419     execscsicmd_out_ioctl, /* execscsicmd_out_func    exec_out; */
420     execscsicmd_in_ioctl, /* execscsicmd_in_func     exec_in; */
421     execscsicmd_direct_ioctl, /* execscsicmd_direct_func exec_direct; */
422 
423     pause_ioctl, /* pause_func pause; */
424     stop_ioctl, /* stop_func  stop; */
425     play_ioctl, /* play_func  play; */
426     0,          /* colume_func qcode; */
427     qcode_ioctl, /* qcode_func qcode; */
428     toc_ioctl, /* toc_func   toc; */
429     read_ioctl, /* read_func  read; */
430     0,          /* rawread_func read; */
431     write_ioctl, /* write_func write; */
432 
433     check_isatapi_ioctl, /* isatapi_func isatapi; */
434     0,                   /* ismedia_func ismedia; */
435 
436     0                    /* scsiemu_func scsiemu; */
437 };
438 #endif
439