Listtree.mcc: update version and date
[AROS.git] / rom / devs / ata / scsiemu.c
blob066dcc2c25881972d9163f1349d5cb08dcb95e66
1 /*
2 Copyright © 2011-2012, The AROS Development Team. All rights reserved
3 $Id$
5 Desc: Simple HD_SCSICMD emulator.
6 Lang: English
7 */
9 #include <aros/debug.h>
11 #include <exec/types.h>
12 #include <exec/exec.h>
13 #include <proto/exec.h>
14 #include <devices/scsidisk.h>
16 #include "ata.h"
18 static void wl(UBYTE *p, ULONG v)
20 p[0] = v >> 24;
21 p[1] = v >> 16;
22 p[2] = v >> 8;
23 p[3] = v;
26 static void ww(UBYTE *p, UWORD v)
28 p[0] = v >> 8;
29 p[1] = v;
32 static ULONG rl(UBYTE *p)
34 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
37 static UBYTE scsi_read32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
39 return unit->au_Read32(unit, offset, len, data, outlen);
42 static UBYTE scsi_write32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
44 return unit->au_Write32(unit, offset, len, data, outlen);
47 static UBYTE scsi_inquiry(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
49 UBYTE *cmdbuf = cmd->scsi_Command;
50 UBYTE *out = (UBYTE*)cmd->scsi_Data;
51 UBYTE len;
53 if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
54 return 0xff;
55 len = cmdbuf[4];
56 if (cmdbuf[1] >> 5)
57 return 0xff; /* no lun supported */
58 out[2] = 2; /* supports SCSI-2 */
59 out[3] = 2; /* response data format */
60 out[4] = 32; /* additional length */
61 out[7] = 0x20; /* 16 bit bus */
62 *outlen = len < 36 ? len : 36;
63 memset(out + 8, ' ', 8 + 16 + 4);
64 CopyMem(unit->au_Model, out + 8, strlen(unit->au_Model) > 16 + 8 ? 16 + 8 : strlen(unit->au_Model));
65 CopyMem(unit->au_FirmwareRev, out + 8 + 16, strlen(unit->au_FirmwareRev) > 4 ? 4 : strlen(unit->au_FirmwareRev));
66 return 0;
69 static UBYTE scsi_modesense(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
71 UBYTE *cmdbuf = cmd->scsi_Command;
72 UBYTE *out = (UBYTE*)cmd->scsi_Data;
73 UBYTE pcode = cmdbuf[2] & 0x3f;
74 UBYTE dbd = cmdbuf[1] & 8;
75 UWORD blocksize = 1 << unit->au_SectorShift;
76 UBYTE *p;
78 p = out;
79 p[0] = 4 - 1;
80 p[1] = 0;
81 p[2] = 0;
82 p[3] = 0;
83 p += 4;
84 if (!dbd) {
85 p[-1] = 8;
86 wl(out + 0, unit->au_Capacity);
87 wl(out + 4, blocksize);
88 p += 8;
90 if (pcode == 0) {
91 p[0] = 0;
92 p[1] = 0;
93 p[2] = 0x20;
94 p[3] = 0;
95 out[0] += 4;
96 } else if (pcode == 3) {
97 p[0] = 3;
98 p[1] = 24;
99 p[3] = 1;
100 p[10] = unit->au_Sectors >> 8;
101 p[11] = unit->au_Sectors;
102 p[12] = blocksize >> 8;
103 p[13] = blocksize;
104 p[15] = 1; // interleave
105 p[20] = 0x80;
106 out[0] += p[1];
107 } else if (pcode == 4) {
108 p[0] = 4;
109 wl(p + 1, unit->au_Cylinders);
110 p[1] = 24;
111 p[5] = unit->au_Heads;
112 wl(p + 13, unit->au_Cylinders);
113 ww(p + 20, 5400);
114 out[0] += p[1];
115 } else {
116 return 0xff;
118 out[0] += out[3];
119 *outlen = out[0] + 1;
120 return 0;
123 static UBYTE scsi_readcapacity(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
125 UBYTE *cmdbuf = cmd->scsi_Command;
126 UBYTE *out = (UBYTE*)cmd->scsi_Data;
127 BOOL pmi;
128 ULONG lba, blocks;
130 blocks = unit->au_Capacity;
131 pmi = cmdbuf[8] & 1;
132 lba = (cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5];
133 if (pmi == 0 && lba != 0)
134 return 0xff;
135 if (pmi) {
136 UWORD cylsize = unit->au_Heads * unit->au_Sectors;
137 lba += cylsize;
138 lba /= cylsize;
139 lba *= cylsize;
140 if (lba > blocks)
141 lba = blocks;
142 blocks = lba;
144 wl (out + 0, blocks);
145 wl (out + 4, 1 << unit->au_SectorShift);
146 *outlen = 8;
147 return 0;
150 BYTE SCSIEmu(struct ata_Unit *unit, struct SCSICmd *cmd)
152 ULONG len, offset;
153 ULONG scsi_len;
154 UWORD scsi_sense_len = (cmd->scsi_Flags & (1 << SCSIB_OLDAUTOSENSE)) ? 4 :
155 (cmd->scsi_Flags & (1 << SCSIB_AUTOSENSE)) ? cmd->scsi_SenseLength : 0;
156 UBYTE *cmdbuf = cmd->scsi_Command;
157 UBYTE sense[32];
158 UWORD senselen;
159 UBYTE err, status;
161 /* bug("SCSIEMU CMD=%02x\n", cmdbuf[0]); */
162 err = 0;
163 status = 0;
164 scsi_len = 0;
165 senselen = 0;
166 if (scsi_sense_len > sizeof sense)
167 scsi_sense_len = sizeof sense;
168 switch(cmdbuf[0])
170 case 0x00: /* TEST UNIT READY */
171 break;
173 case 0x08: /* READ (6) */
174 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
175 len = cmdbuf[4];
176 if (!len)
177 len = 256;
178 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
179 break;
180 case 0x28: /* READ (10) */
181 offset = rl(cmdbuf + 2);
182 len = rl(cmdbuf + 7 - 2) & 0xffff;
183 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
184 break;
185 case 0xa8: /* READ (12) */
186 offset = rl(cmdbuf + 2);
187 len = rl(cmdbuf + 6);
188 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
189 break;
190 case 0x0a: /* WRITE (6) */
191 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
192 len = cmdbuf[4];
193 if (!len)
194 len = 256;
195 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
196 break;
197 case 0x2a: /* WRITE (10) */
198 offset = rl(cmdbuf + 2);
199 len = rl(cmdbuf + 7 - 2) & 0xffff;
200 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
201 break;
202 case 0xaa: /* WRITE (12) */
203 offset = rl(cmdbuf + 2);
204 len = rl(cmdbuf + 6);
205 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
206 break;
208 case 0x37: /* READ DEFECT DATA */
209 status = 2;
210 senselen = 32;
211 memset(sense, 0, senselen);
212 sense[0] = 0x70;
213 sense[2] = 0x00;
214 sense[12] = 0x1c;
215 break;
217 case 0x12: /* INQUIRY */
218 err = scsi_inquiry(unit, cmd, &scsi_len);
219 break;
220 case 0x1a: /* MODE SENSE(6) */
221 err = scsi_modesense(unit, cmd, &scsi_len);
222 break;
223 case 0x25: /* READ CAPACITY */
224 err = scsi_readcapacity(unit, cmd, &scsi_len);
225 break;
227 case 0x1d: /* SEND DIAGNOSTICS */
228 case 0x35: /* SYNCHRONIZE CACHE */
229 break;
231 default:
232 err = 0xff;
233 break;
236 if (err == 0xff) {
237 status = 2; /* CHECK CONDITION */
238 senselen = 32;
239 memset(sense, 0, senselen);
240 sense[0] = 0x70;
241 sense[2] = 5; /* ILLEGAL REQUEST */
242 sense[12] = 0x24; /* ILLEGAL FIELD IN CDB */
243 err = TDERR_NotSpecified;
246 if (senselen && scsi_sense_len) {
247 if (senselen > scsi_sense_len)
248 senselen = scsi_sense_len;
249 CopyMem(sense, cmd->scsi_SenseData, senselen);
250 cmd->scsi_SenseActual = senselen;
252 cmd->scsi_Status = status;
253 cmd->scsi_CmdActual = status != 0 ? 0 : cmd->scsi_CmdLength;
254 cmd->scsi_Actual = scsi_len;
256 return err;