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