2 Copyright © 2004-2011, The AROS Development Team. All rights reserved
9 /* Maintainer: Jason S. McMullan <jason.mcmullan@gmail.com>
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>
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>
42 #include "ahci_intern.h"
43 #include "ahci_scsi.h"
46 #include LC_LIBDEFS_FILE
48 /* Translate to a SCSI command, since the
49 * backend will make that work on both ATAPI
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
;
64 /* It's safe to allocate these on the stack, since they
65 * will never be referenced once the ahci_scsi_*_io()
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?
77 io
->io_Error
= TDERR_DiskChanged
;
80 bmask
= sector_size
- 1;
82 if ((off64
& bmask
) || bmask
== 0 || data
== NULL
) {
83 io
->io_Error
= IOERR_BADADDRESS
;
86 if ((len
& bmask
) || len
== 0) {
87 io
->io_Error
= IOERR_BADLENGTH
;
92 IOStdReq(io
)->io_Actual
= 0;
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 */
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
);
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
);
156 io
->io_Error
= IOERR_NOCMD
;
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
)
172 const UWORD NSDSupported
[] = {
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
;
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
;
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
);
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
))
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
);
236 IOStdReq(io
)->io_Actual
= (ap
->ap_type
== ATA_PORT_T_DISK
) ? 0 : -1;
240 IOStdReq(io
)->io_Actual
= unit
->sim_ChangeNum
;
244 IOStdReq(io
)->io_Actual
= (unit
->sim_Flags
& SIMF_MediaPresent
) ? 0 : -1;
248 // FIXME: Eject removable media
251 case TD_GETDRIVETYPE
:
252 IOStdReq(io
)->io_Actual
= DRIVE_NEWSTYLE
;
256 if (len
< sizeof(*geom
))
259 if (ap
->ap_type
!= ATA_PORT_T_DISK
&& ap
->ap_type
!= ATA_PORT_T_ATAPI
)
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
);
279 if (len
& (at
->at_identify
.nsectors
* at
->at_identify
.sector_size
- 1))
281 off64
= iotd
->iotd_Req
.io_Offset
;
282 if (off64
& (at
->at_identify
.nsectors
* at
->at_identify
.sector_size
- 1))
284 done
= ahci_sector_rw(io
, off64
, TRUE
);
287 // FIXME: Tie in with power management
288 IOStdReq(io
)->io_Actual
= 1;
292 off64
= iotd
->iotd_Req
.io_Offset
;
293 done
= ahci_sector_rw(io
, off64
, TRUE
);
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
);
302 off64
= iotd
->iotd_Req
.io_Offset
;
303 done
= ahci_sector_rw(io
, off64
, FALSE
);
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
);
312 if (sizeof(struct SCSICmd
) != len
)
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
);
321 case TD_ADDCHANGEINT
:
322 if (io
->io_Flags
& IOF_QUICK
)
325 case TD_REMCHANGEINT
:
326 if (io
->io_Flags
& IOF_QUICK
)
331 // FIXME: Implemennt cache invalidate
335 // FIXME: Implement cache flush
339 bug("ahci.device %d: Unknown IO command %d\n", unit
->sim_Unit
, io
->io_Command
);
341 io
->io_Error
= IOERR_NOCMD
;
345 io
->io_Error
= IOERR_BADLENGTH
;
349 io
->io_Error
= IOERR_BADADDRESS
;
354 /* The IO is finished, so no need to keep it around anymore */
356 ObtainSemaphore(&unit
->sim_Lock
);
357 Remove(&io
->io_Message
.mn_Node
);
358 ReleaseSemaphore(&unit
->sim_Lock
);
360 io
->io_Flags
&= ~IOF_QUICK
;
363 /* Need a reply now? */
364 if (done
&& !(io
->io_Flags
& IOF_QUICK
))
365 ReplyMsg(&io
->io_Message
);
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
));
373 AROS_LH1(LONG
, AbortIO
,
374 AROS_LHA(struct IORequest
*, io
, A1
),
375 LIBBASETYPEPTR
, LIBBASE
, 6, ahci
)
379 /* Aborting IOs is not (yet) supported */
386 /* vim: set ts=8 sts=4 et : */