]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOATAPIDVDDrive/IOATAPIDVDDrive.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Families / IOATAPIDVDDrive / IOATAPIDVDDrive.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/assert.h>
24 #include <IOKit/IOBufferMemoryDescriptor.h>
25 #include <IOKit/storage/ata/IOATAPIDVDDrive.h>
26 #include <IOKit/storage/ata/IOATAPIDVDDriveNub.h>
27 #include <IOKit/storage/IODVDTypes.h>
28 #include <libkern/OSByteOrder.h>
29
30 // Define this to log debug messages.
31 //
32 // #define LOG_DVD_MESSAGES 1
33
34 #ifdef LOG_DVD_MESSAGES
35 #define DEBUG_LOG(fmt, args...) IOLog(fmt, ## args)
36 #else
37 #define DEBUG_LOG(fmt, args...)
38 #endif
39
40 #define super IOATAPICDDrive
41 OSDefineMetaClassAndStructors( IOATAPIDVDDrive, IOATAPICDDrive )
42
43 //---------------------------------------------------------------------------
44 // Feature header and descriptor definition.
45
46 struct featureHdr {
47 UInt32 totalLen;
48 UInt8 reserved1[2];
49 UInt16 currentProfile;
50 };
51
52 struct featureDescriptor {
53 UInt16 featureCode;
54 UInt8 versionPC;
55 UInt8 additionalLength;
56 };
57
58 #define kConfigBufferSize 2048
59 #define kConfigFeatureHeaderBytes sizeof(struct featureHdr)
60 #define kConfigDataLengthBytes sizeof(UInt32)
61 #define kConfigMinDataLength (sizeof(struct featureHdr) - \
62 kConfigDataLengthBytes + \
63 sizeof(struct featureDescriptor))
64
65 //---------------------------------------------------------------------------
66 // Classify an ATAPI CD-ROM drive as a CD or a DVD drive.
67
68 IOReturn IOATAPIDVDDrive::classifyDrive(bool * isDVDDrive)
69 {
70 IOATACommand * cmd = 0;
71 IOBufferMemoryDescriptor * copyrightDesc;
72 IOBufferMemoryDescriptor * senseDesc;
73 IOReturn ret = kIOReturnSuccess;
74
75 do {
76 // Buffer descriptor to hold Copyright Information.
77
78 copyrightDesc = IOBufferMemoryDescriptor::withCapacity(8,
79 kIODirectionIn);
80
81 // Buffer descriptor to hold sense data.
82
83 senseDesc = IOBufferMemoryDescriptor::withCapacity(
84 sizeof(ATASenseData),
85 kIODirectionIn);
86
87 if ( (copyrightDesc == 0) || (senseDesc == 0) )
88 {
89 ret = kIOReturnNoMemory;
90 break;
91 }
92
93 bzero(senseDesc->getBytesNoCopy(), senseDesc->getCapacity());
94
95 // READ DVD STRUCTURE command - DVD Copyright Information
96
97 cmd = atapiCommandReadDVDStructure(copyrightDesc,
98 kIODVDReadStructureCopyright);
99 if (cmd == 0)
100 {
101 ret = kIOReturnNoMemory;
102 break;
103 }
104
105 // Execute the command, and get sense data.
106
107 ret = syncExecute(cmd,
108 kATADefaultTimeout,
109 kATADefaultRetries,
110 senseDesc);
111
112 // By default, consider it a DVD drive, unless the drive
113 // returns an error, and the sense data contains,
114 //
115 // KEY = 0x05
116 // ASC = 0x20
117 // ASCQ = 0x00
118
119 *isDVDDrive = true;
120
121 if (ret != kIOReturnSuccess)
122 {
123 ATASenseData * senseData;
124
125 senseData = (ATASenseData *) senseDesc->getBytesNoCopy();
126
127 if ((senseData->errorCode == kATAPISenseCurrentErr) ||
128 (senseData->errorCode == kATAPISenseDeferredErr))
129 {
130 if ((senseData->senseKey == kATAPISenseIllegalReq) &&
131 (senseData->additionalSenseCode == 0x20) &&
132 (senseData->additionalSenseQualifier == 0x0))
133 {
134 *isDVDDrive = false;
135 }
136 }
137
138 ret = kIOReturnSuccess;
139 }
140 }
141 while (false);
142
143 if (senseDesc) senseDesc->release();
144 if (copyrightDesc) copyrightDesc->release();
145 if (cmd) cmd->release();
146
147 return ret;
148 }
149
150 //---------------------------------------------------------------------------
151 // Determine the media type (book type) in the DVD drive.
152
153 IOReturn
154 IOATAPIDVDDrive::determineMediaType(UInt32 * mediaType)
155 {
156 IOATACommand * cmd = 0;
157 IOBufferMemoryDescriptor * dataDesc;
158 IODVDStructurePhysical * data;
159 IOReturn ret = kIOReturnSuccess;
160
161 *mediaType = kDVDMediaTypeUnknown;
162
163 do {
164 // Buffer descriptor to hold Physical Format Information.
165
166 dataDesc = IOBufferMemoryDescriptor::withCapacity(sizeof(*data),
167 kIODirectionIn);
168
169 if ( dataDesc == 0 )
170 {
171 ret = kIOReturnNoMemory;
172 break;
173 }
174
175 data = (IODVDStructurePhysical *) dataDesc->getBytesNoCopy();
176 bzero(data, sizeof(data->length));
177
178 // READ DVD STRUCTURE command - Physical Format Information
179
180 cmd = atapiCommandReadDVDStructure(dataDesc,
181 kIODVDReadStructurePhysical);
182 if ( cmd == 0 )
183 {
184 ret = kIOReturnNoMemory;
185 break;
186 }
187
188 // Execute the command.
189
190 if ( syncExecute(cmd) != kIOReturnSuccess )
191 {
192 *mediaType = kCDMediaTypeROM; // Assume its a CD.
193 }
194 else if ( IODVDGetDataLength16(data->length) <
195 (sizeof(*data) - sizeof(data->length)) )
196 {
197 ret = kIOReturnUnderrun;
198 }
199 else
200 {
201 DEBUG_LOG("%s: DVD Book Type: %x Part Version: %x\n",
202 getName(), data->bookType, data->partVersion);
203
204 switch (data->bookType)
205 {
206 default:
207 case kIODVDBookTypeDVDROM:
208 case kIODVDBookTypeDVDR:
209 case kIODVDBookTypeDVDRW:
210 case kIODVDBookTypeDVDPlusRW:
211 *mediaType = kDVDMediaTypeROM;
212 break;
213
214 case kIODVDBookTypeDVDRAM:
215 *mediaType = kDVDMediaTypeRAM;
216 break;
217 }
218 }
219 }
220 while (false);
221
222 if (dataDesc) dataDesc->release();
223 if (cmd) cmd->release();
224
225 return ret;
226 }
227
228 //---------------------------------------------------------------------------
229 // Perform active matching with an ATAPI device nub published by the
230 // ATA controller driver.
231
232 bool
233 IOATAPIDVDDrive::matchATAPIDeviceType(UInt8 type, SInt32 * score)
234 {
235 bool isDVDDrive;
236 bool match = false;
237
238 do {
239 // If the device type reported by INQUIRY data is not a CD-ROM type,
240 // then give up immediately.
241
242 if ( type != kIOATAPIDeviceTypeCDROM )
243 break;
244
245 // Select timing protocol before performing I/O.
246
247 if ( selectTimingProtocol() == false )
248 break;
249
250 // Is this unit a DVD drive?
251
252 if ( classifyDrive(&isDVDDrive) != kIOReturnSuccess )
253 break;
254
255 if ( isDVDDrive )
256 {
257 // Indicate a strong affinity for the DVD drive by setting
258 // a higher probe score when a DVD drive is detected.
259
260 DEBUG_LOG("%s::%s DVD drive detected\n", getName(), __FUNCTION__);
261 *score = 20;
262 match = true;
263 }
264 else
265 {
266 // Not a DVD drive.
267 DEBUG_LOG("%s::%s Not a DVD drive\n", getName(), __FUNCTION__);
268 }
269 }
270 while (false);
271
272 return match;
273 }
274
275 //---------------------------------------------------------------------------
276 // GET CONFIGURATION command.
277
278 IOReturn
279 IOATAPIDVDDrive::getConfiguration(UInt8 * buf,
280 UInt32 length,
281 UInt32 * actualLength,
282 bool current)
283 {
284 IOMemoryDescriptor * bufDesc = 0;
285 IOATACommand * cmd = 0;
286 IOReturn ret = kIOReturnNoMemory;
287 ATAResults results;
288
289 do {
290 bufDesc = IOMemoryDescriptor::withAddress(buf, length, kIODirectionIn);
291 if (bufDesc == 0)
292 break;
293
294 cmd = atapiCommandGetConfiguration(bufDesc, 0x01);
295 if (cmd == 0)
296 break;
297
298 ret = syncExecute(cmd);
299
300 cmd->getResults(&results);
301 *actualLength = results.bytesTransferred;
302 }
303 while (0);
304
305 if (cmd) cmd->release();
306 if (bufDesc) bufDesc->release();
307
308 return ret;
309 }
310
311 //---------------------------------------------------------------------------
312 // Report disk type.
313
314 const char *
315 IOATAPIDVDDrive::getDeviceTypeName()
316 {
317 return kIOBlockStorageDeviceTypeDVD;
318 }
319
320 //---------------------------------------------------------------------------
321 // Report the type of media in the DVD drive.
322
323 UInt32
324 IOATAPIDVDDrive::getMediaType()
325 {
326 UInt32 mediaType;
327
328 determineMediaType(&mediaType);
329
330 return mediaType;
331 }
332
333 //---------------------------------------------------------------------------
334 // Initialize the IOATAPIDVDDrive object.
335
336 bool
337 IOATAPIDVDDrive::init(OSDictionary * properties)
338 {
339 return super::init(properties);
340 }
341
342 //---------------------------------------------------------------------------
343 // Instantiate an IOATAPIDVDDriveNub nub.
344
345 IOService *
346 IOATAPIDVDDrive::instantiateNub(void)
347 {
348 IOService * nub = new IOATAPIDVDDriveNub;
349
350 /* Instantiate a generic DVD nub so a generic driver can match above us. */
351
352 return nub;
353 }
354
355 //---------------------------------------------------------------------------
356 // Report the media state.
357
358 IOReturn
359 IOATAPIDVDDrive::reportMediaState(bool * mediaPresent, bool * changed)
360 {
361 IOReturn result;
362
363 // Let superclass check for media in a generic fashion.
364
365 result = super::reportMediaState(mediaPresent, changed);
366
367 #if 0 // For testing only
368
369 if (result != kIOReturnSuccess)
370 {
371 return result;
372 }
373
374 // For a new media, determine its type.
375
376 if (*mediaPresent && *changed)
377 {
378 getMediaType();
379 }
380 #endif
381
382 return result;
383 }
384
385 //---------------------------------------------------------------------------
386 // Report random write support.
387
388 IOReturn
389 IOATAPIDVDDrive::reportWriteProtection(bool * isWriteProtected)
390 {
391 UInt32 len;
392 struct featureHdr * fh;
393 struct featureDescriptor * fdp;
394 IOReturn result;
395 UInt8 * configBuf;
396 UInt32 configBufSize; /* not used */
397
398 *isWriteProtected = true;
399
400 /* Allocate memory for the configuration data.
401 Theoretically, this can be up to 65534 bytes. */
402
403 configBuf = (UInt8 *) IOMalloc(kConfigBufferSize);
404 if ( configBuf == 0 )
405 return kIOReturnNoMemory;
406
407 bzero((void *) configBuf, kConfigBufferSize);
408
409 /* Get the *current* configuration information, relating to the media. */
410
411 do {
412 result = getConfiguration(configBuf,
413 kConfigBufferSize,
414 &configBufSize,
415 true); /* Get current (active) features */
416
417 if (result == kIOReturnUnderrun)
418 {
419 // getConfiguration() will report an underrun.
420 result = kIOReturnSuccess;
421 }
422
423 if (result != kIOReturnSuccess)
424 {
425 DEBUG_LOG("%s::%s getConfiguration() error = %s\n",
426 getName(), __FUNCTION__, stringFromReturn(result));
427 result = kIOReturnSuccess;
428 break;
429 }
430
431 fh = (struct featureHdr *) configBuf;
432 len = OSSwapBigToHostInt32(fh->totalLen);
433
434 if (len < kConfigMinDataLength)
435 {
436 result = kIOReturnUnderrun;
437 break;
438 }
439
440 // total length, including the Data Length field.
441 //
442 len += kConfigDataLengthBytes;
443 len = min(len, kConfigBufferSize);
444 DEBUG_LOG("%s::%s config length = %ld\n", getName(), __FUNCTION__, len);
445
446 // Points to the first Feature Descriptor after the Feature Header.
447 //
448 fdp = (struct featureDescriptor *)
449 &configBuf[kConfigFeatureHeaderBytes];
450
451 do {
452 if (OSSwapBigToHostInt16(fdp->featureCode) ==
453 kIOATAPIFeatureRandomWrite)
454 {
455 *isWriteProtected = false;
456 break;
457 }
458
459 fdp = (struct featureDescriptor *)((char *)fdp +
460 sizeof(struct featureDescriptor) +
461 fdp->additionalLength);
462 }
463 while ( ((UInt8 *)fdp + sizeof(*fdp)) <= &configBuf[len] );
464 }
465 while (false);
466
467 IOFree((void *) configBuf, kConfigBufferSize);
468
469 return result;
470 }
471
472 //---------------------------------------------------------------------------
473 // SEND KEY command.
474
475 IOReturn
476 IOATAPIDVDDrive::sendKey(IOMemoryDescriptor * buffer,
477 const DVDKeyClass keyClass,
478 const UInt8 agid,
479 const DVDKeyFormat keyFormat)
480 {
481 IOATACommand * cmd = 0;
482 IOReturn ret = kIOReturnNoMemory;
483
484 do {
485 assert(buffer);
486
487 cmd = atapiCommandSendKey(buffer, keyClass, agid, keyFormat);
488 if (cmd == 0)
489 break;
490
491 ret = syncExecute(cmd);
492 }
493 while (0);
494
495 if (cmd)
496 cmd->release();
497
498 return ret;
499 }
500
501 //---------------------------------------------------------------------------
502 // REPORT KEY command.
503
504 IOReturn
505 IOATAPIDVDDrive::reportKey(IOMemoryDescriptor * buffer,
506 const DVDKeyClass keyClass,
507 const UInt32 lba,
508 const UInt8 agid,
509 const DVDKeyFormat keyFormat)
510 {
511 IOATACommand * cmd = 0;
512 IOReturn ret = kIOReturnNoMemory;
513
514 do {
515 assert(buffer);
516
517 cmd = atapiCommandReportKey(buffer, keyClass, lba, agid, keyFormat);
518 if (cmd == 0)
519 break;
520
521 ret = syncExecute(cmd);
522 }
523 while (0);
524
525 if (cmd)
526 cmd->release();
527
528 return ret;
529 }