2 Copyright © 2004-2011, The AROS Development Team. All rights reserved
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>
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>
40 #include "ahci_intern.h"
41 #include "ahci_scsi.h"
44 #include LC_LIBDEFS_FILE
46 /* Translate to a SCSI command, since the
47 * backend will make that work on both ATAPI
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
;
62 /* It's safe to allocate these on the stack, since they
63 * will never be referenced once the ahci_scsi_*_io()
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?
75 io
->io_Error
= TDERR_DiskChanged
;
78 bmask
= sector_size
- 1;
80 if ((off64
& bmask
) || bmask
== 0 || data
== NULL
) {
81 io
->io_Error
= IOERR_BADADDRESS
;
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
;
91 IOStdReq(io
)->io_Actual
= 0;
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 */
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
);
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
);
155 io
->io_Error
= IOERR_NOCMD
;
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
)
171 const UWORD NSDSupported
[] = {
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
;
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
;
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
);
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
))
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
);
235 IOStdReq(io
)->io_Actual
= (ap
->ap_type
== ATA_PORT_T_DISK
) ? 0 : -1;
239 IOStdReq(io
)->io_Actual
= unit
->sim_ChangeNum
;
243 IOStdReq(io
)->io_Actual
= (unit
->sim_Flags
& SIMF_MediaPresent
) ? 0 : -1;
247 // FIXME: Eject removable media
250 case TD_GETDRIVETYPE
:
251 IOStdReq(io
)->io_Actual
= DRIVE_NEWSTYLE
;
255 if (len
< sizeof(*geom
))
258 if (ap
->ap_type
!= ATA_PORT_T_DISK
&& ap
->ap_type
!= ATA_PORT_T_ATAPI
)
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
);
278 if (len
& (at
->at_identify
.nsectors
* at
->at_identify
.sector_size
- 1))
280 off64
= iotd
->iotd_Req
.io_Offset
;
281 if (off64
& (at
->at_identify
.nsectors
* at
->at_identify
.sector_size
- 1))
283 done
= ahci_sector_rw(io
, off64
, TRUE
);
286 // FIXME: Tie in with power management
287 IOStdReq(io
)->io_Actual
= 1;
291 off64
= iotd
->iotd_Req
.io_Offset
;
292 done
= ahci_sector_rw(io
, off64
, TRUE
);
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
);
301 off64
= iotd
->iotd_Req
.io_Offset
;
302 done
= ahci_sector_rw(io
, off64
, FALSE
);
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
);
311 if (sizeof(struct SCSICmd
) != len
)
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
);
320 case TD_ADDCHANGEINT
:
321 if (io
->io_Flags
& IOF_QUICK
)
324 case TD_REMCHANGEINT
:
325 if (io
->io_Flags
& IOF_QUICK
)
330 // FIXME: Implemennt cache invalidate
334 // FIXME: Implement cache flush
338 bug("ahci.device %d: Unknown IO command %d\n", unit
->sim_Unit
, io
->io_Command
);
340 io
->io_Error
= IOERR_NOCMD
;
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
;
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_Comand = %d, io_Error = %d\n", unit
->sim_Unit
, io
, io
->io_Flags
, io
->io_Command
, 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 : */