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

# User Rev Content
1 cebix 1.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     }