revert between 56095 -> 55830 in arch
[AROS.git] / rom / devs / ahci / ahci_io.c
blob4cc20078f21d8db5d7bfa301e507f73a6509fb1e
1 /*
2 Copyright © 2004-2018, The AROS Development Team. All rights reserved
3 $Id$
5 Desc:
6 Lang: English
7 */
9 /* Maintainer: Jason S. McMullan <jason.mcmullan@gmail.com>
12 #include <aros/debug.h>
14 #include <proto/exec.h>
16 /* We want all other bases obtained from our base */
17 #define __NOLIBBASE__
19 #include <aros/atomic.h>
20 #include <aros/symbolsets.h>
21 #include <exec/exec.h>
22 #include <exec/resident.h>
23 #include <exec/tasks.h>
24 #include <exec/memory.h>
25 #include <exec/nodes.h>
26 #include <utility/utility.h>
27 #include <libraries/expansion.h>
28 #include <libraries/configvars.h>
29 #include <devices/trackdisk.h>
30 #include <devices/newstyle.h>
31 #include <dos/bptr.h>
32 #include <dos/dosextens.h>
33 #include <dos/filehandler.h>
35 #include <proto/timer.h>
36 #include <proto/bootloader.h>
37 #include <proto/expansion.h>
38 #include <proto/oop.h>
40 #include <hidd/pci.h>
42 #include <string.h>
44 #include "ahci.h"
45 #include "ahci_scsi.h"
46 #include "timer.h"
48 #include LC_LIBDEFS_FILE
50 /* Translate to a SCSI command, since the
51 * backend will make that work on both ATAPI
52 * and ATA devices.
54 * Return whether or not we are completed.
56 static BOOL ahci_sector_rw(struct IORequest *io, UQUAD off64, BOOL is_write)
58 struct IOExtTD *iotd = (struct IOExtTD *)io;
59 struct cam_sim *unit = (struct cam_sim *)io->io_Unit;
60 struct ahci_port *ap = unit->sim_Port;
61 struct ata_port *at = ap->ap_ata[0];
62 APTR data = iotd->iotd_Req.io_Data;
63 ULONG len = iotd->iotd_Req.io_Length;
64 ULONG sector_size, bmask;
65 BOOL done = TRUE;
66 /* It's safe to allocate these on the stack, since they
67 * will never be referenced once the ahci_scsi_*_io()
68 * routine returns.
70 struct SCSICmd scsi = {};
71 union scsi_cdb cdb = {};
73 if (ap->ap_type == ATA_PORT_T_DISK) {
74 sector_size = at->at_identify.sector_size;
75 } else if (ap->ap_type == ATA_PORT_T_ATAPI) {
76 // FIXME: Where should this come from?
77 sector_size = 2048;
78 } else {
79 io->io_Error = TDERR_DiskChanged;
80 return TRUE;
82 bmask = sector_size - 1;
84 if ((off64 & bmask) || bmask == 0 || data == NULL) {
85 io->io_Error = IOERR_BADADDRESS;
86 return TRUE;
88 if ((len & bmask) || len == 0) {
89 D(bug("[AHCI%02ld] IO %p Fault, io_Flags = %d, io_Command = %d, IOERR_BADLENGTH (len=0x%x, bmask=0x%x)\n", unit->sim_Unit, io, io->io_Flags, io->io_Command, len, bmask));
90 io->io_Error = IOERR_BADLENGTH;
91 return TRUE;
94 if (len == 0) {
95 IOStdReq(io)->io_Actual = 0;
96 return TRUE;
99 scsi.scsi_Data = data;
100 scsi.scsi_Length = len;
101 scsi.scsi_Flags = is_write ? SCSIF_WRITE : SCSIF_READ;
103 /* Make in units of sector size */
104 len /= sector_size;
105 off64 /= sector_size;
107 /* Set up the CDB, based on what the transfer size is */
108 scsi.scsi_Command = (APTR)&cdb;
110 if (off64 < (1 << 21) && len < (1 << 8)) {
111 cdb.rw_6.opcode = is_write ? SCSI_DA_WRITE_6 : SCSI_DA_READ_6;
112 cdb.rw_6.addr[0] = (off64 >> 16) & 0x1f;
113 cdb.rw_6.addr[1] = (off64 >> 8) & 0xff;
114 cdb.rw_6.addr[2] = (off64 >> 0) & 0xff;
115 cdb.rw_6.length = len;
116 scsi.scsi_CmdLength = sizeof(cdb.rw_6);
117 } else if (off64 < (1ULL << 32) && len < (1 << 16)) {
118 cdb.rw_10.opcode = is_write ? SCSI_DA_WRITE_10 : SCSI_DA_READ_10;
119 cdb.rw_10.addr[0] = (off64 >> 24) & 0xff;
120 cdb.rw_10.addr[1] = (off64 >> 16) & 0xff;
121 cdb.rw_10.addr[2] = (off64 >> 8) & 0xff;
122 cdb.rw_10.addr[3] = (off64 >> 0) & 0xff;
123 cdb.rw_10.length[0] = (len >> 8) & 0xff;
124 cdb.rw_10.length[1] = (len >> 0) & 0xff;
125 scsi.scsi_CmdLength = sizeof(cdb.rw_10);
126 } else if (off64 < (1ULL << 32)) {
127 cdb.rw_12.opcode = is_write ? SCSI_DA_WRITE_12 : SCSI_DA_READ_12;
128 cdb.rw_12.addr[0] = (off64 >> 24) & 0xff;
129 cdb.rw_12.addr[1] = (off64 >> 16) & 0xff;
130 cdb.rw_12.addr[2] = (off64 >> 8) & 0xff;
131 cdb.rw_12.addr[3] = (off64 >> 0) & 0xff;
132 cdb.rw_12.length[0] = (len >> 24) & 0xff;
133 cdb.rw_12.length[1] = (len >> 16) & 0xff;
134 cdb.rw_12.length[2] = (len >> 8) & 0xff;
135 cdb.rw_12.length[3] = (len >> 0) & 0xff;
136 scsi.scsi_CmdLength = sizeof(cdb.rw_12);
137 } else {
138 cdb.rw_16.opcode = is_write ? SCSI_DA_WRITE_16 : SCSI_DA_READ_16;
139 cdb.rw_16.addr[0] = (off64 >> 56) & 0xff;
140 cdb.rw_16.addr[1] = (off64 >> 48) & 0xff;
141 cdb.rw_16.addr[2] = (off64 >> 40) & 0xff;
142 cdb.rw_16.addr[3] = (off64 >> 32) & 0xff;
143 cdb.rw_16.addr[4] = (off64 >> 24) & 0xff;
144 cdb.rw_16.addr[5] = (off64 >> 16) & 0xff;
145 cdb.rw_16.addr[6] = (off64 >> 8) & 0xff;
146 cdb.rw_16.addr[7] = (off64 >> 0) & 0xff;
147 cdb.rw_16.length[0] = (len >> 24) & 0xff;
148 cdb.rw_16.length[1] = (len >> 16) & 0xff;
149 cdb.rw_16.length[2] = (len >> 8) & 0xff;
150 cdb.rw_16.length[3] = (len >> 0) & 0xff;
151 scsi.scsi_CmdLength = sizeof(cdb.rw_16);
154 if (ap->ap_type == ATA_PORT_T_DISK)
155 done = ahci_scsi_disk_io(io, &scsi);
156 else if (ap->ap_type == ATA_PORT_T_ATAPI)
157 done = ahci_scsi_atapi_io(io, &scsi);
158 else
159 io->io_Error = IOERR_NOCMD;
161 return done;
165 Try to do IO commands. All commands which require talking with ahci devices
166 will be handled slow, that is they will be passed to bus task which will
167 execute them as soon as hardware will be free.
169 AROS_LH1(void, BeginIO,
170 AROS_LHA(struct IORequest *, io, A1),
171 LIBBASETYPEPTR, LIBBASE, 5, ahci)
173 AROS_LIBFUNC_INIT
175 const UWORD NSDSupported[] = {
176 CMD_READ,
177 CMD_WRITE,
178 CMD_UPDATE,
179 CMD_CLEAR,
180 TD_ADDCHANGEINT,
181 TD_CHANGENUM,
182 TD_CHANGESTATE,
183 TD_EJECT,
184 TD_FORMAT,
185 TD_GETGEOMETRY,
186 TD_MOTOR,
187 TD_PROTSTATUS,
188 TD_READ64,
189 TD_REMCHANGEINT,
190 TD_WRITE64,
191 NSCMD_DEVICEQUERY,
192 NSCMD_TD_READ64,
193 NSCMD_TD_WRITE64,
196 struct IOExtTD *iotd = (struct IOExtTD *)io;
197 struct cam_sim *unit = (struct cam_sim *)io->io_Unit;
198 struct ahci_port *ap = unit->sim_Port;
199 struct ata_port *at = ap->ap_ata[0];
200 APTR data = iotd->iotd_Req.io_Data;
201 ULONG len = iotd->iotd_Req.io_Length;
202 UQUAD off64;
203 struct DriveGeometry *geom;
204 struct NSDeviceQueryResult *nsqr;
205 BOOL done = (io->io_Flags & IOF_QUICK) ? TRUE : FALSE;
207 io->io_Message.mn_Node.ln_Type = NT_MESSAGE;
208 io->io_Error = 0;
210 D(bug("[AHCI%02ld] IO %p Start, io_Flags = %d, io_Command = %d\n", unit->sim_Unit, io, io->io_Flags, io->io_Command));
212 /* Unit going offline? Don't permit new commands. */
213 if (unit->sim_Flags & SIMF_OffLine) {
214 io->io_Error = IOERR_OPENFAIL;
215 if (!(io->io_Flags & IOF_QUICK))
216 ReplyMsg(&io->io_Message);
217 return;
220 ObtainSemaphore(&unit->sim_Lock);
221 AddHead(&unit->sim_IOs, &io->io_Message.mn_Node);
222 ReleaseSemaphore(&unit->sim_Lock);
224 switch (io->io_Command) {
225 case NSCMD_DEVICEQUERY:
226 if (len < sizeof(*nsqr))
227 goto bad_length;
229 nsqr = data;
230 nsqr->DevQueryFormat = 0;
231 nsqr->SizeAvailable = sizeof(struct NSDeviceQueryResult);
232 nsqr->DeviceType = NSDEVTYPE_TRACKDISK;
233 nsqr->DeviceSubType = 0;
234 nsqr->SupportedCommands = (UWORD *)NSDSupported;
235 IOStdReq(io)->io_Actual = sizeof(*nsqr);
236 done = TRUE;
237 break;
238 case TD_PROTSTATUS:
239 IOStdReq(io)->io_Actual = (ap->ap_type == ATA_PORT_T_DISK) ? 0 : -1;
240 done = TRUE;
241 break;
242 case TD_CHANGENUM:
243 IOStdReq(io)->io_Actual = unit->sim_ChangeNum;
244 done = TRUE;
245 break;
246 case TD_CHANGESTATE:
247 IOStdReq(io)->io_Actual = (unit->sim_Flags & SIMF_MediaPresent) ? 0 : -1;
248 done = TRUE;
249 break;
250 case TD_EJECT:
252 if (at->at_identify.config & (1 << 7))
254 // FIXME: Eject removable media
256 done = TRUE;
257 break;
259 case TD_GETDRIVETYPE:
260 IOStdReq(io)->io_Actual = DRIVE_NEWSTYLE;
261 done = TRUE;
262 break;
263 case TD_GETGEOMETRY:
264 if (len < sizeof(*geom))
265 goto bad_length;
267 if (ap->ap_type != ATA_PORT_T_DISK && ap->ap_type != ATA_PORT_T_ATAPI)
268 goto bad_cmd;
270 geom = data;
271 memset(geom, 0, len);
272 if (ap->ap_type == ATA_PORT_T_DISK) {
273 geom->dg_SectorSize = at->at_identify.sector_size;
274 geom->dg_TotalSectors = 0;
275 geom->dg_Cylinders = at->at_identify.ncyls;
276 geom->dg_CylSectors = at->at_identify.nsectors * at->at_identify.nheads;
277 geom->dg_Heads = at->at_identify.nheads;
278 geom->dg_TrackSectors = at->at_identify.nsectors;
280 geom->dg_BufMemType = MEMF_PUBLIC;
281 geom->dg_DeviceType = (ap->ap_type == ATA_PORT_T_ATAPI) ? DG_CDROM : DG_DIRECT_ACCESS;
282 geom->dg_Flags = (at->at_identify.config & (1 << 7)) ? DGF_REMOVABLE : 0;
283 IOStdReq(io)->io_Actual = sizeof(*geom);
284 done = TRUE;
285 break;
286 case TD_FORMAT:
287 if (len & (at->at_identify.nsectors * at->at_identify.sector_size - 1))
288 goto bad_length;
289 off64 = iotd->iotd_Req.io_Offset;
290 if (off64 & (at->at_identify.nsectors * at->at_identify.sector_size - 1))
291 goto bad_address;
292 done = ahci_sector_rw(io, off64, TRUE);
293 break;
294 case TD_MOTOR:
295 // FIXME: Tie in with power management
296 IOStdReq(io)->io_Actual = 1;
297 done = TRUE;
298 break;
299 case CMD_WRITE:
300 off64 = iotd->iotd_Req.io_Offset;
301 done = ahci_sector_rw(io, off64, TRUE);
302 break;
303 case TD_WRITE64:
304 case NSCMD_TD_WRITE64:
305 off64 = iotd->iotd_Req.io_Offset;
306 off64 |= ((UQUAD)iotd->iotd_Req.io_Actual)<<32;
307 done = ahci_sector_rw(io, off64, TRUE);
308 break;
309 case CMD_READ:
310 off64 = iotd->iotd_Req.io_Offset;
311 done = ahci_sector_rw(io, off64, FALSE);
312 break;
313 case TD_READ64:
314 case NSCMD_TD_READ64:
315 off64 = iotd->iotd_Req.io_Offset;
316 off64 |= ((UQUAD)iotd->iotd_Req.io_Actual)<<32;
317 done = ahci_sector_rw(io, off64, FALSE);
318 break;
319 case HD_SCSICMD:
320 if (sizeof(struct SCSICmd) != len)
321 goto bad_length;
322 if (ap->ap_type == ATA_PORT_T_DISK)
323 done = ahci_scsi_disk_io(io, data);
324 else if (ap->ap_type == ATA_PORT_T_ATAPI)
325 done = ahci_scsi_atapi_io(io, data);
326 else
327 goto bad_cmd;
328 break;
329 case TD_ADDCHANGEINT:
330 if (io->io_Flags & IOF_QUICK)
331 goto bad_cmd;
332 break;
333 case TD_REMCHANGEINT:
334 if (io->io_Flags & IOF_QUICK)
335 goto bad_cmd;
336 done = TRUE;
337 break;
338 case CMD_CLEAR:
339 // FIXME: Implemennt cache invalidate
340 done = TRUE;
341 break;
342 case CMD_UPDATE:
343 // FIXME: Implement cache flush
344 done = TRUE;
345 break;
346 default:
347 bug("ahci.device %d: Unknown IO command %d\n", unit->sim_Unit, io->io_Command);
348 bad_cmd:
349 io->io_Error = IOERR_NOCMD;
350 done = TRUE;
351 break;
352 bad_length:
353 D(bug("[AHCI%02ld] IO %p Fault, io_Flags = %d, io_Command = %d, IOERR_BADLENGTH (len = %d)\n", unit->sim_Unit, io, io->io_Flags, io->io_Command, len));
354 io->io_Error = IOERR_BADLENGTH;
355 done = TRUE;
356 break;
357 bad_address:
358 io->io_Error = IOERR_BADADDRESS;
359 done = TRUE;
360 break;
363 /* The IO is finished, so no need to keep it around anymore */
364 if (done) {
365 ObtainSemaphore(&unit->sim_Lock);
366 Remove(&io->io_Message.mn_Node);
367 ReleaseSemaphore(&unit->sim_Lock);
368 } else
369 io->io_Flags &= ~IOF_QUICK;
372 /* Need a reply now? */
373 if (done && !(io->io_Flags & IOF_QUICK))
374 ReplyMsg(&io->io_Message);
376 if (done)
377 D(bug("[AHCI%02ld] IO %p Quick, io_Flags = %d, io_Comand = %d, io_Error = %d\n", unit->sim_Unit, io, io->io_Flags, io->io_Command, io->io_Error));
379 AROS_LIBFUNC_EXIT
382 AROS_LH1(LONG, AbortIO,
383 AROS_LHA(struct IORequest *, io, A1),
384 LIBBASETYPEPTR, LIBBASE, 6, ahci)
386 AROS_LIBFUNC_INIT
388 /* Aborting IOs is not (yet) supported */
389 return 0;
391 AROS_LIBFUNC_EXIT
395 /* vim: set ts=8 sts=4 et : */