|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /* Copyright (c) 1991 NeXT Computer, Inc. All rights reserved.
25: *
26: * SCSIDisk.m - Implementation of exported methods of SCSI Disk device class.
27: *
28: * HISTORY
29: *
30: * 30-Jul-98 Dan Markarian at Apple. See Radar 2260508, 2258860. If the
31: * SCSIDisk driver attempts to probe a non-zero LUN and a
32: * target exists on LUN 0 that is actively doing (tagged) I/O,
33: * the target may abort all commands with an overlapped command
34: * error, causing the original command to fail without completion.
35: * Suggested work-arounds (for MacOS X Server, release 1): probe
36: * only LUN 0. If a target is discovered, reserve all LUNs for
37: * that target. This code must be revisited for IOKit.
38: *
39: * 11-Feb-91 Doug Mitchell at NeXT
40: * Created.
41: */
42:
43: #define MACH_USER_API 1
44: #undef KERNEL_PRIVATE
45:
46: #import <bsd/sys/types.h>
47: #import <driverkit/return.h>
48: #import <driverkit/IODisk.h>
49: #import <driverkit/SCSIDiskPrivate.h>
50: #import <driverkit/SCSIDiskTypes.h>
51: #import <driverkit/SCSIDisk.h>
52: #import <driverkit/SCSIDiskKern.h>
53: #import <driverkit/SCSIStructInlines.h>
54: #import <bsd/dev/scsireg.h>
55: #import <driverkit/xpr_mi.h>
56: #import <kernserv/prototypes.h>
57: #import <mach/mach_interface.h>
58: #import <driverkit/kernelDiskMethods.h>
59: #import <machkit/NXLock.h>
60:
61: #ifdef DEBUG
62: #define SCSI_SA_TEST 0
63: #else DEBUG
64: #define SCSI_SA_TEST 0
65: #endif DEBUG
66:
67: /*
68: * FIXME - ensure that diskUnit doesn't exceed NUM_SD_DEV.
69: */
70: static int diskUnit = 0;
71:
72: @implementation SCSIDisk
73:
74: + (IODeviceStyle)deviceStyle
75: {
76: return IO_IndirectDevice;
77: }
78:
79: /*
80: * The protocol we need as an indirect device.
81: */
82: static Protocol *protocols[] = {
83: @protocol(IOSCSIControllerExported),
84: nil
85: };
86:
87: + (Protocol **)requiredProtocols
88: {
89: return protocols;
90: }
91:
92:
93: + initialize
94: {
95: if(self == [SCSIDisk class]) {
96: sd_init_idmap();
97: }
98: return [super initialize];
99: }
100:
101: /*
102: * probe is invoked at load time. It determines what devices are on the
103: * bus and alloc's and init:'s an instance of this class for each one.
104: */
105: + (BOOL)probe : deviceDescription;
106: {
107: char Target, Lun;
108: sdInitReturn_t irtn = SDR_ERROR;
109: SCSIDisk *diskId = nil;
110: IODevAndIdInfo *idMap = sd_idmap();
111: id controllerId = [deviceDescription directDevice];
112: BOOL brtn = NO;
113: SCSIDisk *diskIdArray[SCSI_NLUNS];
114: int nLuns;
115: /*
116: * Radar 2005639
117: */
118: unsigned int paramValue[1];
119: unsigned int count = 1;
120: IOReturn ioReturn;
121:
122: /* asm volatile("int3"); */
123:
124: #if hppa
125: /* search from the top down on hp since that was done before */
126: for(Target=[controllerId numberOfTargets]-1; Target>=0; Target--)
127: #else
128: for(Target=0; Target<[controllerId numberOfTargets]; Target++)
129: #endif
130: {
131:
132: #if 0 // Prior to Radar Fix #2260508
133: for(Lun=nLuns=0; Lun<SCSI_NLUNS; Lun++) {
134: #else // Radar Fix #2260508
135: for(Lun=nLuns=0; Lun<1; Lun++) {
136: #endif // Radar Fix #2260508
137: if(diskId == nil) {
138: /*
139: * Create an instance, do some basic
140: * initialization. Set up a default
141: * device name for error reporting during
142: * initialization.
143: */
144: diskId = [SCSIDisk alloc];
145: [diskId setName:"SCSIDisk"];
146: /*
147: * Radar 2005639. If the controller implements
148: * the getIntValues APPLE_MAX_THREADS selector,
149: * instantiate with the specified number of
150: * of threads, otherwise let the diskID
151: * instantiate itself with its default number.
152: */
153: ioReturn = [controllerId getIntValues
154: : ¶mValue[0]
155: forParameter : APPLE_MAX_THREADS
156: count : &count];
157: if (ioReturn == IO_R_SUCCESS && count == 1) {
1.1.1.2 ! root 158: #if 0 /* Radar 2291688: supress IOLog */
1.1 root 159: IOLog("%s: %s requests %d threads\n",
160: [self name],
161: [controllerId name],
162: paramValue[0]
163: );
1.1.1.2 ! root 164: #endif
1.1 root 165: [diskId initResourcesWithThreadCount
166: : paramValue[0]];
167: }
168: else {
1.1.1.2 ! root 169: #if 0 /* Radar 2291688: supress IOLog */
1.1 root 170: IOLog(
171: "%s: %s does not support %s selector, using default\n",
172: [self name],
173: [controllerId name],
174: APPLE_MAX_THREADS
175: );
1.1.1.2 ! root 176: #endif
1.1 root 177: [diskId initResources];
178: }
179: [diskId setDevAndIdInfo:&(idMap[diskUnit])];
180: }
181: if([controllerId reserveTarget:Target
182: lun:Lun
183: forOwner:diskId]) {
184: /*
185: * Someone already has this one.
186: */
187: continue;
188: }
189: diskId->_isReserved = 1;
190: #if 1 // Radar Fix #2260508
191: /*
192: * A valid target at LUN 0 has been reserved: reserve
193: * all non-zero LUNs as well. Note that _controller
194: * must be set before calling this next method, so go
195: * ahead and set it ourselves since the diskId object
196: * might be newly allocated (and yet-uninitialized).
197: */
198: diskId->_controller = controllerId;
199: [diskId reserveNonZeroLunsOnTarget:Target];
200: #endif // Radar Fix #2260508
201: irtn = [diskId SCSIDiskInit:(int)diskUnit
202: targetId:Target
203: lun:Lun
204: controller:controllerId];
205: [diskId setDeviceDescription:deviceDescription];
206: switch(irtn) {
207: case SDR_GOOD:
208: /*
209: * Postpone registering this device
210: * until we have looked at all other LUNs.
211: * This prevents I/O to multiple LUNs
212: * while we are probing, which is not handled
213: * well by some devices.
214: */
215: diskIdArray[nLuns++] = diskId;
216: diskUnit++;
217: diskId = nil;
218: brtn = YES;
219: break;
220:
221: default:
222: #if 1 // Radar Fix #2260508
223: /*
224: * LUN 0 will be unreserved: unreserve all
225: * non-zero LUNs first. Note _controller
226: * is already set at this point.
227: */
228: [diskId releaseNonZeroLunsOnTarget:Target];
229: #endif // Radar Fix #2260508
230: [controllerId releaseTarget:Target
231: lun:Lun
232: forOwner:diskId];
233: diskId->_isReserved = 0;
234:
235: if(irtn == SDR_SELECTTO) {
236: /*
237: * Skip the rest of the luns on
238: * this target.
239: */
240: goto nextTarget;
241: }
242: /*
243: * else try next lun.
244: */
245: }
246: } /* for lun */
247: nextTarget:
248: /* Now we have looked at all luns. */
249: for (Lun=0; Lun < nLuns; Lun++) {
250: SCSIDisk *thisId = diskIdArray[Lun];
251: /*
252: * All right! Have IODisk superclass take
253: * care of the rest.
254: */
255: [thisId setDeviceKind:"SCSIDisk"];
256: [thisId setIsPhysical:YES];
257: [thisId registerDevice];
258: thisId->_isRegistered = 1;
259: }
260: continue;
261: } /* for target */
262:
263: /*
264: * Free up leftover owner and id. At this point, diskId does NOT have
265: * a target/lun reserved.
266: */
267: if(diskId) {
268: [diskId free];
269: }
270:
271: #if SCSI_SA_TEST
272: diskTest("sd0");
273: #endif SCSI_SA_TEST
274: return brtn;
275: }
276:
277: /*
278: * IODiskReadingAndWriting protocol methods.
279: */
280: - (IOReturn) readAt : (unsigned)offset
281: length : (unsigned)length
282: buffer : (unsigned char *)buffer
283: actualLength : (unsigned *)actualLength
284: client : (vm_task_t)client
285: {
286: IOReturn rtn;
287:
288: xpr_sd("%s read: offset 0x%x length 0x%x\n",
289: [self name], offset, length, 4,5);
290: rtn = [self deviceRwCommon : SDOP_READ
291: block : offset
292: length : length
293: buffer : buffer
294: client: client
295: pending : NULL
296: actualLength : actualLength];
297: xpr_sd("%s read: RETURNING %s\n", [self name],
298: [self stringFromReturn:rtn], 3,4,5);
299: return(rtn);
300: }
301:
302: - (IOReturn) readAsyncAt : (unsigned)offset
303: length : (unsigned)length
304: buffer : (unsigned char *)buffer
305: pending : (void *)pending
306: client : (vm_task_t)client
307: {
308: IOReturn rtn;
309:
310: xpr_sd("%s readAsync: offset 0x%x length 0x%x\n",
311: [self name], offset, length, 4,5);
312: rtn = [self deviceRwCommon : SDOP_READ
313: block : offset
314: length : length
315: buffer : buffer
316: client : client
317: pending : (void *)pending
318: actualLength : NULL];
319: xpr_sd("%s readAsync: RETURNING %s\n", [self name],
320: [self stringFromReturn:rtn], 3,4,5);
321: return(rtn);
322: }
323:
324: - (IOReturn) writeAt : (unsigned)offset
325: length : (unsigned)length
326: buffer : (unsigned char *)buffer
327: actualLength : (unsigned *)actualLength
328: client : (vm_task_t)client
329: {
330: IOReturn rtn;
331:
332: xpr_sd("%s write: offset 0x%x length 0x%x\n",
333: [self name], offset, length, 4,5);
334: rtn = [self deviceRwCommon : SDOP_WRITE
335: block : offset
336: length : length
337: buffer : buffer
338: client: client
339: pending : NULL
340: actualLength : actualLength];
341: xpr_sd("%s deviceWrite: RETURNING %s\n", [self name],
342: [self stringFromReturn:rtn], 3,4,5);
343: return(rtn);
344: }
345:
346: - (IOReturn) writeAsyncAt : (unsigned)offset
347: length : (unsigned)length
348: buffer : (unsigned char *)buffer
349: pending : (void *)pending
350: client : (vm_task_t)client
351: {
352: IOReturn rtn;
353:
354: xpr_sd("%s writeAsync: offset 0x%x length 0x%x\n",
355: [self name], offset, length, 4,5);
356: rtn = [self deviceRwCommon : SDOP_WRITE
357: block : offset
358: length : length
359: buffer : buffer
360: client : client
361: pending : (void *)pending
362: actualLength : NULL];
363: xpr_sd("%s writeAsync: RETURNING %s\n", [self name],
364: [self stringFromReturn:rtn], 3,4,5);
365: return(rtn);
366: }
367:
368:
369: - (void) synchronizeCache
370: {
371: IOSCSIRequest scsiReq;
372: cdb_10_t *cdbp = &scsiReq.cdb.cdb_c10;
373: sdBuf_t *sdBuf;
374:
375: bzero(&scsiReq, sizeof(IOSCSIRequest));
376: scsiReq.target = _target;
377: scsiReq.lun = _lun;
378: scsiReq.timeoutLength = SD_TIMEOUT_SIMPLE;
379: scsiReq.disconnect = 1;
380:
381: sdBuf = [self allocSdBuf:NULL];
382: sdBuf->command = SDOP_CDB_WRITE;
383: sdBuf->scsiReq = &scsiReq;
384: sdBuf->retryDisable = 1;
385: sdBuf->needsDisk = 0;
386:
387: /* Zeroing the lba and count fields will cause the drive to
388: * flush everything. We should be able to zero these fields
389: * explicitly, but for some reason the 10-byte commands don't
390: * have the proper field definitions.
391: */
392: bzero(cdbp,10);
393:
394: cdbp->c10_opcode = 0x35;
395: cdbp->c10_lun = _lun;
396:
397: [self enqueueSdBuf:sdBuf];
398:
399: [self freeSdBuf:sdBuf];
400: }
401:
402: - (IODiskReadyState)updateReadyState
403: {
404: IOSCSIRequest scsiReq;
405: cdb_6_t *cdbp = &scsiReq.cdb.cdb_c6;
406: sdBuf_t *sdBuf;
407: IODiskReadyState readyState;
408: esense_reply_t senseReply;
409:
410: xpr_sd("%s updateReadyState\n", [self name], 2,3,4,5);
411:
412: if( NO == _isReserved) {
413: return([self lastReadyState]);
414: }
415:
416: bzero(&scsiReq, sizeof(IOSCSIRequest));
417: scsiReq.target = _target;
418: scsiReq.lun = _lun;
419: scsiReq.timeoutLength = SD_TIMEOUT_SIMPLE;
420: scsiReq.disconnect = 1;
421:
422: sdBuf = [self allocSdBuf:NULL];
423: sdBuf->command = SDOP_CDB_WRITE;
424: sdBuf->scsiReq = &scsiReq;
425: sdBuf->retryDisable = 1;
426: sdBuf->needsDisk = 0;
427:
428: cdbp->c6_opcode = C6OP_TESTRDY;
429: cdbp->c6_lun = _lun;
430:
431: [self enqueueSdBuf:sdBuf];
432:
433: /*
434: * FIXME - how to distinguish not ready from no disk???
435: */
436: switch(scsiReq.driverStatus) {
437: case SR_IOST_GOOD:
438: readyState = IO_Ready;
439: break;
440: default:
441: readyState = IO_NotReady;
442: break;
443:
444: }
445: xpr_sd("%s updateReadyState: DONE; state = %s\n",
446: [self name],
447: IOFindNameForValue(readyState, readyStateValues), 3,4,5);
448: [self freeSdBuf:sdBuf];
449: return(readyState);
450: }
451:
452: /*
453: * IOPhysicalDiskMethods protocol methods.
454: */
455: - (IOReturn) ejectPhysical
456: {
457: return [self scsiStartStop:SS_EJECT inhibitRetry:NO];
458: }
459:
460:
461: /*
462: * Get physical parameters (dev_size, block_size, etc.) from new disk.
463: */
464: - (IOReturn)updatePhysicalParameters
465: {
466: capacity_reply_t capacityData;
467: mode_sel_data_t modeData;
468: sc_status_t rtn;
469: int block;
470: void *dataBuf;
471: void *freePtr;
472: unsigned freeCnt;
473: u_int block_size;
474: BOOL wp = NO;
475: int i;
476:
477: rtn = [self updateReadyState];
478: if(rtn) {
479: return IO_R_NOT_READY;
480: }
481: rtn = [self sdReadCapacity:&capacityData];
482: if(rtn) {
483: return(IO_R_IO);
484: }
485: [self setDiskSize:scsi_lastlba(&capacityData) + 1];
486: [self setBlockSize:scsi_blklen(&capacityData)];
487:
488: /*
489: * Try reading a block to see if disk is formatted.
490: */
491: block_size = [self blockSize];
492: dataBuf = [_controller allocateBufferOfLength:block_size
493: actualStart:&freePtr
494: actualLength:&freeCnt];
495: block = 10;
496: for(i=0; i<5; i++) {
497: rtn = [self sdRawRead:block blockCnt:1 buffer:dataBuf];
498: if(rtn == IO_R_SUCCESS) {
499: break;
500: }
501: block += 10;
502: }
503: if(rtn == IO_R_SUCCESS)
504: [self setFormattedInternal:1];
505: else
506: [self setFormattedInternal:0];
507:
508: IOFree(freePtr, freeCnt);
509:
510: /*
511: * Mark CD-ROMs as write-protected; use a mode sense for the others.
512: */
513:
514: if(_inquiryDeviceType == DEVTYPE_CDROM)
515: wp = YES;
516: else {
517: bzero(&modeData, sizeof(mode_sel_data_t));
518: rtn = [self sdModeSense:&modeData];
519:
520: if (modeData.msd_header.msh_wp)
521: wp = YES;
522: }
523:
524: [self setWriteProtected: wp];
525:
526: return(IO_R_SUCCESS);
527: }
528:
529: /*
530: * Called by volCheck thread when WS has told us that a requested disk is
531: * not present. Pending I/Os which require a disk to be present must be
532: * aborted.
533: */
534: - (void)abortRequest
535: {
536: sdBuf_t *sdBuf;
537:
538: xpr_sd("%s: abortRequest\n", [self name], 2,3,4,5);
539:
540: sdBuf = [self allocSdBuf:NULL];
541: sdBuf->command = SDOP_ABORT;
542: sdBuf->needsDisk = 0;
543: [self enqueueSdBuf:sdBuf];
544: xpr_sd("%s abortRequest: done %s\n", [self name], 2,3,4,5);
545: [self freeSdBuf:sdBuf];
546: return;
547: }
548:
549: /*
550: * Called by the volCheck thread when a transition to "ready" is detected.
551: * Pending I/Os which require a disk may proceed. All we have to do is
552: * wakeup the I/O threads which are waiting for something to show up
553: * in an I/O queue.
554: */
555: - (void)diskBecameReady
556: {
557: xpr_sd("diskBecameReady: %s\n", [self name], 2,3,4,5);
558: [_ioQLock lock];
559: [_ioQLock unlockWith:WORK_AVAILABLE];
560: }
561:
562: /*
563: * Inquire if disk is present; if not, and 'prompt' is YES, ask for it.
564: * Returns IO_R_NO_DISK if:
565: * prompt YES, disk not present, and user cancels request for disk.
566: * prompt NO, disk not present.
567: * Else returns IO_R_SUCCESS.
568: */
569: - (IOReturn)isDiskReady : (BOOL)prompt
570: {
571: sdBuf_t *sdBuf;
572: IOReturn rtn;
573:
574: xpr_sd("%s: diskBecameReady\n", [self name], 2,3,4,5);
575:
576: if( NO == _isReserved)
577: return(IO_R_NO_DISK);
578:
579: if([self lastReadyState] == IO_Ready) {
580: /*
581: * This one's easy...
582: */
583: return(IO_R_SUCCESS);
584: }
585: if(!prompt) {
586: return(IO_R_NO_DISK);
587: }
588: sdBuf = [self allocSdBuf:NULL];
589: sdBuf->command = SDOP_PROBEDISK;
590: sdBuf->needsDisk = 1;
591: rtn = [self enqueueSdBuf:sdBuf];
592: xpr_sd("%s diskBecameReady: returning %s\n", [self name],
593: [self stringFromReturn:rtn], 3,4,5);
594: [self freeSdBuf:sdBuf];
595: return(rtn);
596: }
597:
598: /*
599: * We have to override IODisk's setLastReadyState: so we know when to
600: * clear our local ejectPending flag.
601: */
602: - (void)setLastReadyState : (IODiskReadyState)readyState
603: {
604: xpr_sd("sd setLastReadyState\n", 1,2,3,4,5);
605: if(_ejectPending && (readyState != IO_Ejecting))
606: _ejectPending = 0;
607: [super setLastReadyState:readyState];
608: }
609:
610: /*
611: * Exported methods unique to the SCSIDisk class.
612: * Note caller must provide well-aligned DMA buffers.
613: */
614: - (IOReturn) sdCdbRead : (IOSCSIRequest *)scsiReq /* SCSI parameters */
615: buffer:(void *)buffer /* data destination */
616: client:(vm_task_t)client;
617: {
618: sdBuf_t *sdBuf;
619: IOReturn rtn;
620:
621: xpr_sd("sd%d sdCdbRead: opcode = 0x%x\n", [self unit],
622: scsiReq->cdb.cdb_opcode, 3,4,5);
623:
624: sdBuf = [self allocSdBuf:NULL];
625: sdBuf->command = SDOP_CDB_READ;
626: sdBuf->scsiReq = scsiReq;
627: sdBuf->buf = buffer;
628: sdBuf->client = client;
629: sdBuf->pending = NULL;
630: sdBuf->needsDisk = 0;
631: rtn = [self enqueueSdBuf:sdBuf];
632: xpr_sd("sd%d sdCdbRead: returning %s\n", [self unit],
633: [self stringFromReturn:rtn], 3,4,5);
634: [self freeSdBuf:sdBuf];
635: return(rtn);
636: }
637:
638: - (IOReturn) sdCdbWrite : (IOSCSIRequest *)scsiReq /* SCSI parameters */
639: buffer:(void *)buffer /* data destination */
640: client:(vm_task_t)client
641: {
642: sdBuf_t *sdBuf;
643: IOReturn rtn;
644:
645: xpr_sd("sd%d sdCdbWrite: opcode = 0x%x\n", [self unit],
646: scsiReq->cdb.cdb_opcode, 3,4,5);
647:
648: sdBuf = [self allocSdBuf:NULL];
649: sdBuf->command = SDOP_CDB_WRITE;
650: sdBuf->scsiReq = scsiReq;
651: sdBuf->buf = buffer;
652: sdBuf->client = client;
653: sdBuf->pending = NULL;
654: sdBuf->needsDisk = 0;
655: rtn = [self enqueueSdBuf:sdBuf];
656: xpr_sd("sd%d sdCdbWrite: returning %s\n", [self unit],
657: [self stringFromReturn:rtn], 3,4,5);
658: [self freeSdBuf:sdBuf];
659: return(rtn);
660: }
661:
662: - (int)target
663: {
664: return _target;
665: }
666:
667: - (int)lun
668: {
669: return _lun;
670: }
671:
672: /*
673: * This is mainly here for the convenience of 486 CDROM boot. It's cheap so
674: * we'll leave it in for other platforms too.
675: */
676: - (unsigned char)inquiryDeviceType
677: {
678: return _inquiryDeviceType;
679: }
680:
681: - controller
682: {
683: return _controller;
684: }
685:
686: - getDevicePath:(char *)path maxLength:(int)maxLen useAlias:(BOOL)doAlias
687: {
688: if( [super getDevicePath:path maxLength:maxLen useAlias:doAlias]) {
689:
690: char unitStr[ 12 ];
691: int len = maxLen - strlen( path);
692:
693: sprintf( unitStr, "/@%x", [self target]);
694: len -= strlen( unitStr);
695: if( len < 0)
696: return( nil);
697: strcat( path, unitStr);
698: if( [self lun]) {
699: sprintf( unitStr, ",%x", [self lun]);
700: len -= strlen( unitStr);
701: if( len < 0)
702: return( nil);
703: strcat( path, unitStr);
704: }
705: return( self);
706: }
707: return( nil);
708: }
709:
710: - (char *) matchDevicePath:(char *)matchPath
711: {
712: BOOL matches = NO;
713: char * unitStr;
714: extern long int strtol(const char *nptr, char **endptr, int base);
715:
716: unitStr = [super matchDevicePath:matchPath];
1.1.1.2 ! root 717: if( unitStr && (*unitStr == '/')) {
1.1 root 718: unitStr = (char *)strchr( unitStr, '@');
719: if( unitStr) {
720: matches = ([self target] == strtol( unitStr + 1, &unitStr, 16));
721: if( matches && (*unitStr == ','))
722: matches = ([self lun] == strtol( unitStr + 1, &unitStr, 16));
1.1.1.2 ! root 723: }
1.1 root 724: }
725: if( matches)
726: return( unitStr);
727: else
728: return( NULL);
729: }
730:
731: - property_IOUnit:(char *)result length:(unsigned int *)maxLen
732: {
733: if( [self lun])
734: sprintf( result, "%d,%d", [self target], [self lun]);
735: else
736: sprintf( result, "%d", [self target]);
737:
738: return( self);
739: }
740:
741: - property_IODeviceType:(char *)types length:(unsigned int *)maxLen
742: {
743: static const char * inquiryDevTypeStrings[ 9 ] = {
744: 0, IOTypeTape, IOTypePrinter, 0,
745: IOTypeWORM, IOTypeCDROM, IOTypeScanner, IOTypeOptical,
746: IOTypeChanger
747: };
748:
749: [super property_IODeviceType:types length:maxLen];
750:
751: if( (_inquiryDeviceType <= 8) && inquiryDevTypeStrings[ _inquiryDeviceType ]) {
752: strcat( types, " ");
753: strcat( types, inquiryDevTypeStrings[ _inquiryDeviceType ]);
754: }
755: return( self);
756: }
757:
758: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.