1 /*
2  * UAE - The Un*x Amiga Emulator
3  *
4  * SCSI emulation (not uaescsi.device)
5  *
6  * Copyright 2007 Toni Wilen
7  *
8  */
9 
10 #include "sysconfig.h"
11 #include "sysdeps.h"
12 
13 #include "options.h"
14 #include "scsi.h"
15 #include "filesys.h"
16 #include "blkdev.h"
17 
18 static int outcmd[] = { 0x0a, 0x2a, 0x2f, 0xaa, 0x15, 0x55, -1 };
19 static int incmd[] = { 0x03, 0x08, 0x12, 0x1a, 0x5a, 0x25, 0x28, 0x37, 0x42, 0x43, 0xa8, 0x51, 0x52, -1 };
20 static int nonecmd[] = { 0x00, 0x1b, 0x1e, 0x35, -1 };
21 static int scsicmdsizes[] = { 6, 10, 10, 12, 16, 12, 10, 10 };
22 
scsi_data_dir(struct scsi_data * sd)23 static int scsi_data_dir(struct scsi_data *sd)
24 {
25 	int i;
26 	uae_u8 cmd;
27 
28 	cmd = sd->cmd[0];
29 	for (i = 0; outcmd[i] >= 0; i++) {
30 		if (cmd == outcmd[i]) {
31 			return 1;
32 		}
33 	}
34 	for (i = 0; incmd[i] >= 0; i++) {
35 		if (cmd == incmd[i]) {
36 			return -1;
37 		}
38 	}
39 	for (i = 0; nonecmd[i] >= 0; i++) {
40 		if (cmd == nonecmd[i]) {
41 			return 0;
42 		}
43 	}
44 	write_log (_T("SCSI command %02X, no direction specified!\n"), sd->cmd[0]);
45 	return 0;
46 }
47 
scsi_emulate_analyze(struct scsi_data * sd)48 void scsi_emulate_analyze (struct scsi_data *sd)
49 {
50 	int cmd_len, data_len;
51 
52 	data_len = sd->data_len;
53 	cmd_len = scsicmdsizes[sd->cmd[0] >> 5];
54 	switch (sd->cmd[0])
55 	{
56 	case 0x0a:
57 		data_len = sd->cmd[4] * sd->blocksize;
58 	break;
59 	case 0x2a:
60 		data_len = ((sd->cmd[7] << 8) | (sd->cmd[8] << 0)) * (uae_s64)sd->blocksize;
61 	break;
62 	case 0xaa:
63 		data_len = ((sd->cmd[6] << 24) | (sd->cmd[7] << 16) | (sd->cmd[8] << 8) | (sd->cmd[9] << 0)) * (uae_s64)sd->blocksize;
64 	break;
65 	}
66 	sd->cmd_len = cmd_len;
67 	sd->data_len = data_len;
68 	sd->direction = scsi_data_dir (sd);
69 }
70 
scsi_illegal_lun(struct scsi_data * sd)71 void scsi_illegal_lun(struct scsi_data *sd)
72 {
73 	uae_u8 *s = sd->sense;
74 
75 	memset (s, 0, sizeof (sd->sense));
76 	sd->status = 2; /* CHECK CONDITION */
77 	s[0] = 0x70;
78 	s[2] = 5; /* ILLEGAL REQUEST */
79 	s[12] = 0x25; /* INVALID LUN */
80 	sd->sense_len = 0x12;
81 }
82 
scsi_emulate_cmd(struct scsi_data * sd)83 void scsi_emulate_cmd(struct scsi_data *sd)
84 {
85 	sd->status = 0;
86 	if ((sd->message[0] & 0xc0) == 0x80 && (sd->message[0] & 0x1f)) {
87 		uae_u8 lun = sd->message[0] & 0x1f;
88 		if (lun > 7)
89 			lun = 7;
90 		sd->cmd[1] &= ~(7 << 5);
91 		sd->cmd[1] |= lun << 5;
92 	}
93 	//write_log (_T("CMD=%02x\n"), sd->cmd[0]);
94 	if (sd->cd_emu_unit >= 0) {
95 		if (sd->cmd[0] == 0x03) { /* REQUEST SENSE */
96 			int len = sd->cmd[4];
97 			scsi_cd_emulate(sd->cd_emu_unit, sd->cmd, 0, 0, 0, 0, 0, 0, 0, sd->atapi); /* ack request sense */
98 			memset (sd->buffer, 0, len);
99 			memcpy (sd->buffer, sd->sense, sd->sense_len > len ? len : sd->sense_len);
100 			sd->data_len = len;
101 		} else {
102 			sd->status = scsi_cd_emulate(sd->cd_emu_unit, sd->cmd, sd->cmd_len, sd->buffer, &sd->data_len, sd->reply, &sd->reply_len, sd->sense, &sd->sense_len, sd->atapi);
103 			if (sd->status == 0) {
104 				if (sd->reply_len > 0) {
105 					memset(sd->buffer, 0, 256);
106 					memcpy(sd->buffer, sd->reply, sd->reply_len);
107 				}
108 			}
109 		}
110 	} else if (sd->nativescsiunit < 0) {
111 		if (sd->cmd[0] == 0x03) { /* REQUEST SENSE */
112 			int len = sd->cmd[4];
113 			memset (sd->buffer, 0, len);
114 			memcpy (sd->buffer, sd->sense, sd->sense_len > len ? len : sd->sense_len);
115 			sd->data_len = len;
116 		} else {
117 			sd->status = scsi_hd_emulate(&sd->hfd->hfd, sd->hfd,
118 				sd->cmd, sd->cmd_len, sd->buffer, &sd->data_len, sd->reply, &sd->reply_len, sd->sense, &sd->sense_len);
119 			if (sd->status == 0) {
120 				if (sd->reply_len > 0) {
121 					memset(sd->buffer, 0, 256);
122 					memcpy(sd->buffer, sd->reply, sd->reply_len);
123 				}
124 			}
125 		}
126 	} else {
127 		struct amigascsi as;
128 
129 		memset(sd->sense, 0, 256);
130 		memset(&as, 0, sizeof as);
131 		memcpy (&as.cmd, sd->cmd, sd->cmd_len);
132 		as.flags = 2 | 1;
133 		if (sd->direction > 0)
134 			as.flags &= ~1;
135 		as.sense_len = 32;
136 		as.cmd_len = sd->cmd_len;
137 		as.data = sd->buffer;
138 		as.len = sd->direction < 0 ? DEVICE_SCSI_BUFSIZE : sd->data_len;
139 		sys_command_scsi_direct_native(sd->nativescsiunit, &as);
140 		sd->status = as.status;
141 		sd->data_len = as.len;
142 		if (sd->status) {
143 			sd->direction = 0;
144 			sd->data_len = 0;
145 			memcpy(sd->sense, as.sensedata, as.sense_len);
146 		}
147 	}
148 	sd->offset = 0;
149 }
150 
scsi_alloc_hd(int id,struct hd_hardfiledata * hfd)151 struct scsi_data *scsi_alloc_hd(int id, struct hd_hardfiledata *hfd)
152 {
153 	struct scsi_data *sd = xcalloc (struct scsi_data, 1);
154 	sd->hfd = hfd;
155 	sd->id = id;
156 	sd->nativescsiunit = -1;
157 	sd->cd_emu_unit = -1;
158 	sd->blocksize = hfd->hfd.ci.blocksize;
159 	return sd;
160 }
161 
scsi_alloc_cd(int id,int unitnum,bool atapi)162 struct scsi_data *scsi_alloc_cd(int id, int unitnum, bool atapi)
163 {
164 	struct scsi_data *sd;
165 	if (!sys_command_open (unitnum)) {
166 		write_log (_T("SCSI: CD EMU scsi unit %d failed to open\n"), unitnum);
167 		return NULL;
168 	}
169 	sd = xcalloc (struct scsi_data, 1);
170 	sd->id = id;
171 	sd->cd_emu_unit = unitnum;
172 	sd->nativescsiunit = -1;
173 	sd->atapi = atapi;
174 	sd->blocksize = 2048;
175 	return sd;
176 }
177 
scsi_alloc_native(int id,int nativeunit)178 struct scsi_data *scsi_alloc_native(int id, int nativeunit)
179 {
180 	struct scsi_data *sd;
181 	if (!sys_command_open (nativeunit)) {
182 		write_log (_T("SCSI: native scsi unit %d failed to open\n"), nativeunit);
183 		return NULL;
184 	}
185 	sd = xcalloc (struct scsi_data, 1);
186 	sd->id = id;
187 	sd->nativescsiunit = nativeunit;
188 	sd->cd_emu_unit = -1;
189 	sd->blocksize = 2048;
190 	return sd;
191 }
192 
scsi_reset(void)193 void scsi_reset(void)
194 {
195 	//device_func_init (DEVICE_TYPE_SCSI);
196 }
197 
scsi_free(struct scsi_data * sd)198 void scsi_free(struct scsi_data *sd)
199 {
200 	if (!sd)
201 		return;
202 	if (sd->nativescsiunit >= 0) {
203 		sys_command_close (sd->nativescsiunit);
204 		sd->nativescsiunit = -1;
205 	}
206 	if (sd->cd_emu_unit >= 0) {
207 		sys_command_close (sd->cd_emu_unit);
208 		sd->cd_emu_unit = -1;
209 	}
210 	xfree(sd);
211 }
212 
scsi_start_transfer(struct scsi_data * sd)213 void scsi_start_transfer(struct scsi_data *sd)
214 {
215 	sd->offset = 0;
216 }
217 
scsi_send_data(struct scsi_data * sd,uae_u8 b)218 int scsi_send_data(struct scsi_data *sd, uae_u8 b)
219 {
220 	if (sd->direction == 1) {
221 		if (sd->offset >= SCSI_DATA_BUFFER_SIZE) {
222 			write_log (_T("SCSI data buffer overflow!\n"));
223 			return 0;
224 		}
225 		sd->buffer[sd->offset++] = b;
226 	} else if (sd->direction == 2) {
227 		if (sd->offset >= 16) {
228 			write_log (_T("SCSI command buffer overflow!\n"));
229 			return 0;
230 		}
231 		sd->cmd[sd->offset++] = b;
232 		if (sd->offset == sd->cmd_len)
233 			return 1;
234 	} else {
235 		write_log (_T("scsi_send_data() without direction!\n"));
236 		return 0;
237 	}
238 	if (sd->offset == sd->data_len)
239 		return 1;
240 	return 0;
241 }
242 
scsi_receive_data(struct scsi_data * sd,uae_u8 * b)243 int scsi_receive_data(struct scsi_data *sd, uae_u8 *b)
244 {
245 	if (!sd->data_len)
246 		return -1;
247 	*b = sd->buffer[sd->offset++];
248 	if (sd->offset == sd->data_len)
249 		return 1; // requested length got
250 	return 0;
251 }
252