Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsi-mac-iokit.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
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).
6  *
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.
10  *
11  */
12
13 /* @(#)scsi-mac-iokit.c 1.10 05/05/15 Copyright 1997,2001-2004 J. Schilling */
14 /*
15  *      Interface to the Darwin IOKit SCSI drivers
16  *
17  *      Notes: Uses the IOKit/scsi-commands/SCSITaskLib interface
18  *
19  *      As of October 2001, this interface does not support SCSI parallel bus
20  *      (old-fashioned SCSI). It does support ATAPI, Firewire, and USB.
21  *
22  *      First version done by Constantine Sapuntzakis <csapuntz@Stanford.EDU>
23  *
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.
29  *
30  *      Copyright (c) 1997,2001-2004 J. Schilling
31  */
32 /*
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.
36  *
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.
41  *
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.
45  */
46
47 /*
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.
53  */
54 static  char    _usal_trans_version[] = "scsi-mac-iokit.c-1.10";        /* The version for this transport */
55
56 #define MAX_SCG         16      /* Max # of SCSI controllers */
57 #define MAX_TGT         16
58 #define MAX_LUN         8
59
60 #include <statdefs.h>
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>
67
68 struct usal_local {
69         MMCDeviceInterface      **mmcDeviceInterface;
70         SCSITaskDeviceInterface **scsiTaskDeviceInterface;
71         mach_port_t             masterPort;
72 };
73 #define usallocal(p)    ((struct usal_local *)((p)->local))
74
75 #define MAX_DMA_NEXT    (32*1024)
76 #if 0
77 #define MAX_DMA_NEXT    (64*1024)       /* Check if this is not too big */
78 #endif
79
80 /*
81  * Return version information for the low level SCSI transport code.
82  * This has been introduced to make it easier to trace down problems
83  * in applications.
84  */
85 static char *
86 usalo_version(SCSI *usalp, int what)
87 {
88         if (usalp != (SCSI *)0) {
89                 switch (what) {
90
91                 case SCG_VERSION:
92                         return (_usal_trans_version);
93                 /*
94                  * If you changed this source, you are not allowed to
95                  * return "schily" for the SCG_AUTHOR request.
96                  */
97                 case SCG_AUTHOR:
98                         return (_usal_auth_cdrkit);
99                 case SCG_SCCS_ID:
100                         return (__sccsid);
101                 }
102         }
103         return ((char *)0);
104 }
105
106 static int
107 usalo_help(SCSI *usalp, FILE *f)
108 {
109         __usal_help(f, "SCSITaskDeviceInterface", "Apple SCSI",
110                 "", "Mac Prom device name", "IOCompactDiscServices/0",
111                                                                 FALSE, FALSE);
112         return (0);
113 }
114
115
116 /*
117  * Valid Device names:
118  *    IOCompactDiscServices
119  *    IODVDServices
120  *    IOSCSIPeripheralDeviceNub
121  *
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)
125  */
126 static int
127 usalo_open(SCSI *usalp, char *device)
128 {
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;
138         SInt32 score = 0;
139         int err = -1;
140         char *realdevice = NULL, *tmp;
141         int driveidx = 1, idx = 1;
142
143         if (device == NULL) {
144                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
145                 "Please specify a device name (e.g. IOCompactDiscServices/0)");
146                 goto out;
147         }
148
149         realdevice = tmp = strdup(device);
150         tmp = strchr(tmp, '/');
151         if (tmp != NULL) {
152                 *tmp++ = '\0';
153                 driveidx = atoi(tmp);
154         }
155
156         if (usalp->local == NULL) {
157                 usalp->local = malloc(sizeof (struct usal_local));
158                 if (usalp->local == NULL)
159                         goto out;
160         }
161
162         ioReturnValue = IOMasterPort(bootstrap_port, &masterPort);
163
164         if (ioReturnValue != kIOReturnSuccess) {
165                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
166                             "Couldn't get a master IOKit port. Error %d",
167                             ioReturnValue);
168                 goto out;
169         }
170
171         dict = IOServiceMatching(realdevice);
172         if (dict == NULL) {
173                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
174                             "Couldn't create dictionary for searching");
175                 goto out;
176         }
177
178         ioReturnValue = IOServiceGetMatchingServices(masterPort, dict,
179                                                     &scsiObjectIterator);
180         dict = NULL;
181
182         if (scsiObjectIterator == NULL ||
183             (ioReturnValue != kIOReturnSuccess)) {
184                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
185                             "No matching device %s found.", device);
186                 goto out;
187         }
188
189         if (driveidx <= 0)
190                 driveidx = 1;
191
192         idx = 1;
193         while ((scsiDevice = IOIteratorNext(scsiObjectIterator)) != NULL) {
194                 if (idx == driveidx)
195                         break;
196                 IOObjectRelease(scsiDevice);
197                 scsiDevice = NULL;
198                 idx++;
199         }
200
201         if (scsiDevice == NULL) {
202                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
203                             "No matching device found. Iterator failed.");
204                 goto out;
205         }
206
207         ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
208                         kIOMMCDeviceUserClientTypeID,
209                         kIOCFPlugInInterfaceID,
210                         &plugInInterface, &score);
211         if (ioReturnValue != kIOReturnSuccess) {
212                 goto try_generic;
213         }
214
215         plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
216                                 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
217                                 (LPVOID)&mmcDeviceInterface);
218
219         if (plugInResult != KERN_SUCCESS) {
220                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
221                             "Unable to get MMC Interface: 0x%lX",
222                             (long)plugInResult);
223
224                 goto out;
225         }
226
227         scsiTaskDeviceInterface =
228                 (*mmcDeviceInterface)->GetSCSITaskDeviceInterface(mmcDeviceInterface);
229
230         if (scsiTaskDeviceInterface == NULL) {
231                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
232                             "Failed to get taskDeviceInterface");
233                 goto out;
234         }
235
236         goto init;
237
238 try_generic:
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",
246                             ioReturnValue);
247                 goto out;
248         }
249
250         plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
251                             CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID),
252                                         (LPVOID)&scsiTaskDeviceInterface);
253
254         if (plugInResult != KERN_SUCCESS) {
255                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
256                             "Unable to get generic Interface: 0x%lX",
257                             (long)plugInResult);
258
259                 goto out;
260         }
261
262 init:
263         ioReturnValue =
264                 (*scsiTaskDeviceInterface)->ObtainExclusiveAccess(scsiTaskDeviceInterface);
265
266         if (ioReturnValue != kIOReturnSuccess) {
267                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
268                             "Unable to get exclusive access to device");
269                 goto out;
270         }
271
272         if (mmcDeviceInterface) {
273                 (*mmcDeviceInterface)->AddRef(mmcDeviceInterface);
274         }
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);
280         err = 1;
281
282 out:
283         if (scsiTaskDeviceInterface != NULL) {
284                 (*scsiTaskDeviceInterface)->Release(scsiTaskDeviceInterface);
285         }
286
287         if (plugInInterface != NULL) {
288                 (*plugInInterface)->Release(plugInInterface);
289         }
290
291         if (scsiDevice != NULL) {
292                 IOObjectRelease(scsiDevice);
293         }
294
295         if (scsiObjectIterator != NULL) {
296                 IOObjectRelease(scsiObjectIterator);
297         }
298
299         if (err < 0) {
300                 if (usalp->local) {
301                         free(usalp->local);
302                         usalp->local = NULL;
303                 }
304
305                 if (masterPort) {
306                         mach_port_deallocate(mach_task_self(), masterPort);
307                 }
308         }
309
310         if (dict != NULL) {
311                 CFRelease(dict);
312         }
313
314         if (realdevice != NULL) {
315                 free(realdevice);
316         }
317         return (err);
318 }
319
320 static int
321 usalo_close(SCSI *usalp)
322 {
323         SCSITaskDeviceInterface **sc;
324         MMCDeviceInterface      **mmc;
325
326         if (usalp->local == NULL)
327                 return (-1);
328
329         sc = usallocal(usalp)->scsiTaskDeviceInterface;
330         (*sc)->ReleaseExclusiveAccess(sc);
331         (*sc)->Release(sc);
332         usallocal(usalp)->scsiTaskDeviceInterface = NULL;
333
334         mmc = usallocal(usalp)->mmcDeviceInterface;
335         if (mmc != NULL)
336                 (*mmc)->Release(mmc);
337
338         mach_port_deallocate(mach_task_self(), usallocal(usalp)->masterPort);
339
340         free(usalp->local);
341         usalp->local = NULL;
342
343         return (0);
344 }
345
346 static long
347 usalo_maxdma(SCSI *usalp, long amt)
348 {
349         long maxdma = MAX_DMA_NEXT;
350 #ifdef  SGIOCMAXDMA
351         int  m;
352
353         if (ioctl(usallocal(usalp)->usalfile, SGIOCMAXDMA, &m) >= 0) {
354                 maxdma = m;
355                 if (usalp->debug > 0) {
356                         fprintf((FILE *)usalp->errfile,
357                                 "maxdma: %d\n", maxdma);
358                 }
359         }
360 #endif
361         return (maxdma);
362 }
363
364 static void *
365 usalo_getbuf(SCSI *usalp, long amt)
366 {
367         if (usalp->debug > 0) {
368                 fprintf((FILE *)usalp->errfile,
369                         "usalo_getbuf: %ld bytes\n", amt);
370         }
371         usalp->bufbase = malloc((size_t)(amt));
372         return (usalp->bufbase);
373 }
374
375 static void
376 usalo_freebuf(SCSI *usalp)
377 {
378         if (usalp->bufbase)
379                 free(usalp->bufbase);
380         usalp->bufbase = NULL;
381 }
382
383 static BOOL
384 usalo_havebus(SCSI *usalp, int busno)
385 {
386         if (busno == 0)
387                 return (TRUE);
388         return (FALSE);
389 }
390
391 static int
392 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
393 {
394         return (-1);
395 }
396
397 static int
398 usalo_initiator_id(SCSI *usalp)
399 {
400         return (-1);
401 }
402
403 static int
404 usalo_isatapi(SCSI *usalp)
405 {
406         return (FALSE);
407 }
408
409 static int
410 usalo_reset(SCSI *usalp, int what)
411 {
412         if (what == SCG_RESET_NOP)
413                 return (0);
414         if (what != SCG_RESET_BUS) {
415                 errno = EINVAL;
416                 return (-1);
417         }
418
419         errno = 0;
420         return (-1);
421 }
422
423 static int
424 usalo_send(SCSI *usalp)
425 {
426         struct usal_cmd         *sp = usalp->scmd;
427         SCSITaskDeviceInterface **sc = NULL;
428         SCSITaskInterface       **cmd = NULL;
429         IOVirtualRange          iov;
430         SCSI_Sense_Data         senseData;
431         SCSITaskStatus          status;
432         UInt64                  bytesTransferred;
433         IOReturn                ioReturnValue;
434         int                     ret = 0;
435
436         if (usalp->local == NULL) {
437                 return (-1);
438         }
439
440         sc = usallocal(usalp)->scsiTaskDeviceInterface;
441
442         cmd = (*sc)->CreateSCSITask(sc);
443         if (cmd == NULL) {
444                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
445                             "Failed to create SCSI task");
446                 ret = -1;
447
448                 sp->error = SCG_FATAL;
449                 sp->ux_errno = EIO;
450                 goto out;
451         }
452
453
454         iov.address = (IOVirtualAddress) sp->addr;
455         iov.length = sp->size;
456
457         ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd,
458                                                 sp->cdb.cmd_cdb, sp->cdb_len);
459
460         if (ioReturnValue != kIOReturnSuccess) {
461                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
462                             "SetCommandDescriptorBlock failed with status %x",
463                             ioReturnValue);
464                 ret = -1;
465                 goto out;
466         }
467
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",
475                             ioReturnValue);
476                 ret = -1;
477                 goto out;
478         }
479
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",
484                             ioReturnValue);
485                 ret = -1;
486                 goto out;
487         }
488
489         memset(&senseData, 0, sizeof (senseData));
490
491         seterrno(0);
492         ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,
493                                 &senseData, &status, &bytesTransferred);
494
495         sp->resid = sp->size - bytesTransferred;
496         sp->error = SCG_NO_ERROR;
497         sp->ux_errno = geterrno();
498
499         if (ioReturnValue != kIOReturnSuccess) {
500                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
501                             "Command execution failed with status %x",
502                             ioReturnValue);
503                 sp->error = SCG_RETRYABLE;
504                 ret = -1;
505                 goto out;
506         }
507
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) {
511                 /*
512                  * There is no sense length - we need to asume that
513                  * we always get 18 bytes.
514                  */
515                 sp->sense_count = kSenseDefaultSize;
516                 memmove(&sp->u_sense.cmd_sense, &senseData, kSenseDefaultSize);
517                 if (sp->ux_errno == 0)
518                         sp->ux_errno = EIO;
519         }
520
521         sp->u_scb.cmd_scb[0] = status;
522
523         /* ??? */
524         if (status == kSCSITaskStatus_No_Status) {
525                 sp->error = SCG_RETRYABLE;
526                 ret = -1;
527                 goto out;
528         }
529         /*
530          * XXX Is it possible to have other senseful SCSI transport error codes?
531          */
532
533 out:
534         if (cmd != NULL) {
535                 (*cmd)->Release(cmd);
536         }
537
538         return (ret);
539 }