]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOSCSICDDrive/IOSCSICDDrive.cpp
xnu-124.1.tar.gz
[apple/xnu.git] / iokit / Families / IOSCSICDDrive / IOSCSICDDrive.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <IOKit/IOLib.h>
24 #include <IOKit/IOReturn.h>
25 #include <IOKit/IOMemoryDescriptor.h>
26 #include <IOKit/scsi/IOSCSIDeviceInterface.h>
27 #include <IOKit/storage/scsi/IOSCSICDDrive.h>
28 #include <IOKit/storage/scsi/IOSCSICDDriveNub.h>
29
30 #define super IOSCSIHDDrive
31 OSDefineMetaClassAndStructors(IOSCSICDDrive,IOSCSIHDDrive)
32
33 static void __inline ConvertBCDToHex(UInt8 *value)
34 {
35 *value = (((*value) >> 4) * 10) + ((*value) & 0x0f);
36 }
37
38 /* The Callback (C) entry from the SCSI provider. We just glue
39 * right into C++.
40 */
41
42 void
43 IOSCSICDDrive_gc_glue(IOService *object,void *param)
44 {
45 IOSCSICDDrive *self;
46 struct IOBasicSCSI::context *cx;
47
48 self = (IOSCSICDDrive *)object;
49 cx = (struct IOBasicSCSI::context *)param;
50 self->genericCompletion(cx); /* do it in C++ */
51 }
52
53 IOReturn
54 IOSCSICDDrive::audioPause(bool pause)
55 {
56 struct context *cx;
57 SCSICDBInfo scsiCmd;
58 IOReturn result;
59
60 cx = allocateContext();
61 if (cx == NULL) {
62 return(kIOReturnNoMemory);
63 }
64
65 bzero(&scsiCmd, sizeof(scsiCmd));
66
67 scsiCmd.cdbLength = 10;
68 scsiCmd.cdb[0] = kIOSCSICommandPauseResume;
69 scsiCmd.cdb[8] = pause ? 0x00 : 0x01;
70
71 cx->scsireq->setCDB(&scsiCmd);
72 cx->scsireq->setPointers(cx->memory, 0, false);
73 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
74 cx->scsireq->setTimeout(5000);
75
76 result = simpleSynchIO(cx);
77
78 deleteContext(cx);
79
80 return(result);
81 }
82
83 IOReturn
84 IOSCSICDDrive::audioPlay(CDMSF timeStart,CDMSF timeStop)
85 {
86 return(doAudioPlayCommand(timeStart,timeStop));
87 }
88
89 IOReturn
90 IOSCSICDDrive::audioScan(CDMSF timeStart,bool reverse)
91 {
92 struct context *cx;
93 SCSICDBInfo scsiCmd;
94 IOReturn result;
95
96 cx = allocateContext();
97 if (cx == NULL) {
98 return(kIOReturnNoMemory);
99 }
100
101 bzero(&scsiCmd, sizeof(scsiCmd));
102
103 scsiCmd.cdbLength = 10;
104 scsiCmd.cdb[0] = 0xCD; /* AUDIO SCAN (10) */
105 scsiCmd.cdb[1] = reverse ? 0x10 : 0x00;
106 scsiCmd.cdb[3] = timeStart.minute;
107 scsiCmd.cdb[4] = timeStart.second;
108 scsiCmd.cdb[5] = timeStart.frame;
109 scsiCmd.cdb[9] = 0x40; /* MSF */
110
111 cx->scsireq->setCDB(&scsiCmd);
112 cx->scsireq->setPointers(cx->memory, 0, false);
113 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
114 cx->scsireq->setTimeout(5000);
115
116 result = simpleSynchIO(cx);
117
118 deleteContext(cx);
119
120 return(result);
121 }
122
123 IOReturn
124 IOSCSICDDrive::audioStop()
125 {
126 struct context *cx;
127 SCSICDBInfo scsiCmd;
128 IOReturn result;
129
130 cx = allocateContext();
131 if (cx == NULL) {
132 return(kIOReturnNoMemory);
133 }
134
135 bzero(&scsiCmd, sizeof(scsiCmd));
136
137 scsiCmd.cdbLength = 6;
138 scsiCmd.cdb[0] = 0x01; /* REZERO UNIT (6) */
139
140 cx->scsireq->setCDB(&scsiCmd);
141 cx->scsireq->setPointers(cx->memory, 0, false);
142 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
143 cx->scsireq->setTimeout(5000);
144
145 result = simpleSynchIO(cx);
146
147 deleteContext(cx);
148
149 return(result);
150 }
151
152 bool
153 IOSCSICDDrive::deviceTypeMatches(UInt8 inqBuf[],UInt32 inqLen,SInt32 *score)
154 {
155 if ((inqBuf[0] & 0x1f) == kIOSCSIDeviceTypeCDROM) {
156 // IOLog("%s[IOSCSICDDrive]::deviceTypeMatches, returning TRUE\n",getName());
157 *score = 0;
158 return(true);
159 } else {
160 // IOLog("%s[IOSCSICDDrive]::deviceTypeMatches, returning FALSE\n",getName());
161 return(false); /* we don't handle other devices */
162 }
163 }
164
165 IOReturn
166 IOSCSICDDrive::doAsyncReadCD(IOMemoryDescriptor *buffer,
167 UInt32 block,UInt32 nblks,
168 CDSectorArea sectorArea,
169 CDSectorType sectorType,
170 IOStorageCompletion completion)
171 {
172 struct context *cx;
173 SCSICDBInfo scsiCmd;
174
175 assert(buffer->getDirection() == kIODirectionIn);
176
177 bzero(&scsiCmd, sizeof(scsiCmd));
178
179 if (sectorArea == kCDSectorAreaUser) {
180 if (sectorType == kCDSectorTypeCDDA) {
181 scsiCmd.cdbLength = 12;
182 scsiCmd.cdb[ 0] = 0xD8; /* READ CD-DA */
183 scsiCmd.cdb[ 2] = (block >> 24) & 0xFF;
184 scsiCmd.cdb[ 3] = (block >> 16) & 0xFF;
185 scsiCmd.cdb[ 4] = (block >> 8) & 0xFF;
186 scsiCmd.cdb[ 5] = (block ) & 0xFF;
187 scsiCmd.cdb[ 6] = (nblks >> 24) & 0xFF;
188 scsiCmd.cdb[ 7] = (nblks >> 16) & 0xFF;
189 scsiCmd.cdb[ 8] = (nblks >> 8) & 0xFF;
190 scsiCmd.cdb[ 9] = (nblks ) & 0xFF;
191 } else if (sectorType == kCDSectorTypeMode1 ||
192 sectorType == kCDSectorTypeMode2Form1) {
193 return doAsyncReadWrite(buffer,block,nblks,completion);
194 }
195 }
196
197 if (scsiCmd.cdbLength == 0) {
198 return(kIOReturnUnsupported);
199 }
200
201 cx = allocateContext();
202 if (cx == NULL) {
203 return(kIOReturnNoMemory);
204 }
205
206 buffer->retain(); /* bump the retain count */
207 cx->memory = buffer;
208 cx->completion = completion;
209 cx->state = kAsyncReadWrite;
210
211 cx->scsireq->setCallback(this, (CallbackFn)IOSCSICDDrive_gc_glue, cx);
212 cx->scsireq->setCDB(&scsiCmd);
213 cx->scsireq->setPointers(buffer, buffer->getLength(), false);
214 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
215 cx->scsireq->setTimeout(60000);
216
217 /* Queue the request awaiting power and return. When power comes up,
218 * the request will be passed to standardAsyncReadWriteExecute.
219 */
220 queueCommand(cx,kAsync,getReadWritePowerState()); /* queue and possibly wait for power */
221
222 return(kIOReturnSuccess);
223 }
224
225 IOReturn
226 IOSCSICDDrive::doAsyncReadWrite(IOMemoryDescriptor *buffer,
227 UInt32 block,UInt32 nblks,
228 IOStorageCompletion completion)
229 {
230 if (buffer->getDirection() == kIODirectionOut) {
231 return(kIOReturnNotWritable);
232 }
233
234 return(super::doAsyncReadWrite(buffer,block,nblks,completion));
235 }
236
237 IOReturn
238 IOSCSICDDrive::doSyncReadWrite(IOMemoryDescriptor *buffer,UInt32 block,UInt32 nblks)
239 {
240 if (buffer->getDirection() == kIODirectionOut) {
241 return(kIOReturnNotWritable);
242 }
243
244 return(super::doSyncReadWrite(buffer,block,nblks));
245 }
246
247 IOReturn
248 IOSCSICDDrive::doAudioPlayCommand(CDMSF timeStart,CDMSF timeStop)
249 {
250 struct context *cx;
251 struct IOAudioPlayMSFcdb *p;
252 IOSCSICommand *req;
253 SCSICDBInfo scsiCDB;
254 IOReturn result;
255
256 cx = allocateContext();
257 if (cx == NULL) {
258 return(kIOReturnNoMemory);
259 }
260
261 req = cx->scsireq;
262
263 bzero( &scsiCDB, sizeof(scsiCDB) );
264
265 p = (struct IOAudioPlayMSFcdb *)scsiCDB.cdb; /* use PlayAudioMSF */
266 p->opcode = kIOSCSICommandPlayAudioMSF;
267 p->lunbits = 0;
268 p->reserved1 = 0;
269 p->start_m = timeStart.minute;
270 p->start_s = timeStart.second;
271 p->start_f = timeStart.frame;
272 p->end_m = timeStop.minute;
273 p->end_s = timeStop.second;
274 p->end_f = timeStop.frame;
275 p->ctlbyte = 0;
276
277 scsiCDB.cdbLength = 10;
278 req->setCDB( &scsiCDB );
279 req->setPointers(cx->senseDataDesc, 255, false, true);
280
281 req->setPointers( cx->memory, 0, false );
282 req->setTimeout( 5000 );
283
284 result = simpleSynchIO(cx);
285
286 deleteContext(cx);
287
288 return(result);
289 }
290
291 IOReturn
292 IOSCSICDDrive::doFormatMedia(UInt64 /* byteCapacity */)
293 {
294 return(kIOReturnUnsupported);
295 }
296
297 UInt32
298 IOSCSICDDrive::doGetFormatCapacities(UInt64 * /* capacities */,UInt32 /* capacitiesMaxCount */) const
299 {
300 return(kIOReturnUnsupported);
301 }
302
303 IOReturn
304 IOSCSICDDrive::doSynchronizeCache(void)
305 {
306 return(kIOReturnUnsupported);
307 }
308
309 IOReturn
310 IOSCSICDDrive::getAudioStatus(CDAudioStatus *status)
311 {
312 IOReturn result;
313 UInt8 *tempBuf;
314
315 /* Get a buffer for the returned data: */
316
317 result = allocateTempBuffer(&tempBuf,16);
318 if (result != kIOReturnSuccess) {
319 return(kIOReturnNoMemory);
320 }
321
322 result = readSubChannel(tempBuf,16,IORSCcdb::kCurrentPosition,0);
323
324 if (result == kIOReturnSuccess) { /* we got the data */
325 assert(tempBuf[2] == 0);
326 assert(tempBuf[3] == 12);
327 assert(tempBuf[4] == 1);
328
329 status->status = tempBuf[ 1];
330
331 status->position.track.number = tempBuf[ 6];
332 status->position.track.index = tempBuf[ 7];
333
334 status->position.time.minute = tempBuf[ 9];
335 status->position.time.second = tempBuf[10];
336 status->position.time.frame = tempBuf[11];
337
338 status->position.track.time.minute = tempBuf[13];
339 status->position.track.time.second = tempBuf[14];
340 status->position.track.time.frame = tempBuf[15];
341 }
342 deleteTempBuffer(tempBuf,16);
343
344 return(result);
345 }
346
347 IOReturn
348 IOSCSICDDrive::getAudioVolume(UInt8 *leftVolume,UInt8 *rightVolume)
349 {
350 struct context *cx;
351 SCSICDBInfo scsiCmd;
352 IOReturn result;
353 UInt8 audio_control[28];
354
355 cx = allocateContext();
356 if (cx == NULL) {
357 return(kIOReturnNoMemory);
358 }
359
360 cx->memory = IOMemoryDescriptor::withAddress(audio_control,
361 sizeof(audio_control),
362 kIODirectionIn);
363 if (cx->memory == NULL) {
364 deleteContext(cx);
365 return(kIOReturnNoMemory);
366 }
367
368 bzero(&scsiCmd, sizeof(scsiCmd));
369
370 scsiCmd.cdbLength = 6;
371 scsiCmd.cdb[0] = 0x1a; /* MODE SENSE (6) */
372 scsiCmd.cdb[2] = 0x0e; /* PAGE CODE E */
373 scsiCmd.cdb[4] = sizeof(audio_control);
374
375 cx->scsireq->setCDB(&scsiCmd);
376 cx->scsireq->setPointers(cx->memory, sizeof(audio_control), true);
377 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
378 cx->scsireq->setTimeout(5000);
379
380 result = simpleSynchIO(cx);
381
382 if (result == kIOReturnSuccess) {
383 assert((audio_control[ 0] ) == 28-1);
384 assert((audio_control[ 3] ) == 0x08);
385 assert((audio_control[12] & 0x3f) == 0x0e);
386 assert((audio_control[13] ) == 0x0e);
387
388 *leftVolume = audio_control[21];
389 *rightVolume = audio_control[23];
390 }
391
392 deleteContext(cx);
393
394 return(result);
395 }
396
397 const char *
398 IOSCSICDDrive::getDeviceTypeName(void)
399 {
400 return(kIOBlockStorageDeviceTypeCDROM);
401 }
402
403 bool
404 IOSCSICDDrive::init(OSDictionary * properties)
405 {
406 return(super::init(properties));
407 }
408
409 IOService *
410 IOSCSICDDrive::instantiateNub(void)
411 {
412 IOService *nub;
413
414 /* Instantiate a generic CDROM nub so a generic driver can match above us. */
415
416 nub = new IOSCSICDDriveNub;
417 return(nub);
418 }
419
420 void
421 IOSCSICDDrive::mediaArrived(void)
422 {
423 }
424
425 void
426 IOSCSICDDrive::mediaGone(void)
427 {
428 }
429
430 IOReturn
431 IOSCSICDDrive::readISRC(UInt8 track,CDISRC isrc)
432 {
433 IOReturn result;
434 UInt8 *tempBuf;
435
436 /* Get a buffer for the returned data: */
437
438 result = allocateTempBuffer(&tempBuf,24);
439 if (result != kIOReturnSuccess) {
440 return(kIOReturnNoMemory);
441 }
442
443 result = readSubChannel(tempBuf,24,IORSCcdb::kISRC,track);
444 if (result == kIOReturnSuccess) {
445 assert(tempBuf[2] == 0);
446 assert(tempBuf[3] == 20);
447 assert(tempBuf[4] == 3);
448
449 if ((tempBuf[8] & 0x80)) { /* return the ISRC */
450 bcopy(&tempBuf[9],isrc,kCDISRCMaxLength);
451 isrc[kCDISRCMaxLength] = '\0';
452 } else {
453 result = kIOReturnNotFound;
454 }
455 }
456
457 deleteTempBuffer(tempBuf,24);
458
459 return(result);
460 }
461
462 IOReturn
463 IOSCSICDDrive::readMCN(CDMCN mcn)
464 {
465 IOReturn result;
466 UInt8 *tempBuf;
467
468 /* Get a buffer for the returned data: */
469
470 result = allocateTempBuffer(&tempBuf,24);
471 if (result != kIOReturnSuccess) {
472 return(kIOReturnNoMemory);
473 }
474
475 result = readSubChannel(tempBuf,24,IORSCcdb::kMCN,0);
476 if (result == kIOReturnSuccess) {
477 assert(tempBuf[2] == 0);
478 assert(tempBuf[3] == 20);
479 assert(tempBuf[4] == 2);
480
481 if ((tempBuf[8] & 0x80)) { /* return the MCN */
482 bcopy(&tempBuf[9],mcn,kCDMCNMaxLength);
483 mcn[kCDMCNMaxLength] = '\0';
484 } else {
485 result = kIOReturnNotFound;
486 }
487 }
488
489 deleteTempBuffer(tempBuf,24);
490
491 return(result);
492 }
493
494
495 IOReturn
496 IOSCSICDDrive::readSubChannel(UInt8 *buffer,UInt32 length,UInt8 dataFormat,UInt8 trackNumber)
497 {
498 struct context *cx;
499 struct IORSCcdb *c;
500 IOSCSICommand *req;
501 SCSICDBInfo scsiCDB;
502 IOReturn result;
503
504 cx = allocateContext();
505 if (cx == NULL) {
506 return(kIOReturnNoMemory);
507 }
508
509 req = cx->scsireq;
510
511 bzero( &scsiCDB, sizeof(scsiCDB) );
512
513 bzero(buffer,length);
514
515 c = (struct IORSCcdb *)(scsiCDB.cdb);
516
517 c->opcode = kIOSCSICommandReadSubChannel;
518 c->lunbits = 0;
519 c->lunbits |= IORSCcdb::kMSF;
520 c->subq = IORSCcdb::kSubq;
521 c->dataformat = dataFormat;
522 c->track = trackNumber; /* any valid track will do */
523 c->reserved1 = 0;
524 c->reserved2 = 0;
525 c->len_hi = length >> 8;
526 c->len_lo = length & 0xff;
527 c->ctlbyte = 0;
528
529 scsiCDB.cdbLength = 10;
530 req->setCDB( &scsiCDB );
531 req->setPointers(cx->senseDataDesc, 255, false, true);
532
533 cx->memory = IOMemoryDescriptor::withAddress((void *)buffer,
534 length,
535 kIODirectionIn);
536 req->setPointers( cx->memory, length, false );
537
538 req->setTimeout( 5000 );
539
540 result = simpleSynchIO(cx);
541
542 deleteContext(cx);
543
544 return(result);
545 }
546
547 IOReturn
548 IOSCSICDDrive::readTOC(IOMemoryDescriptor *buffer)
549 {
550 struct context *cx;
551 struct IOReadToccdb *c;
552 IOSCSICommand *req;
553 SCSICDBInfo scsiCDB;
554 IOReturn result;
555
556 cx = allocateContext();
557 if (cx == NULL) {
558 return(kIOReturnNoMemory);
559 }
560
561 req = cx->scsireq;
562
563 bzero( &scsiCDB, sizeof(scsiCDB) );
564
565 c = (struct IOReadToccdb *)scsiCDB.cdb;
566
567 c->opcode = kIOSCSICommandReadTOC;
568 c->lunbits = IOReadToccdb::kMSF;
569 c->reserved1 = 0;
570 c->reserved2 = 0;
571 c->reserved3 = 0;
572 c->reserved4 = 0;
573 c->start_trk_session = 0;
574 c->len_hi = buffer->getLength() >> 8;
575 c->len_lo = buffer->getLength() & 0xff;
576 c->ctlbyte = IOReadToccdb::kFullTOC << 6; /* old format field */
577
578 scsiCDB.cdbLength = 10;
579 req->setCDB( &scsiCDB );
580 req->setPointers(cx->senseDataDesc, 255, false, true);
581
582 buffer->retain(); /* bump the retain count */
583 cx->memory = buffer;
584
585 req->setPointers( cx->memory, cx->memory->getLength(), false );
586
587 req->setTimeout( 5000 );
588
589 result = simpleSynchIO(cx);
590
591 deleteContext(cx);
592
593 #ifdef HOLLYWOOD_BCD_TO_HEX_SUPPORT
594 IOByteCount tocMaxSize;
595 CDTOC *toc = buffer->getVirtualSegment(0, &tocMaxSize);
596
597 /* Convert BCD-encoded values in TOC to hex values. */
598 if (toc && tocMaxSize >= sizeof(UInt32)) {
599 UInt32 count = (tocMaxSize - sizeof(UInt32)) / sizeof(CDTOCDescriptor);
600 for (UInt32 index = 0; index < count; index++) {
601 if (toc->descriptors[index].point <= 0x99) {
602 ConvertBCDToHex(&toc->descriptors[index].point);
603 }
604 if ((toc->descriptors[index].point & 0xf0) == 0xb0) {
605 ConvertBCDToHex(&toc->descriptors[index].address.minute);
606 ConvertBCDToHex(&toc->descriptors[index].address.second);
607 ConvertBCDToHex(&toc->descriptors[index].address.frame);
608 ConvertBCDToHex(&toc->descriptors[index].zero);
609 }
610 if ( toc->descriptors[index].point <= 0x99 ||
611 ( toc->descriptors[index].point >= 0xa0 &&
612 toc->descriptors[index].point <= 0xc0 ) ) {
613 ConvertBCDToHex(&toc->descriptors[index].p.minute);
614 if (toc->descriptors[index].point != 0xa0) {
615 ConvertBCDToHex(&toc->descriptors[index].p.second);
616 }
617 ConvertBCDToHex(&toc->descriptors[index].p.frame);
618 }
619 }
620 }
621 #endif HOLLYWOOD_BCD_TO_HEX_SUPPORT
622
623 return(result);
624 }
625
626 IOReturn
627 IOSCSICDDrive::reportMaxWriteTransfer(UInt64 /* blockSize */,UInt64 * /* max */)
628 {
629 return(0);
630 }
631
632 IOReturn
633 IOSCSICDDrive::reportMediaState(bool *mediaPresent,bool *changed)
634 {
635 IOReturn result;
636
637 result = super::reportMediaState(mediaPresent,changed);
638
639 if (result != kIOReturnSuccess) {
640 IOLog("%s[IOSCSICDDrive]::reportMediaState; result=%s, changed = %s, present = %s\n",
641 getName(),stringFromReturn(result),*changed ? "Y" : "N", *mediaPresent ? "Y" : "N");
642 }
643
644 if ((result == kIOReturnSuccess) && *changed) { /* the media state changed */
645 if (*mediaPresent) { /* new media inserted */
646 mediaArrived();
647 } else { /* media went away */
648 mediaGone();
649 }
650 }
651
652 /* We don't return the result of our internal operations. But since they
653 * indicate a problem, we probably should report some kind of problem,
654 * or maybe just ignore the media change.
655 */
656
657 return(result);
658 }
659
660 IOReturn
661 IOSCSICDDrive::reportWriteProtection(bool *isWriteProtected)
662 {
663 *isWriteProtected = true;
664 return(kIOReturnSuccess);
665 }
666
667 IOReturn
668 IOSCSICDDrive::setAudioVolume(UInt8 leftVolume,UInt8 rightVolume)
669 {
670 struct context *cx;
671 SCSICDBInfo scsiCmd;
672 IOReturn result;
673 UInt8 audio_control[28];
674
675 cx = allocateContext();
676 if (cx == NULL) {
677 return(kIOReturnNoMemory);
678 }
679
680 cx->memory = IOMemoryDescriptor::withAddress(audio_control,
681 sizeof(audio_control),
682 kIODirectionIn);
683 if (cx->memory == NULL) {
684 deleteContext(cx);
685 return(kIOReturnNoMemory);
686 }
687
688 /* Get current values. */
689
690 bzero(&scsiCmd, sizeof(scsiCmd));
691
692 scsiCmd.cdbLength = 6;
693 scsiCmd.cdb[0] = 0x1a; /* MODE SENSE (6) */
694 scsiCmd.cdb[2] = 0x0e; /* PAGE CODE E */
695 scsiCmd.cdb[4] = sizeof(audio_control);
696
697 cx->scsireq->setCDB(&scsiCmd);
698 cx->scsireq->setPointers(cx->memory, sizeof(audio_control), true);
699 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
700 cx->scsireq->setTimeout(5000);
701
702 result = simpleSynchIO(cx);
703
704 if (result == kIOReturnSuccess) {
705 assert((audio_control[ 0] ) == 28-1);
706 assert((audio_control[ 3] ) == 0x08);
707 assert((audio_control[12] & 0x3f) == 0x0e);
708 assert((audio_control[13] ) == 0x0e);
709
710 /* Set new values. */
711
712 audio_control[21] = audio_control[25] = leftVolume;
713 audio_control[23] = audio_control[27] = rightVolume;
714
715 deleteContext(cx);
716
717 cx = allocateContext();
718 if (cx == NULL) {
719 return(kIOReturnNoMemory);
720 }
721
722 cx->memory = IOMemoryDescriptor::withAddress(audio_control,
723 sizeof(audio_control),
724 kIODirectionOut);
725 if (cx->memory == NULL) {
726 deleteContext(cx);
727 return(kIOReturnNoMemory);
728 }
729
730 bzero(&scsiCmd, sizeof(scsiCmd));
731
732 scsiCmd.cdbLength = 6;
733 scsiCmd.cdb[0] = 0x15; /* MODE SELECT (6) */
734 scsiCmd.cdb[4] = sizeof(audio_control);
735
736 cx->scsireq->setCDB(&scsiCmd);
737 cx->scsireq->setPointers(cx->memory, sizeof(audio_control), true);
738 cx->scsireq->setPointers(cx->senseDataDesc, 255, false, true);
739 cx->scsireq->setTimeout(5000);
740
741 result = simpleSynchIO(cx);
742 }
743
744 deleteContext(cx);
745
746 return(result);
747 }