ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/scsi.cpp
Revision: 1.8
Committed: 2008-01-01T09:40:31Z (16 years, 4 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * scsi.cpp - SCSI Manager
3 *
4 * Basilisk II (C) 1997-2008 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * SEE ALSO
23 * Inside Macintosh: Devices, chapter 3 "SCSI Manager"
24 * Technote DV 24: "Fear No SCSI"
25 */
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "sysdeps.h"
31 #include "cpu_emulation.h"
32 #include "main.h"
33 #include "user_strings.h"
34 #include "scsi.h"
35
36 #define DEBUG 0
37 #include "debug.h"
38
39
40 // Error codes
41 enum {
42 scCommErr = 2,
43 scArbNBErr,
44 scBadParmsErr,
45 scPhaseErr,
46 scCompareErr,
47 scMgrBusyErr,
48 scSequenceErr,
49 scBusTOErr,
50 scComplPhaseErr
51 };
52
53 // TIB opcodes
54 enum {
55 scInc = 1,
56 scNoInc,
57 scAdd,
58 scMove,
59 scLoop,
60 scNop,
61 scStop,
62 scComp
63 };
64
65 // Logical SCSI phases
66 enum {
67 PH_FREE, // Bus free
68 PH_ARBITRATED, // Bus arbitrated (after SCSIGet())
69 PH_SELECTED, // Target selected (after SCSISelect())
70 PH_TRANSFER // Command sent (after SCSICmd())
71 };
72
73 // Global variables
74 static int target_id; // ID of active target
75 static int phase; // Logical SCSI phase
76 static uint16 fake_status; // Faked 5830 status word
77 static bool reading; // Flag: reading from device
78
79 const int SG_TABLE_SIZE = 1024;
80 static int sg_index; // Index of first unused entry in S/G table
81 static uint8 *sg_ptr[SG_TABLE_SIZE]; // Scatter/gather table data pointer (host address space)
82 static uint32 sg_len[SG_TABLE_SIZE]; // Scatter/gather table data length
83 static uint32 sg_total_length; // Total data length
84
85
86 /*
87 * Execute TIB, constructing S/G table
88 */
89
90 static int16 exec_tib(uint32 tib)
91 {
92 for (;;) {
93
94 // Read next opcode and parameters
95 uint16 cmd = ReadMacInt16(tib); tib += 2;
96 uint32 ptr = ReadMacInt32(tib); tib += 4;
97 uint32 len = ReadMacInt32(tib); tib += 4;
98
99 #if DEBUG
100 const char *cmd_str;
101 switch (cmd) {
102 case scInc: cmd_str = "INC "; break;
103 case scNoInc: cmd_str = "NOINC"; break;
104 case scAdd: cmd_str = "ADD "; break;
105 case scMove: cmd_str = "MOVE "; break;
106 case scLoop: cmd_str = "LOOP "; break;
107 case scNop: cmd_str = "NOP "; break;
108 case scStop: cmd_str = "STOP "; break;
109 case scComp: cmd_str = "COMP "; break;
110 default: cmd_str = "??? "; break;
111 }
112 D(bug(" %s(%d) %08x %d\n", cmd_str, cmd, ptr, len));
113 #endif
114
115 switch (cmd) {
116 case scInc:
117 WriteMacInt32(tib - 8, ptr + len);
118 case scNoInc:
119 if ((sg_index > 0) && (Mac2HostAddr(ptr) == sg_ptr[sg_index-1] + sg_len[sg_index-1]))
120 sg_len[sg_index-1] += len; // Merge to previous entry
121 else {
122 if (sg_index == SG_TABLE_SIZE) {
123 ErrorAlert(GetString(STR_SCSI_SG_FULL_ERR));
124 return -108;
125 }
126 sg_ptr[sg_index] = Mac2HostAddr(ptr); // Create new entry
127 sg_len[sg_index] = len;
128 sg_index++;
129 }
130 sg_total_length += len;
131 break;
132
133 case scAdd:
134 WriteMacInt32(ptr, ReadMacInt32(ptr) + len);
135 break;
136
137 case scMove:
138 WriteMacInt32(len, ReadMacInt32(ptr));
139 break;
140
141 case scLoop:
142 WriteMacInt32(tib - 4, len - 1);
143 if (len - 1 > 0)
144 tib += (int32)ptr - 10;
145 break;
146
147 case scNop:
148 break;
149
150 case scStop:
151 return 0;
152
153 case scComp:
154 printf("WARNING: Unimplemented scComp opcode\n");
155 return scCompareErr;
156
157 default:
158 printf("FATAL: Unrecognized TIB opcode %d\n", cmd);
159 return scBadParmsErr;
160 }
161 }
162 }
163
164
165 /*
166 * Reset SCSI bus
167 */
168
169 int16 SCSIReset(void)
170 {
171 D(bug("SCSIReset\n"));
172
173 phase = PH_FREE;
174 fake_status = 0x0000; // Bus free
175 sg_index = 0;
176 target_id = 8;
177 return 0;
178 }
179
180
181 /*
182 * Arbitrate bus
183 */
184
185 int16 SCSIGet(void)
186 {
187 D(bug("SCSIGet\n"));
188 if (phase != PH_FREE)
189 return scMgrBusyErr;
190
191 phase = PH_ARBITRATED;
192 fake_status = 0x0040; // Bus busy
193 reading = false;
194 sg_index = 0; // Flush S/G table
195 sg_total_length = 0;
196 return 0;
197 }
198
199
200 /*
201 * Select SCSI device
202 */
203
204 int16 SCSISelect(int id)
205 {
206 D(bug("SCSISelect %d\n", id));
207 if (phase != PH_ARBITRATED)
208 return scSequenceErr;
209
210 // ID valid?
211 if (id >= 0 && id <= 7) {
212 target_id = id;
213
214 // Target present?
215 if (scsi_is_target_present(target_id)) {
216 phase = PH_SELECTED;
217 fake_status = 0x006a; // Target selected, command phase
218 return 0;
219 }
220 }
221
222 // Error
223 phase = PH_FREE;
224 fake_status = 0x0000; // Bus free
225 return scCommErr;
226 }
227
228
229 /*
230 * Send SCSI command
231 */
232
233 int16 SCSICmd(int cmd_length, uint8 *cmd)
234 {
235 #if DEBUG
236 switch (cmd_length) {
237 case 6:
238 D(bug("SCSICmd len 6, cmd %02x %02x %02x %02x %02x %02x\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]));
239 break;
240 case 10:
241 D(bug("SCSICmd len 10, cmd %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]));
242 break;
243 case 12:
244 D(bug("SCSICmd len 12, cmd %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9], cmd[10], cmd[11]));
245 break;
246 default:
247 D(bug("SCSICmd bogus length %d\n", cmd_length));
248 break;
249 }
250 #endif
251
252 if (phase != PH_SELECTED)
253 return scPhaseErr;
254
255 // Commdn length valid?
256 if (cmd_length != 6 && cmd_length != 10 && cmd_length != 12)
257 return scBadParmsErr;
258
259 // Set command, extract LUN
260 scsi_set_cmd(cmd_length, cmd);
261
262 // Extract LUN, set target
263 if (!scsi_set_target(target_id, (cmd[1] >> 5) & 7)) {
264 phase = PH_FREE;
265 fake_status = 0x0000; // Bus free
266 return scCommErr;
267 }
268
269 phase = PH_TRANSFER;
270 fake_status = 0x006e; // Target selected, data phase
271 return 0;
272 }
273
274
275 /*
276 * Read data
277 */
278
279 int16 SCSIRead(uint32 tib)
280 {
281 D(bug("SCSIRead TIB %08lx\n", tib));
282 if (phase != PH_TRANSFER)
283 return scPhaseErr;
284
285 // Execute TIB, fill S/G table
286 reading = true;
287 return exec_tib(tib);
288 }
289
290
291 /*
292 * Write data
293 */
294
295 int16 SCSIWrite(uint32 tib)
296 {
297 D(bug("SCSIWrite TIB %08lx\n", tib));
298 if (phase != PH_TRANSFER)
299 return scPhaseErr;
300
301 // Execute TIB, fill S/G table
302 return exec_tib(tib);
303 }
304
305
306 /*
307 * Wait for command completion (we're actually doing everything in here...)
308 */
309
310 int16 SCSIComplete(uint32 timeout, uint32 message, uint32 stat)
311 {
312 D(bug("SCSIComplete wait %d, msg %08lx, stat %08lx\n", timeout, message, stat));
313 WriteMacInt16(message, 0);
314 if (phase != PH_TRANSFER)
315 return scPhaseErr;
316
317 // Send command, process S/G table
318 uint16 scsi_stat = 0;
319 bool success = scsi_send_cmd(sg_total_length, reading, sg_index, sg_ptr, sg_len, &scsi_stat, timeout);
320 WriteMacInt16(stat, scsi_stat);
321
322 // Complete command
323 phase = PH_FREE;
324 fake_status = 0x0000; // Bus free
325 return success ? 0 : scCommErr;
326 }
327
328
329 /*
330 * Get bus status
331 */
332
333 uint16 SCSIStat(void)
334 {
335 D(bug("SCSIStat returns %04x\n", fake_status));
336 return fake_status;
337 }
338
339
340 /*
341 * SCSI Manager busy?
342 */
343
344 int16 SCSIMgrBusy(void)
345 {
346 // D(bug("SCSIMgrBusy\n"));
347 return phase != PH_FREE;
348 }