- HD_SCSICMD: assume there is no sense-data buffer if no AUTOSENSE
[AROS.git] / rom / devs / ata / scsiemu.c
blobfdcfc6ebed2e9b71f80e2769a17371abc3fe6527
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;
25 static void ww(UBYTE *p, UWORD v)
27 p[0] = v >> 8;
28 p[1] = v;
30 static ULONG rl(UBYTE *p)
32 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
35 static UBYTE scsi_read32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
37 return unit->au_Read32(unit, offset, len, data, outlen);
39 static UBYTE scsi_write32(struct ata_Unit *unit, APTR data, ULONG offset, ULONG len, ULONG *outlen)
41 return unit->au_Write32(unit, offset, len, data, outlen);
44 static UBYTE scsi_inquiry(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
46 UBYTE *cmdbuf = cmd->scsi_Command;
47 UBYTE *out = (UBYTE*)cmd->scsi_Data;
48 UBYTE len;
50 if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
51 return 0xff;
52 len = cmdbuf[4];
53 if (cmdbuf[1] >> 5)
54 return 0xff; /* no lun supported */
55 out[2] = 2; /* supports SCSI-2 */
56 out[3] = 2; /* response data format */
57 out[4] = 32; /* additional length */
58 out[7] = 0x20; /* 16 bit bus */
59 *outlen = len < 36 ? len : 36;
60 memset(out + 8, ' ', 8 + 16 + 4);
61 CopyMem(unit->au_Model, out + 8, strlen(unit->au_Model) > 16 + 8 ? 16 + 8 : strlen(unit->au_Model));
62 CopyMem(unit->au_FirmwareRev, out + 8 + 16, strlen(unit->au_FirmwareRev) > 4 ? 4 : strlen(unit->au_FirmwareRev));
63 return 0;
66 static UBYTE scsi_modesense(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
68 UBYTE *cmdbuf = cmd->scsi_Command;
69 UBYTE *out = (UBYTE*)cmd->scsi_Data;
70 UBYTE pcode = cmdbuf[2] & 0x3f;
71 UBYTE dbd = cmdbuf[1] & 8;
72 UWORD blocksize = 1 << unit->au_SectorShift;
73 UBYTE *p;
75 p = out;
76 p[0] = 4 - 1;
77 p[1] = 0;
78 p[2] = 0;
79 p[3] = 0;
80 p += 4;
81 if (!dbd) {
82 p[-1] = 8;
83 wl(out + 0, unit->au_Capacity);
84 wl(out + 4, blocksize);
85 p += 8;
87 if (pcode == 0) {
88 p[0] = 0;
89 p[1] = 0;
90 p[2] = 0x20;
91 p[3] = 0;
92 out[0] += 4;
93 } else if (pcode == 3) {
94 p[0] = 3;
95 p[1] = 24;
96 p[3] = 1;
97 p[10] = unit->au_Sectors >> 8;
98 p[11] = unit->au_Sectors;
99 p[12] = blocksize >> 8;
100 p[13] = blocksize;
101 p[15] = 1; // interleave
102 p[20] = 0x80;
103 out[0] += p[1];
104 } else if (pcode == 4) {
105 p[0] = 4;
106 wl(p + 1, unit->au_Cylinders);
107 p[1] = 24;
108 p[5] = unit->au_Heads;
109 wl(p + 13, unit->au_Cylinders);
110 ww(p + 20, 5400);
111 out[0] += p[1];
112 } else {
113 return 0xff;
115 out[0] += out[3];
116 *outlen = out[0] + 1;
117 return 0;
120 static UBYTE scsi_readcapacity(struct ata_Unit *unit, struct SCSICmd *cmd, ULONG *outlen)
122 UBYTE *cmdbuf = cmd->scsi_Command;
123 UBYTE *out = (UBYTE*)cmd->scsi_Data;
124 BOOL pmi;
125 ULONG lba, blocks;
127 blocks = unit->au_Capacity;
128 pmi = cmdbuf[8] & 1;
129 lba = (cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5];
130 if (pmi == 0 && lba != 0)
131 return 0xff;
132 if (pmi) {
133 UWORD cylsize = unit->au_Heads * unit->au_Sectors;
134 lba += cylsize;
135 lba /= cylsize;
136 lba *= cylsize;
137 if (lba > blocks)
138 lba = blocks;
139 blocks = lba;
141 wl (out + 0, blocks);
142 wl (out + 4, 1 << unit->au_SectorShift);
143 *outlen = 8;
144 return 0;
147 BYTE SCSIEmu(struct ata_Unit *unit, struct SCSICmd *cmd)
149 ULONG len, offset;
150 ULONG scsi_len;
151 UWORD scsi_sense_len = (cmd->scsi_Flags & (1 << SCSIB_OLDAUTOSENSE)) ? 4 :
152 (cmd->scsi_Flags & (1 << SCSIB_AUTOSENSE)) ? cmd->scsi_SenseLength : 0;
153 UBYTE *cmdbuf = cmd->scsi_Command;
154 UBYTE sense[32];
155 UWORD senselen;
156 UBYTE err, status;
158 /* bug("SCSIEMU CMD=%02x\n", cmdbuf[0]); */
159 err = 0;
160 status = 0;
161 scsi_len = 0;
162 senselen = 0;
163 if (scsi_sense_len > sizeof sense)
164 scsi_sense_len = sizeof sense;
165 switch(cmdbuf[0])
167 case 0x00: /* TEST UNIT READY */
168 break;
170 case 0x08: /* READ (6) */
171 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
172 len = cmdbuf[4];
173 if (!len)
174 len = 256;
175 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
176 break;
177 case 0x28: /* READ (10) */
178 offset = rl(cmdbuf + 2);
179 len = rl(cmdbuf + 7 - 2) & 0xffff;
180 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
181 break;
182 case 0xa8: /* READ (12) */
183 offset = rl(cmdbuf + 2);
184 len = rl(cmdbuf + 6);
185 err = scsi_read32(unit, cmd->scsi_Data, offset, len, &scsi_len);
186 break;
187 case 0x0a: /* WRITE (6) */
188 offset = ((cmdbuf[1] & 31) << 16) | (cmdbuf[2] << 8) | cmdbuf[3];
189 len = cmdbuf[4];
190 if (!len)
191 len = 256;
192 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
193 break;
194 case 0x2a: /* WRITE (10) */
195 offset = rl(cmdbuf + 2);
196 len = rl(cmdbuf + 7 - 2) & 0xffff;
197 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
198 break;
199 case 0xaa: /* WRITE (12) */
200 offset = rl(cmdbuf + 2);
201 len = rl(cmdbuf + 6);
202 err = scsi_write32(unit, cmd->scsi_Data, offset, len, &scsi_len);
203 break;
205 case 0x37: /* READ DEFECT DATA */
206 status = 2;
207 senselen = 32;
208 memset(sense, 0, senselen);
209 sense[0] = 0x70;
210 sense[2] = 0x00;
211 sense[12] = 0x1c;
212 break;
214 case 0x12: /* INQUIRY */
215 err = scsi_inquiry(unit, cmd, &scsi_len);
216 break;
217 case 0x1a: /* MODE SENSE(6) */
218 err = scsi_modesense(unit, cmd, &scsi_len);
219 break;
220 case 0x25: /* READ CAPACITY */
221 err = scsi_readcapacity(unit, cmd, &scsi_len);
222 break;
224 case 0x1d: /* SEND DIAGNOSTICS */
225 case 0x35: /* SYNCHRONIZE CACHE */
226 break;
228 default:
229 err = 0xff;
230 break;
233 if (err == 0xff) {
234 status = 2; /* CHECK CONDITION */
235 senselen = 32;
236 memset(sense, 0, senselen);
237 sense[0] = 0x70;
238 sense[2] = 5; /* ILLEGAL REQUEST */
239 sense[12] = 0x24; /* ILLEGAL FIELD IN CDB */
240 err = TDERR_NotSpecified;
243 if (senselen && scsi_sense_len) {
244 if (senselen > scsi_sense_len)
245 senselen = scsi_sense_len;
246 CopyMem(sense, cmd->scsi_SenseData, senselen);
247 cmd->scsi_SenseActual = senselen;
249 cmd->scsi_Status = status;
250 cmd->scsi_CmdActual = status != 0 ? 0 : cmd->scsi_CmdLength;
251 cmd->scsi_Actual = scsi_len;
253 return err;