1 /*****************************************************************************
2 * DVDioctl.cpp: Linux-like DVD driver for Darwin and MacOS X
3 *****************************************************************************
4 * Copyright (C) 1998-2000 Apple Computer, Inc. All rights reserved.
5 * Copyright (C) 2001 VideoLAN
6 * $Id: DVDioctl.cpp,v 1.7 2001/06/25 11:34:08 sam Exp $
8 * Authors: Samuel Hocevar <sam@zoy.org>
9 * Eugenio Jarosiewicz <ej0@cise.ufl.edu>
11 * The contents of this file constitute Original Code as defined in and
12 * are subject to the Apple Public Source License Version 1.1 (the
13 * "License"). You may not use this file except in compliance with the
14 * License. Please obtain a copy of the License at
15 * http://www.apple.com/publicsource and read it before using this file.
17 * This Original Code and all software distributed under the License are
18 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
19 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
20 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
22 * License for the specific language governing rights and limitations
24 *****************************************************************************/
26 /*****************************************************************************
28 * - add a timeout to waitForService() so that we don't wait forever
29 * - find a way to prevent user from ejecting DVD using the GUI while
31 *****************************************************************************/
33 /*****************************************************************************
35 *****************************************************************************/
38 #include <sys/param.h>
40 #include <sys/syslog.h>
41 #include <sys/systm.h>
42 #include <sys/ioccom.h>
43 #include <sys/fcntl.h>
46 #include <miscfs/devfs/devfs.h>
48 #include <mach/mach_types.h>
51 #include <IOKit/IOLib.h>
52 #include <IOKit/IOService.h>
53 #include <IOKit/storage/IOMedia.h>
54 #include <IOKit/storage/IODVDMedia.h>
55 #include <IOKit/storage/IODVDBlockStorageDriver.h>
57 #undef CONTROL //some include above #defines this and breaks the next include...grr.
58 #include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h>
59 #include <IOKit/scsi-commands/IODVDServices.h>
63 /*****************************************************************************
65 *****************************************************************************/
66 class DVDioctl : public IOService
68 OSDeclareDefaultStructors( DVDioctl )
72 virtual bool init ( OSDictionary *dictionary = 0 );
73 virtual IOService *probe ( IOService *provider, SInt32 *score );
74 virtual bool start ( IOService *provider );
75 virtual void stop ( IOService *provider );
76 virtual void free ( void );
79 #define super IOService
80 OSDefineMetaClassAndStructors( DVDioctl, IOService )
82 /*****************************************************************************
84 *****************************************************************************/
85 typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
86 typedef struct dio { dev_t dev; struct uio * uio; } dio_t;
87 typedef struct buf buf_t;
90 /*****************************************************************************
92 *****************************************************************************/
93 static int DVDClose ( dev_t, int, int, struct proc * );
94 static int DVDBlockIoctl ( dev_t, u_long, caddr_t, int, struct proc * );
95 static int DVDOpen ( dev_t, int, int, struct proc * );
96 static int DVDSize ( dev_t );
97 static void DVDStrategy ( buf_t * );
98 static int DVDReadWrite ( dkr_t, dkrtype_t );
99 static void DVDReadWriteCompletion( void *, void *, IOReturn, UInt64 );
101 static struct bdevsw device_functions =
103 DVDOpen, DVDClose, DVDStrategy, DVDBlockIoctl, eno_dump, DVDSize, D_DISK
106 /*****************************************************************************
108 *****************************************************************************/
109 static DVDioctl * p_this = NULL;
114 static IODVDMedia *p_dvd;
115 static IODVDBlockStorageDriver *p_drive;
116 static IODVDServices *p_services;
117 static IOSCSIMultimediaCommandsDevice *p_scsi_mcd;
119 /*****************************************************************************
120 * DKR_GET_DEV: borrowed from IOMediaBSDClient.cpp
121 *****************************************************************************/
122 static inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
124 return (dkrtype == DKRTYPE_BUF)
125 ? ((buf_t *)dkr)->b_dev : ((dio_t *)dkr)->dev;
128 /*****************************************************************************
129 * DKR_GET_BYTE_COUNT: borrowed from IOMediaBSDClient.cpp
130 *****************************************************************************/
131 static inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
133 return (dkrtype == DKRTYPE_BUF)
134 ? ((buf_t *)dkr)->b_bcount : ((dio_t *)dkr)->uio->uio_resid;
137 /*****************************************************************************
138 * DKR_GET_BYTE_START: borrowed from IOMediaBSDClient.cpp
139 *****************************************************************************/
140 static inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
142 if (dkrtype == DKRTYPE_BUF)
144 buf_t * bp = (buf_t *)dkr;
145 return bp->b_blkno * p_dvd->getPreferredBlockSize();
147 return ((dio_t *)dkr)->uio->uio_offset;
150 /*****************************************************************************
151 * DKR_IS_READ: borrowed from IOMediaBSDClient.cpp
152 *****************************************************************************/
153 static inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
155 return (dkrtype == DKRTYPE_BUF)
156 ? ((((buf_t *)dkr)->b_flags & B_READ) == B_READ)
157 : ((((dio_t *)dkr)->uio->uio_rw) == UIO_READ);
160 /*****************************************************************************
161 * DKR_IS_ASYNCHRONOUS: borrowed from IOMediaBSDClient.cpp
162 *****************************************************************************/
163 static inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
165 return (dkrtype == DKRTYPE_BUF) ? true : false;
168 /*****************************************************************************
169 * DKR_IS_RAW: borrowed from IOMediaBSDClient.cpp
170 *****************************************************************************/
171 static inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
173 return (dkrtype == DKRTYPE_BUF) ? false : true;
176 /*****************************************************************************
177 * DKR_SET_BYTE_COUNT: borrowed from IOMediaBSDClient.cpp
178 *****************************************************************************/
179 static inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
181 if (dkrtype == DKRTYPE_BUF)
183 ((buf_t *)dkr)->b_resid = ((buf_t *)dkr)->b_bcount - bcount;
187 ((dio_t *)dkr)->uio->uio_resid -= bcount;
191 /*****************************************************************************
192 * DKR_RUN_COMPLETION: borrowed from IOMediaBSDClient.cpp
193 *****************************************************************************/
194 static inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
196 if (dkrtype == DKRTYPE_BUF)
198 buf_t * bp = (buf_t *)dkr;
200 bp->b_error = p_this->errnoFromReturn(status);
201 bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0;
206 /*****************************************************************************
207 * DKR_GET_BUFFER: borrowed from IOMediaBSDClient.cpp
208 *****************************************************************************/
209 static inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
211 if (dkrtype == DKRTYPE_BUF)
213 buf_t * bp = (buf_t *)dkr;
215 if ( (bp->b_flags & B_VECTORLIST) )
217 assert(sizeof(IOPhysicalRange ) == sizeof(iovec ));
218 assert(sizeof(IOPhysicalRange::address) == sizeof(iovec::iov_base));
219 assert(sizeof(IOPhysicalRange::length ) == sizeof(iovec::iov_len ));
220 return IOMemoryDescriptor::withPhysicalRanges(
221 (IOPhysicalRange *) bp->b_vectorlist,
222 (UInt32) bp->b_vectorcount,
223 (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
227 return IOMemoryDescriptor::withAddress(
228 (vm_address_t) bp->b_data,
229 (vm_size_t) bp->b_bcount,
230 (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
231 (bp->b_flags & B_PHYS) ? current_task() : kernel_task );
235 struct uio * uio = ((dio_t *)dkr)->uio;
237 assert(sizeof(IOVirtualRange ) == sizeof(iovec ));
238 assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
239 assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
241 return IOMemoryDescriptor::withRanges(
242 (IOVirtualRange *) uio->uio_iov,
243 (UInt32) uio->uio_iovcnt,
244 (uio->uio_rw == UIO_READ ) ? kIODirectionIn : kIODirectionOut,
245 (uio->uio_segflg != UIO_SYSSPACE) ? current_task() : kernel_task,
250 /*****************************************************************************
251 * DVDioctl::init: initialize the driver structure
252 *****************************************************************************/
253 bool DVDioctl::init( OSDictionary *p_dict = 0 )
255 //IOLog( "DVD ioctl: initializing\n" );
267 bool res = super::init( p_dict );
272 /*****************************************************************************
273 * DVDioctl::probe: check whether the driver can be safely activated
274 *****************************************************************************/
275 IOService * DVDioctl::probe( IOService *provider, SInt32 *score )
277 //IOLog( "DVD ioctl: probing\n" );
278 IOService * res = super::probe( provider, score );
283 /*****************************************************************************
284 * DVDioctl::start: start the driver
285 *****************************************************************************/
286 bool DVDioctl::start( IOService *provider )
288 //IOLog( "DVD ioctl: starting\n" );
290 if( !super::start( provider ) )
295 //IOLog( "DVD ioctl: creating device\n" );
297 i_major = bdevsw_add( -1, &device_functions );
301 //log(LOG_INFO, "DVD ioctl: failed to allocate a major number\n");
305 p_node = devfs_make_node ( makedev( i_major, 0 ), DEVFS_BLOCK,
306 UID_ROOT, GID_WHEEL, 0666, "dvd" );
310 //log( LOG_INFO, "DVD ioctl: failed creating node\n" );
312 if( bdevsw_remove(i_major, &device_functions) == -1 )
314 //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
323 /*****************************************************************************
324 * DVDioctl::stop: stop the driver
325 *****************************************************************************/
326 void DVDioctl::stop( IOService *provider )
328 //IOLog( "DVD ioctl: removing device\n" );
332 devfs_remove( p_node );
337 if( bdevsw_remove(i_major, &device_functions) == -1 )
339 //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
343 //IOLog( "DVD ioctl: stopping\n" );
344 super::stop( provider );
347 /*****************************************************************************
348 * DVDioctl::free: free all resources allocated by the driver
349 *****************************************************************************/
350 void DVDioctl::free( void )
352 //IOLog( "DVD ioctl: freeing\n" );
356 /* following functions are local */
358 /*****************************************************************************
359 * DVDOpen: look for an IODVDMedia object and open it
360 *****************************************************************************/
361 static int DVDOpen( dev_t dev, int flags, int devtype, struct proc * )
363 IOStorageAccess level;
365 /* Check that the device hasn't already been opened */
368 //log( LOG_INFO, "DVD ioctl: already opened\n" );
376 IOService * p_root = IOService::getServiceRoot();
380 //log( LOG_INFO, "DVD ioctl: couldn't find root\n" );
385 OSDictionary * p_dict = p_root->serviceMatching( kIODVDMediaClass );
389 //log( LOG_INFO, "DVD ioctl: couldn't find dictionary\n" );
394 p_dvd = OSDynamicCast( IODVDMedia, p_root->waitForService( p_dict ) );
398 //log( LOG_INFO, "DVD ioctl: couldn't find service\n" );
403 //log( LOG_INFO, "DVD ioctl: found DVD\n" );
405 level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
406 : kIOStorageAccessReader;
408 if( ! p_dvd->open( p_this, 0, level) )
410 log( LOG_INFO, "DVD ioctl: IODVDMedia object busy\n" );
415 p_drive = p_dvd->getProvider();
417 p_services = OSDynamicCast( IODVDServices, p_drive->getProvider() );
419 p_scsi_mcd = OSDynamicCast( IOSCSIMultimediaCommandsDevice, p_services->getProvider() );
421 log( LOG_INFO, "DVD ioctl: IODVDMedia->open()\n" );
426 /*****************************************************************************
427 * DVDClose: close the IODVDMedia object
428 *****************************************************************************/
429 static int DVDClose( dev_t dev, int flags, int devtype, struct proc * )
431 /* Release the device */
432 p_dvd->close( p_this );
440 log( LOG_INFO, "DVD ioctl: IODVDMedia->close()\n" );
445 /*****************************************************************************
446 * DVDSize: return the device size
447 *****************************************************************************/
448 static int DVDSize( dev_t dev )
450 return p_dvd->getPreferredBlockSize();
453 /*****************************************************************************
454 * DVDStrategy: perform read or write operations
455 *****************************************************************************/
456 static void DVDStrategy( buf_t * bp )
458 DVDReadWrite(bp, DKRTYPE_BUF);
462 /*****************************************************************************
463 * DVDBlockIoctl: issue an ioctl on the block device
464 *****************************************************************************/
465 static int DVDBlockIoctl( dev_t dev, u_long cmd, caddr_t addr, int flags,
468 #define p_data (((dvdioctl_data_t *)addr))
469 IOReturn i_ret = EINVAL;
471 /* Only needed for IODVD_READ_STRUCTURE */
473 SCSIServiceResponse response;
475 IOMemoryDescriptor *p_mem;
477 p_mem = IOMemoryDescriptor::withAddress( p_data->p_buffer,
483 case IODVD_READ_STRUCTURE:
485 log( LOG_INFO, "DVD ioctl: IODVD_READ_STRUCTURE\n" );
487 i_ret = kIOReturnUnsupported;
488 response = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
490 /* HACK! - Make GetSCSITask and friends in /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/IOKit/scsi-commands/IOSCSIPrimaryCommandsDevice.h public by moving public: from line 96 to line 79 (as root). It's only a compile time check - not a link time thing, so it should be ok. */
491 p_request = p_scsi_mcd->GetSCSITask( );
493 if ( p_scsi_mcd->READ_DVD_STRUCTURE ( p_request,
498 p_mem->getLength(),//p_data->i_size ?
503 /* The command was successfully built, now send it */
504 response = p_scsi_mcd->SendCommand( p_request );
510 PANIC_NOW(( "IOSCSIMultimediaCommandsDevice:: "
511 "readDVDstruct malformed command" ));
515 if( ( response == kSCSIServiceResponse_TASK_COMPLETE ) &&
516 ( p_request->GetTaskStatus ( ) == kSCSITaskStatus_GOOD ) )
518 i_ret = kIOReturnSuccess;
522 i_ret = kIOReturnError;
525 p_scsi_mcd->ReleaseSCSITask( p_request );
533 log( LOG_INFO, "DVD ioctl: send key to `%s', "
534 "buf %d, class %d, lba N/A, agid %d, format %d\n",
535 p_drive->getDeviceTypeName(),
536 (int)p_data->p_buffer, p_data->i_keyclass,
537 p_data->i_agid, p_data->i_keyformat );
539 i_ret = p_drive->sendKey( p_mem, (DVDKeyClass)p_data->i_keyclass,
541 (DVDKeyFormat)p_data->i_keyformat );
545 case IODVD_REPORT_KEY:
547 log( LOG_INFO, "DVD ioctl: report key from `%s', "
548 "buf %d, class %d, lba %d, agid %d, format %d\n",
549 p_drive->getDeviceTypeName(),
550 (int)p_data->p_buffer, p_data->i_keyclass, p_data->i_lba,
551 p_data->i_agid, p_data->i_keyformat );
553 i_ret = p_drive->reportKey( p_mem, (DVDKeyClass)p_data->i_keyclass,
554 p_data->i_lba, p_data->i_agid,
555 (DVDKeyFormat)p_data->i_keyformat );
561 log( LOG_INFO, "DVD ioctl: unknown ioctl\n" );
572 /*****************************************************************************
573 * DVDReadWrite: borrowed from IOMediaBSDClient.cpp
574 *****************************************************************************/
575 static int DVDReadWrite(dkr_t dkr, dkrtype_t dkrtype)
577 IOMemoryDescriptor * buffer;
578 register UInt64 byteCount;
579 register UInt64 byteStart;
583 byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);
584 byteStart = DKR_GET_BYTE_START(dkr, dkrtype);
585 mediaSize = p_dvd->getSize();
587 if ( byteStart >= mediaSize )
589 status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError; goto dkreadwriteErr;
592 if ( DKR_IS_RAW(dkr, dkrtype) )
594 UInt64 mediaBlockSize = p_dvd->getPreferredBlockSize();
596 if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
598 status = kIOReturnNotAligned;
603 buffer = DKR_GET_BUFFER(dkr, dkrtype);
607 status = kIOReturnNoMemory;
611 if ( byteCount > mediaSize - byteStart )
613 IOMemoryDescriptor * originalBuffer = buffer;
615 buffer = IOMemoryDescriptor::withSubRange( originalBuffer, 0,
616 mediaSize - byteStart, originalBuffer->getDirection() );
617 originalBuffer->release();
620 status = kIOReturnNoMemory;
625 if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )
627 IOStorageCompletion completion;
629 completion.target = dkr;
630 completion.action = DVDReadWriteCompletion;
631 completion.parameter = (void *) dkrtype;
633 if ( DKR_IS_READ(dkr, dkrtype) )
635 p_dvd->read( p_this, byteStart, buffer, completion );
639 p_dvd->write( p_this, byteStart, buffer, completion );
642 status = kIOReturnSuccess;
646 if ( DKR_IS_READ(dkr, dkrtype) )
648 status = p_dvd->IOStorage::read( p_this, byteStart,
649 buffer, &byteCount );
653 status = p_dvd->IOStorage::write( p_this, byteStart,
654 buffer, &byteCount );
657 DVDReadWriteCompletion(dkr, (void *)dkrtype, status, byteCount);
661 return p_this->errnoFromReturn(status);
664 DVDReadWriteCompletion(dkr, (void *)dkrtype, status, 0);
666 return p_this->errnoFromReturn(status);
669 /*****************************************************************************
670 * DVDReadWriteCompletion: borrowed from IOMediaBSDClient.cpp
671 *****************************************************************************/
672 static void DVDReadWriteCompletion( void * target,
675 UInt64 actualByteCount )
677 dkr_t dkr = (dkr_t) target;
678 dkrtype_t dkrtype = (dkrtype_t) (int) parameter;
679 dev_t dev = DKR_GET_DEV(dkr, dkrtype);
681 if ( status != kIOReturnSuccess )
683 IOLog( "DVD ioctl: %s (is the disc authenticated ?)\n",
684 p_this->stringFromReturn(status) );
687 DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);
688 DKR_RUN_COMPLETION(dkr, dkrtype, status);