2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)scsi-mac-iokit.c 1.10 05/05/15 Copyright 1997,2001-2004 J. Schilling */
15 * Interface to the Darwin IOKit SCSI drivers
17 * Notes: Uses the IOKit/scsi-commands/SCSITaskLib interface
19 * As of October 2001, this interface does not support SCSI parallel bus
20 * (old-fashioned SCSI). It does support ATAPI, Firewire, and USB.
22 * First version done by Constantine Sapuntzakis <csapuntz@Stanford.EDU>
24 * Warning: you may change this source, but if you do that
25 * you need to change the _usal_version and _usal_auth* string below.
26 * You may not return "schily" for an SCG_AUTHOR request anymore.
27 * Choose your name instead of "schily" and make clear that the version
28 * string is related to a modified source.
30 * Copyright (c) 1997,2001-2004 J. Schilling
33 * This program is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License version 2
35 * as published by the Free Software Foundation.
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
42 * You should have received a copy of the GNU General Public License along with
43 * this program; see the file COPYING. If not, write to the Free Software
44 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
48 * Warning: you may change this source, but if you do that
49 * you need to change the _usal_version and _usal_auth* string below.
50 * You may not return "schily" for an SCG_AUTHOR request anymore.
51 * Choose your name instead of "schily" and make clear that the version
52 * string is related to a modified source.
54 static char _usal_trans_version[] = "scsi-mac-iokit.c-1.10"; /* The version for this transport */
56 #define MAX_SCG 16 /* Max # of SCSI controllers */
61 #include <mach/mach.h>
62 #include <Carbon/Carbon.h>
63 #include <IOKit/IOKitLib.h>
64 #include <IOKit/IOCFPlugIn.h>
65 #include <IOKit/scsi-commands/SCSITaskLib.h>
66 #include <mach/mach_error.h>
69 MMCDeviceInterface **mmcDeviceInterface;
70 SCSITaskDeviceInterface **scsiTaskDeviceInterface;
71 mach_port_t masterPort;
73 #define usallocal(p) ((struct usal_local *)((p)->local))
75 #define MAX_DMA_NEXT (32*1024)
77 #define MAX_DMA_NEXT (64*1024) /* Check if this is not too big */
81 * Return version information for the low level SCSI transport code.
82 * This has been introduced to make it easier to trace down problems
86 usalo_version(SCSI *usalp, int what)
88 if (usalp != (SCSI *)0) {
92 return (_usal_trans_version);
94 * If you changed this source, you are not allowed to
95 * return "schily" for the SCG_AUTHOR request.
98 return (_usal_auth_cdrkit);
107 usalo_help(SCSI *usalp, FILE *f)
109 __usal_help(f, "SCSITaskDeviceInterface", "Apple SCSI",
110 "", "Mac Prom device name", "IOCompactDiscServices/0",
117 * Valid Device names:
118 * IOCompactDiscServices
120 * IOSCSIPeripheralDeviceNub
122 * Also a / and a number can be appended to refer to something
123 * more than the first device (e.g. IOCompactDiscServices/5 for the 5th
124 * compact disc attached)
127 usalo_open(SCSI *usalp, char *device)
129 mach_port_t masterPort = NULL;
130 io_iterator_t scsiObjectIterator = NULL;
131 IOReturn ioReturnValue = kIOReturnSuccess;
132 CFMutableDictionaryRef dict = NULL;
133 io_object_t scsiDevice = NULL;
134 HRESULT plugInResult;
135 IOCFPlugInInterface **plugInInterface = NULL;
136 MMCDeviceInterface **mmcDeviceInterface = NULL;
137 SCSITaskDeviceInterface **scsiTaskDeviceInterface = NULL;
140 char *realdevice = NULL, *tmp;
141 int driveidx = 1, idx = 1;
143 if (device == NULL) {
144 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
145 "Please specify a device name (e.g. IOCompactDiscServices/0)");
149 realdevice = tmp = strdup(device);
150 tmp = strchr(tmp, '/');
153 driveidx = atoi(tmp);
156 if (usalp->local == NULL) {
157 usalp->local = malloc(sizeof (struct usal_local));
158 if (usalp->local == NULL)
162 ioReturnValue = IOMasterPort(bootstrap_port, &masterPort);
164 if (ioReturnValue != kIOReturnSuccess) {
165 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
166 "Couldn't get a master IOKit port. Error %d",
171 dict = IOServiceMatching(realdevice);
173 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
174 "Couldn't create dictionary for searching");
178 ioReturnValue = IOServiceGetMatchingServices(masterPort, dict,
179 &scsiObjectIterator);
182 if (scsiObjectIterator == NULL ||
183 (ioReturnValue != kIOReturnSuccess)) {
184 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
185 "No matching device %s found.", device);
193 while ((scsiDevice = IOIteratorNext(scsiObjectIterator)) != NULL) {
196 IOObjectRelease(scsiDevice);
201 if (scsiDevice == NULL) {
202 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
203 "No matching device found. Iterator failed.");
207 ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
208 kIOMMCDeviceUserClientTypeID,
209 kIOCFPlugInInterfaceID,
210 &plugInInterface, &score);
211 if (ioReturnValue != kIOReturnSuccess) {
215 plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
216 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
217 (LPVOID)&mmcDeviceInterface);
219 if (plugInResult != KERN_SUCCESS) {
220 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
221 "Unable to get MMC Interface: 0x%lX",
227 scsiTaskDeviceInterface =
228 (*mmcDeviceInterface)->GetSCSITaskDeviceInterface(mmcDeviceInterface);
230 if (scsiTaskDeviceInterface == NULL) {
231 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
232 "Failed to get taskDeviceInterface");
239 ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
240 kIOSCSITaskDeviceUserClientTypeID,
241 kIOCFPlugInInterfaceID,
242 &plugInInterface, &score);
243 if (ioReturnValue != kIOReturnSuccess) {
244 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
245 "Unable to get plugin Interface: %x",
250 plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
251 CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID),
252 (LPVOID)&scsiTaskDeviceInterface);
254 if (plugInResult != KERN_SUCCESS) {
255 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
256 "Unable to get generic Interface: 0x%lX",
264 (*scsiTaskDeviceInterface)->ObtainExclusiveAccess(scsiTaskDeviceInterface);
266 if (ioReturnValue != kIOReturnSuccess) {
267 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
268 "Unable to get exclusive access to device");
272 if (mmcDeviceInterface) {
273 (*mmcDeviceInterface)->AddRef(mmcDeviceInterface);
275 (*scsiTaskDeviceInterface)->AddRef(scsiTaskDeviceInterface);
276 usallocal(usalp)->mmcDeviceInterface = mmcDeviceInterface;
277 usallocal(usalp)->scsiTaskDeviceInterface = scsiTaskDeviceInterface;
278 usallocal(usalp)->masterPort = masterPort;
279 usal_settarget(usalp, 0, 0, 0);
283 if (scsiTaskDeviceInterface != NULL) {
284 (*scsiTaskDeviceInterface)->Release(scsiTaskDeviceInterface);
287 if (plugInInterface != NULL) {
288 (*plugInInterface)->Release(plugInInterface);
291 if (scsiDevice != NULL) {
292 IOObjectRelease(scsiDevice);
295 if (scsiObjectIterator != NULL) {
296 IOObjectRelease(scsiObjectIterator);
306 mach_port_deallocate(mach_task_self(), masterPort);
314 if (realdevice != NULL) {
321 usalo_close(SCSI *usalp)
323 SCSITaskDeviceInterface **sc;
324 MMCDeviceInterface **mmc;
326 if (usalp->local == NULL)
329 sc = usallocal(usalp)->scsiTaskDeviceInterface;
330 (*sc)->ReleaseExclusiveAccess(sc);
332 usallocal(usalp)->scsiTaskDeviceInterface = NULL;
334 mmc = usallocal(usalp)->mmcDeviceInterface;
336 (*mmc)->Release(mmc);
338 mach_port_deallocate(mach_task_self(), usallocal(usalp)->masterPort);
347 usalo_maxdma(SCSI *usalp, long amt)
349 long maxdma = MAX_DMA_NEXT;
353 if (ioctl(usallocal(usalp)->usalfile, SGIOCMAXDMA, &m) >= 0) {
355 if (usalp->debug > 0) {
356 fprintf((FILE *)usalp->errfile,
357 "maxdma: %d\n", maxdma);
365 usalo_getbuf(SCSI *usalp, long amt)
367 if (usalp->debug > 0) {
368 fprintf((FILE *)usalp->errfile,
369 "usalo_getbuf: %ld bytes\n", amt);
371 usalp->bufbase = malloc((size_t)(amt));
372 return (usalp->bufbase);
376 usalo_freebuf(SCSI *usalp)
379 free(usalp->bufbase);
380 usalp->bufbase = NULL;
384 usalo_havebus(SCSI *usalp, int busno)
392 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
398 usalo_initiator_id(SCSI *usalp)
404 usalo_isatapi(SCSI *usalp)
410 usalo_reset(SCSI *usalp, int what)
412 if (what == SCG_RESET_NOP)
414 if (what != SCG_RESET_BUS) {
424 usalo_send(SCSI *usalp)
426 struct usal_cmd *sp = usalp->scmd;
427 SCSITaskDeviceInterface **sc = NULL;
428 SCSITaskInterface **cmd = NULL;
430 SCSI_Sense_Data senseData;
431 SCSITaskStatus status;
432 UInt64 bytesTransferred;
433 IOReturn ioReturnValue;
436 if (usalp->local == NULL) {
440 sc = usallocal(usalp)->scsiTaskDeviceInterface;
442 cmd = (*sc)->CreateSCSITask(sc);
444 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
445 "Failed to create SCSI task");
448 sp->error = SCG_FATAL;
454 iov.address = (IOVirtualAddress) sp->addr;
455 iov.length = sp->size;
457 ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd,
458 sp->cdb.cmd_cdb, sp->cdb_len);
460 if (ioReturnValue != kIOReturnSuccess) {
461 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
462 "SetCommandDescriptorBlock failed with status %x",
468 ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, sp->size,
469 (sp->flags & SCG_RECV_DATA) ?
470 kSCSIDataTransfer_FromTargetToInitiator :
471 kSCSIDataTransfer_FromInitiatorToTarget);
472 if (ioReturnValue != kIOReturnSuccess) {
473 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
474 "SetScatterGatherEntries failed with status %x",
480 ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, sp->timeout * 1000);
481 if (ioReturnValue != kIOReturnSuccess) {
482 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
483 "SetTimeoutDuration failed with status %x",
489 memset(&senseData, 0, sizeof (senseData));
492 ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,
493 &senseData, &status, &bytesTransferred);
495 sp->resid = sp->size - bytesTransferred;
496 sp->error = SCG_NO_ERROR;
497 sp->ux_errno = geterrno();
499 if (ioReturnValue != kIOReturnSuccess) {
500 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
501 "Command execution failed with status %x",
503 sp->error = SCG_RETRYABLE;
508 memset(&sp->scb, 0, sizeof (sp->scb));
509 memset(&sp->u_sense.cmd_sense, 0, sizeof (sp->u_sense.cmd_sense));
510 if (senseData.VALID_RESPONSE_CODE != 0 || status == 0x02) {
512 * There is no sense length - we need to asume that
513 * we always get 18 bytes.
515 sp->sense_count = kSenseDefaultSize;
516 memmove(&sp->u_sense.cmd_sense, &senseData, kSenseDefaultSize);
517 if (sp->ux_errno == 0)
521 sp->u_scb.cmd_scb[0] = status;
524 if (status == kSCSITaskStatus_No_Status) {
525 sp->error = SCG_RETRYABLE;
530 * XXX Is it possible to have other senseful SCSI transport error codes?
535 (*cmd)->Release(cmd);