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, 10 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * scsi.cpp - SCSI Manager
3     *
4 gbeauche 1.8 * Basilisk II (C) 1997-2008 Christian Bauer
5 cebix 1.1 *
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 cebix 1.5 #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 cebix 1.1
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 cebix 1.5 #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 cebix 1.1 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     }