revert between 56095 -> 55830 in arch
[AROS.git] / rom / devs / ata / scsiemu.c
blob3d2f7f5c02f63f466beb31f3066fe903ef8a66a6
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 <proto/exec.h>
13 #include <exec/types.h>
14 #include <exec/exec.h>
15 #include <devices/scsidisk.h>
17 #include "ata.h"
19 static void wl(UBYTE *p, ULONG v)
21 p[0] = v >> 24;
22 p[1] = v >> 16;
23 p[2] = v >> 8;
24 p[3] = v;
27 static void ww(UBYTE *p, UWORD v)
29 p[0] = v >> 8;
30 p[1] = v;
33 static ULONG rl(UBYTE *p)
35 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
38 static UBYTE scsi_read32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
40 return unit->au_Read32(unit, offset, len, data, outlen);
43 static UBYTE scsi_write32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
45 return unit->au_Write32(unit, offset, len, data, outlen);
48 static UBYTE scsi_inquiry(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
50 UBYTE *cmdbuf = cmd->scsi_Command;
51 UBYTE *out = (UBYTE*)cmd->scsi_Data;
52 UBYTE len;
54 if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
55 return 0xff;
56 len = cmdbuf[4];
57 if (cmdbuf[1] >> 5)
58 return 0xff; /* no lun supported */
59 out[2] = 2; /* supports SCSI-2 */
60 out[3] = 2; /* response data format */
61 out[4] = 32; /* additional length */
62 out[7] = 0x20; /* 16 bit bus */
63 *outlen = len < 36 ? len : 36;
64 memset(out + 8, ' ', 8 + 16 + 4);
65 CopyMem(unit->au_Model, out + 8, strlen(unit->au_Model) > 16 + 8 ? 16 + 8 : strlen(unit->au_Model));
66 CopyMem(unit->au_FirmwareRev, out + 8 + 16, strlen(unit->au_FirmwareRev) > 4 ? 4 : strlen(unit->au_FirmwareRev));
67 return 0;
70 static UBYTE scsi_modesense(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
72 UBYTE *cmdbuf = cmd->scsi_Command;
73 UBYTE *out = (UBYTE*)cmd->scsi_Data;
74 UBYTE pcode = cmdbuf[2] & 0x3f;
75 UBYTE dbd = cmdbuf[1] & 8;
76 UWORD blocksize = 1 << unit->au_SectorShift;
77 UBYTE *p;
79 p = out;
80 p[0] = 4 - 1;
81 p[1] = 0;
82 p[2] = 0;
83 p[3] = 0;
84 p += 4;
85 if (!dbd) {
86 p[-1] = 8;
87 wl(out + 0, unit->au_Capacity);
88 wl(out + 4, blocksize);
89 p += 8;
91 if (pcode == 0) {
92 p[0] = 0;
93 p[1] = 0;
94 p[2] = 0x20;
95 p[3] = 0;
96 out[0] += 4;
97 } else if (pcode == 3) {
98 p[0] = 3;
99 p[1] = 24;
100 p[3] = 1;
101 p[10] = unit->au_Sectors >> 8;
102 p[11] = unit->au_Sectors;
103 p[12] = blocksize >> 8;
104 p[13] = blocksize;
105 p[15] = 1; // interleave
106 p[20] = 0x80;
107 out[0] += p[1];
108 } else if (pcode == 4) {
109 p[0] = 4;
110 wl(p + 1, unit->au_Cylinders);
111 p[1] = 24;
112 p[5] = unit->au_Heads;
113 wl(p + 13, unit->au_Cylinders);
114 ww(p + 20, 5400);
115 out[0] += p[1];
116 } else {
117 return 0xff;
119 out[0] += out[3];
120 *outlen = out[0] + 1;
121 return 0;
124 static UBYTE scsi_readcapacity(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
126 UBYTE *cmdbuf = cmd->scsi_Command;
127 UBYTE *out = (UBYTE*)cmd->scsi_Data;
128 BOOL pmi;
129 ULONG lba, blocks;
131 blocks = unit->au_Capacity;
132 pmi = cmdbuf[8] & 1;
133 lba = (cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5];
134 if (pmi == 0 && lba != 0)
135 return 0xff;
136 if (pmi) {
137 UWORD cylsize = unit->au_Heads * unit->au_Sectors;
138 lba += cylsize;
139 lba /= cylsize;
140 lba *= cylsize;
141 if (lba > blocks)
142 lba = blocks;
143 blocks = lba;
145 wl (out + 0, blocks);
146 wl (out + 4, 1 << unit->au_SectorShift);
147 *outlen = 8;
148 return 0;
151 BYTE SCSIEmu(struct ata_Unit *unit, struct SCSICmd *cmd)
153 ULONG len, offset;
154 ULONG scsi_len;
155 UWORD scsi_sense_len = (cmd->scsi_Flags & (1 << SCSIB_OLDAUTOSENSE)) ? 4 :
156 (cmd->scsi_Flags & (1 << SCSIB_AUTOSENSE)) ? cmd->scsi_SenseLength : 0;
157 UBYTE *cmdbuf = cmd->scsi_Command;
158 UBYTE sense[32];
159 UWORD senselen;
160 UBYTE err, status;
162 /* bug("SCSIEMU CMD=%02x\n", cmdbuf[0]); */
163 err = 0;
164 status = 0;
165 scsi_len = 0;
166 senselen = 0;
167 if (scsi_sense_len > sizeof sense)
168 scsi_sense_len = sizeof sense;
169 switch(cmdbuf[0])
171 case 0x00: /* TEST UNIT READY */
172 break;
174 case 0x08: /* READ (6) */
175 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
176 len = cmdbuf[4];
177 if (!len)
178 len = 256;
179 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
180 break;
181 case 0x28: /* READ (10) */
182 offset = rl(cmdbuf + 2);
183 len = rl(cmdbuf + 7 - 2) & 0xffff;
184 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
185 break;
186 case 0xa8: /* READ (12) */
187 offset = rl(cmdbuf + 2);
188 len = rl(cmdbuf + 6);
189 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
190 break;
191 case 0x0a: /* WRITE (6) */
192 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
193 len = cmdbuf[4];
194 if (!len)
195 len = 256;
196 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
197 break;
198 case 0x2a: /* WRITE (10) */
199 offset = rl(cmdbuf + 2);
200 len = rl(cmdbuf + 7 - 2) & 0xffff;
201 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
202 break;
203 case 0xaa: /* WRITE (12) */
204 offset = rl(cmdbuf + 2);
205 len = rl(cmdbuf + 6);
206 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
207 break;
209 case 0x37: /* READ DEFECT DATA */
210 status = 2;
211 senselen = 32;
212 memset(sense, 0, senselen);
213 sense[0] = 0x70;
214 sense[2] = 0x00;
215 sense[12] = 0x1c;
216 break;
218 case 0x12: /* INQUIRY */
219 err = scsi_inquiry(unit, cmd, &scsi_len);
220 break;
221 case 0x1a: /* MODE SENSE(6) */
222 err = scsi_modesense(unit, cmd, &scsi_len);
223 break;
224 case 0x25: /* READ CAPACITY */
225 err = scsi_readcapacity(unit, cmd, &scsi_len);
226 break;
228 case 0x1d: /* SEND DIAGNOSTICS */
229 case 0x35: /* SYNCHRONIZE CACHE */
230 break;
232 default:
233 err = 0xff;
234 break;
237 if (err == 0xff) {
238 status = 2; /* CHECK CONDITION */
239 senselen = 32;
240 memset(sense, 0, senselen);
241 sense[0] = 0x70;
242 sense[2] = 5; /* ILLEGAL REQUEST */
243 sense[12] = 0x24; /* ILLEGAL FIELD IN CDB */
244 err = TDERR_NotSpecified;
247 if (senselen && scsi_sense_len) {
248 if (senselen > scsi_sense_len)
249 senselen = scsi_sense_len;
250 CopyMem(sense, cmd->scsi_SenseData, senselen);
251 cmd->scsi_SenseActual = senselen;
253 cmd->scsi_Status = status;
254 cmd->scsi_CmdActual = status != 0 ? 0 : cmd->scsi_CmdLength;
255 cmd->scsi_Actual = scsi_len;
257 return err;