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