ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/scsi.cpp
Revision: 1.1
Committed: 1999-10-03T14:16:25Z (25 years, 2 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

# Content
1 /*
2 * scsi.cpp - SCSI Manager
3 *
4 * Basilisk II (C) 1997-1999 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 D(bug(" %d %08x %d\n", cmd, ptr, len));
100
101 switch (cmd) {
102 case scInc:
103 WriteMacInt32(tib - 8, ptr + len);
104 case scNoInc:
105 if ((sg_index > 0) && (Mac2HostAddr(ptr) == sg_ptr[sg_index-1] + sg_len[sg_index-1]))
106 sg_len[sg_index-1] += len; // Merge to previous entry
107 else {
108 if (sg_index == SG_TABLE_SIZE) {
109 ErrorAlert(GetString(STR_SCSI_SG_FULL_ERR));
110 return -108;
111 }
112 sg_ptr[sg_index] = Mac2HostAddr(ptr); // Create new entry
113 sg_len[sg_index] = len;
114 sg_index++;
115 }
116 sg_total_length += len;
117 break;
118
119 case scAdd:
120 WriteMacInt32(ptr, ReadMacInt32(ptr) + len);
121 break;
122
123 case scMove:
124 WriteMacInt32(len, ReadMacInt32(ptr));
125 break;
126
127 case scLoop:
128 WriteMacInt32(tib - 4, len - 1);
129 if (len - 1 > 0)
130 tib += (int32)ptr - 10;
131 break;
132
133 case scNop:
134 break;
135
136 case scStop:
137 return 0;
138
139 case scComp:
140 printf("WARNING: Unimplemented scComp opcode\n");
141 return scCompareErr;
142
143 default:
144 printf("FATAL: Unrecognized TIB opcode %d\n", cmd);
145 return scBadParmsErr;
146 }
147 }
148 }
149
150
151 /*
152 * Reset SCSI bus
153 */
154
155 int16 SCSIReset(void)
156 {
157 D(bug("SCSIReset\n"));
158
159 phase = PH_FREE;
160 fake_status = 0x0000; // Bus free
161 sg_index = 0;
162 target_id = 8;
163 return 0;
164 }
165
166
167 /*
168 * Arbitrate bus
169 */
170
171 int16 SCSIGet(void)
172 {
173 D(bug("SCSIGet\n"));
174 if (phase != PH_FREE)
175 return scMgrBusyErr;
176
177 phase = PH_ARBITRATED;
178 fake_status = 0x0040; // Bus busy
179 reading = false;
180 sg_index = 0; // Flush S/G table
181 sg_total_length = 0;
182 return 0;
183 }
184
185
186 /*
187 * Select SCSI device
188 */
189
190 int16 SCSISelect(int id)
191 {
192 D(bug("SCSISelect %d\n", id));
193 if (phase != PH_ARBITRATED)
194 return scSequenceErr;
195
196 // ID valid?
197 if (id >= 0 && id <= 7) {
198 target_id = id;
199
200 // Target present?
201 if (scsi_is_target_present(target_id)) {
202 phase = PH_SELECTED;
203 fake_status = 0x006a; // Target selected, command phase
204 return 0;
205 }
206 }
207
208 // Error
209 phase = PH_FREE;
210 fake_status = 0x0000; // Bus free
211 return scCommErr;
212 }
213
214
215 /*
216 * Send SCSI command
217 */
218
219 int16 SCSICmd(int cmd_length, uint8 *cmd)
220 {
221 D(bug("SCSICmd len %d, cmd %08x%08x%08x\n", cmd_length, ntohl(0[(uint32 *)cmd]), ntohl(1[(uint32 *)cmd]), ntohl(2[(uint32 *)cmd])));
222 if (phase != PH_SELECTED)
223 return scPhaseErr;
224
225 // Commdn length valid?
226 if (cmd_length != 6 && cmd_length != 10 && cmd_length != 12)
227 return scBadParmsErr;
228
229 // Set command, extract LUN
230 scsi_set_cmd(cmd_length, cmd);
231
232 // Extract LUN, set target
233 if (!scsi_set_target(target_id, (cmd[1] >> 5) & 7)) {
234 phase = PH_FREE;
235 fake_status = 0x0000; // Bus free
236 return scCommErr;
237 }
238
239 phase = PH_TRANSFER;
240 fake_status = 0x006e; // Target selected, data phase
241 return 0;
242 }
243
244
245 /*
246 * Read data
247 */
248
249 int16 SCSIRead(uint32 tib)
250 {
251 D(bug("SCSIRead TIB %08lx\n", tib));
252 if (phase != PH_TRANSFER)
253 return scPhaseErr;
254
255 // Execute TIB, fill S/G table
256 reading = true;
257 return exec_tib(tib);
258 }
259
260
261 /*
262 * Write data
263 */
264
265 int16 SCSIWrite(uint32 tib)
266 {
267 D(bug("SCSIWrite TIB %08lx\n", tib));
268 if (phase != PH_TRANSFER)
269 return scPhaseErr;
270
271 // Execute TIB, fill S/G table
272 return exec_tib(tib);
273 }
274
275
276 /*
277 * Wait for command completion (we're actually doing everything in here...)
278 */
279
280 int16 SCSIComplete(uint32 timeout, uint32 message, uint32 stat)
281 {
282 D(bug("SCSIComplete wait %d, msg %08lx, stat %08lx\n", timeout, message, stat));
283 WriteMacInt16(message, 0);
284 if (phase != PH_TRANSFER)
285 return scPhaseErr;
286
287 // Send command, process S/G table
288 uint16 scsi_stat = 0;
289 bool success = scsi_send_cmd(sg_total_length, reading, sg_index, sg_ptr, sg_len, &scsi_stat, timeout);
290 WriteMacInt16(stat, scsi_stat);
291
292 // Complete command
293 phase = PH_FREE;
294 fake_status = 0x0000; // Bus free
295 return success ? 0 : scCommErr;
296 }
297
298
299 /*
300 * Get bus status
301 */
302
303 uint16 SCSIStat(void)
304 {
305 D(bug("SCSIStat returns %04x\n", fake_status));
306 return fake_status;
307 }
308
309
310 /*
311 * SCSI Manager busy?
312 */
313
314 int16 SCSIMgrBusy(void)
315 {
316 // D(bug("SCSIMgrBusy\n"));
317 return phase != PH_FREE;
318 }