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.5 2001/04/17 14:39:51 sam Exp $
8 * Authors: Samuel Hocevar <sam@zoy.org>
10 * The contents of this file constitute Original Code as defined in and
11 * are subject to the Apple Public Source License Version 1.1 (the
12 * "License"). You may not use this file except in compliance with the
13 * License. Please obtain a copy of the License at
14 * http://www.apple.com/publicsource and read it before using this file.
16 * This Original Code and all software distributed under the License are
17 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
18 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
19 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
21 * License for the specific language governing rights and limitations
23 *****************************************************************************/
25 /*****************************************************************************
27 * - add a timeout to waitForService() so that we don't wait forever
28 * - find a way to prevent user from ejecting DVD using the GUI while
30 *****************************************************************************/
32 //XXX: uncomment to activate the key exchange ioctls - may hang the machine
33 //#define ACTIVATE_DANGEROUS_IOCTL 1
35 /*****************************************************************************
37 *****************************************************************************/
40 #include <sys/param.h>
42 #include <sys/syslog.h>
43 #include <sys/systm.h>
44 #include <sys/ioccom.h>
45 #include <sys/fcntl.h>
48 #include <miscfs/devfs/devfs.h>
50 #include <mach/mach_types.h>
53 #include <IOKit/IOLib.h>
54 #include <IOKit/IOService.h>
55 #include <IOKit/storage/IOMedia.h>
56 #include <IOKit/storage/IODVDMedia.h>
57 #include <IOKit/storage/IODVDBlockStorageDriver.h>
61 /*****************************************************************************
63 *****************************************************************************/
64 class DVDioctl : public IOService
66 OSDeclareDefaultStructors( DVDioctl )
70 virtual bool init ( OSDictionary *dictionary = 0 );
71 virtual IOService *probe ( IOService *provider, SInt32 *score );
72 virtual bool start ( IOService *provider );
73 virtual void stop ( IOService *provider );
74 virtual void free ( void );
77 #define super IOService
78 OSDefineMetaClassAndStructors( DVDioctl, IOService )
80 /*****************************************************************************
82 *****************************************************************************/
83 typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
84 typedef struct dio { dev_t dev; struct uio * uio; } dio_t;
85 typedef struct buf buf_t;
88 /*****************************************************************************
90 *****************************************************************************/
91 static int DVDClose ( dev_t, int, int, struct proc * );
92 static int DVDBlockIoctl ( dev_t, u_long, caddr_t, int, struct proc * );
93 static int DVDOpen ( dev_t, int, int, struct proc * );
94 static int DVDSize ( dev_t );
95 static void DVDStrategy ( buf_t * );
96 static int DVDReadWrite ( dkr_t, dkrtype_t );
97 static void DVDReadWriteCompletion( void *, void *, IOReturn, UInt64 );
99 static struct bdevsw device_functions =
101 DVDOpen, DVDClose, DVDStrategy, DVDBlockIoctl, eno_dump, DVDSize, D_DISK
104 /*****************************************************************************
106 *****************************************************************************/
107 static DVDioctl * p_this = NULL;
112 static IODVDMedia *p_dvd;
113 static IODVDBlockStorageDriver *p_drive;
115 /*****************************************************************************
116 * DKR_GET_DEV: borrowed from IOMediaBSDClient.cpp
117 *****************************************************************************/
118 static inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
120 return (dkrtype == DKRTYPE_BUF)
121 ? ((buf_t *)dkr)->b_dev : ((dio_t *)dkr)->dev;
124 /*****************************************************************************
125 * DKR_GET_BYTE_COUNT: borrowed from IOMediaBSDClient.cpp
126 *****************************************************************************/
127 static inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
129 return (dkrtype == DKRTYPE_BUF)
130 ? ((buf_t *)dkr)->b_bcount : ((dio_t *)dkr)->uio->uio_resid;
133 /*****************************************************************************
134 * DKR_GET_BYTE_START: borrowed from IOMediaBSDClient.cpp
135 *****************************************************************************/
136 static inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
138 if (dkrtype == DKRTYPE_BUF)
140 buf_t * bp = (buf_t *)dkr;
141 return bp->b_blkno * p_dvd->getPreferredBlockSize();
143 return ((dio_t *)dkr)->uio->uio_offset;
146 /*****************************************************************************
147 * DKR_IS_READ: borrowed from IOMediaBSDClient.cpp
148 *****************************************************************************/
149 static inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
151 return (dkrtype == DKRTYPE_BUF)
152 ? ((((buf_t *)dkr)->b_flags & B_READ) == B_READ)
153 : ((((dio_t *)dkr)->uio->uio_rw) == UIO_READ);
156 /*****************************************************************************
157 * DKR_IS_ASYNCHRONOUS: borrowed from IOMediaBSDClient.cpp
158 *****************************************************************************/
159 static inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
161 return (dkrtype == DKRTYPE_BUF) ? true : false;
164 /*****************************************************************************
165 * DKR_IS_RAW: borrowed from IOMediaBSDClient.cpp
166 *****************************************************************************/
167 static inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
169 return (dkrtype == DKRTYPE_BUF) ? false : true;
172 /*****************************************************************************
173 * DKR_SET_BYTE_COUNT: borrowed from IOMediaBSDClient.cpp
174 *****************************************************************************/
175 static inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
177 if (dkrtype == DKRTYPE_BUF)
179 ((buf_t *)dkr)->b_resid = ((buf_t *)dkr)->b_bcount - bcount;
183 ((dio_t *)dkr)->uio->uio_resid -= bcount;
187 /*****************************************************************************
188 * DKR_RUN_COMPLETION: borrowed from IOMediaBSDClient.cpp
189 *****************************************************************************/
190 static inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
192 if (dkrtype == DKRTYPE_BUF)
194 buf_t * bp = (buf_t *)dkr;
196 bp->b_error = p_this->errnoFromReturn(status);
197 bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0;
202 /*****************************************************************************
203 * DKR_GET_BUFFER: borrowed from IOMediaBSDClient.cpp
204 *****************************************************************************/
205 static inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
207 if (dkrtype == DKRTYPE_BUF)
209 buf_t * bp = (buf_t *)dkr;
211 if ( (bp->b_flags & B_VECTORLIST) )
213 assert(sizeof(IOPhysicalRange ) == sizeof(iovec ));
214 assert(sizeof(IOPhysicalRange::address) == sizeof(iovec::iov_base));
215 assert(sizeof(IOPhysicalRange::length ) == sizeof(iovec::iov_len ));
216 return IOMemoryDescriptor::withPhysicalRanges(
217 (IOPhysicalRange *) bp->b_vectorlist,
218 (UInt32) bp->b_vectorcount,
219 (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
223 return IOMemoryDescriptor::withAddress(
224 (vm_address_t) bp->b_data,
225 (vm_size_t) bp->b_bcount,
226 (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
227 (bp->b_flags & B_PHYS) ? current_task() : kernel_task );
231 struct uio * uio = ((dio_t *)dkr)->uio;
233 assert(sizeof(IOVirtualRange ) == sizeof(iovec ));
234 assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
235 assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
237 return IOMemoryDescriptor::withRanges(
238 (IOVirtualRange *) uio->uio_iov,
239 (UInt32) uio->uio_iovcnt,
240 (uio->uio_rw == UIO_READ ) ? kIODirectionIn : kIODirectionOut,
241 (uio->uio_segflg != UIO_SYSSPACE) ? current_task() : kernel_task,
246 /*****************************************************************************
247 * DVDioctl::init: initialize the driver structure
248 *****************************************************************************/
249 bool DVDioctl::init( OSDictionary *p_dict = 0 )
251 //IOLog( "DVD ioctl: initializing\n" );
261 bool res = super::init( p_dict );
266 /*****************************************************************************
267 * DVDioctl::probe: check whether the driver can be safely activated
268 *****************************************************************************/
269 IOService * DVDioctl::probe( IOService *provider, SInt32 *score )
271 //IOLog( "DVD ioctl: probing\n" );
272 IOService * res = super::probe( provider, score );
277 /*****************************************************************************
278 * DVDioctl::start: start the driver
279 *****************************************************************************/
280 bool DVDioctl::start( IOService *provider )
282 //IOLog( "DVD ioctl: starting\n" );
284 if( !super::start( provider ) )
289 //IOLog( "DVD ioctl: creating device\n" );
291 i_major = bdevsw_add( -1, &device_functions );
295 //log(LOG_INFO, "DVD ioctl: failed to allocate a major number\n");
299 p_node = devfs_make_node ( makedev( i_major, 0 ), DEVFS_BLOCK,
300 UID_ROOT, GID_WHEEL, 0666, "dvd" );
304 //log( LOG_INFO, "DVD ioctl: failed creating node\n" );
306 if( bdevsw_remove(i_major, &device_functions) == -1 )
308 //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
317 /*****************************************************************************
318 * DVDioctl::stop: stop the driver
319 *****************************************************************************/
320 void DVDioctl::stop( IOService *provider )
322 //IOLog( "DVD ioctl: removing device\n" );
326 devfs_remove( p_node );
331 if( bdevsw_remove(i_major, &device_functions) == -1 )
333 //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
337 //IOLog( "DVD ioctl: stopping\n" );
338 super::stop( provider );
341 /*****************************************************************************
342 * DVDioctl::free: free all resources allocated by the driver
343 *****************************************************************************/
344 void DVDioctl::free( void )
346 //IOLog( "DVD ioctl: freeing\n" );
350 /* following functions are local */
352 /*****************************************************************************
353 * DVDOpen: look for an IODVDMedia object and open it
354 *****************************************************************************/
355 static int DVDOpen( dev_t dev, int flags, int devtype, struct proc * )
357 IOStorageAccess level;
359 /* Check that the device hasn't already been opened */
362 //log( LOG_INFO, "DVD ioctl: already opened\n" );
370 IOService * p_root = IOService::getServiceRoot();
374 //log( LOG_INFO, "DVD ioctl: couldn't find root\n" );
379 OSDictionary * p_dict = p_root->serviceMatching( kIODVDMediaClass );
383 //log( LOG_INFO, "DVD ioctl: couldn't find dictionary\n" );
388 p_dvd = OSDynamicCast( IODVDMedia, p_root->waitForService( p_dict ) );
392 //log( LOG_INFO, "DVD ioctl: couldn't find service\n" );
397 //log( LOG_INFO, "DVD ioctl: found DVD\n" );
399 level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
400 : kIOStorageAccessReader;
402 if( ! p_dvd->open( p_this, 0, level) )
404 log( LOG_INFO, "DVD ioctl: IODVDMedia object busy\n" );
409 p_drive = p_dvd->getProvider();
411 log( LOG_INFO, "DVD ioctl: IODVDMedia->open()\n" );
416 /*****************************************************************************
417 * DVDClose: close the IODVDMedia object
418 *****************************************************************************/
419 static int DVDClose( dev_t dev, int flags, int devtype, struct proc * )
421 /* Release the device */
422 p_dvd->close( p_this );
428 log( LOG_INFO, "DVD ioctl: IODVDMedia->close()\n" );
433 /*****************************************************************************
434 * DVDSize: return the device size
435 *****************************************************************************/
436 static int DVDSize( dev_t dev )
438 return p_dvd->getPreferredBlockSize();
441 /*****************************************************************************
442 * DVDStrategy: perform read or write operations
443 *****************************************************************************/
444 static void DVDStrategy( buf_t * bp )
446 DVDReadWrite(bp, DKRTYPE_BUF);
450 /*****************************************************************************
451 * DVDBlockIoctl: issue an ioctl on the block device
452 *****************************************************************************/
453 static int DVDBlockIoctl( dev_t dev, u_long cmd, caddr_t addr, int flags,
456 dvdioctl_data_t * p_data = (dvdioctl_data_t *)addr;
460 case IODVD_READ_STRUCTURE:
462 log( LOG_INFO, "DVD ioctl: IODVD_READ_STRUCTURE\n" );
464 /* We don't do anything, since I don't know what to do */
470 log( LOG_INFO, "DVD ioctl: send key to `%s', "
471 "buf %d, format %d, class %d, agid %d\n",
472 p_drive->getDeviceTypeName(),
473 (int)p_data->p_buffer, p_data->i_keyformat,
474 p_data->i_keyclass, p_data->i_agid );
476 #ifdef ACTIVATE_DANGEROUS_IOCTL
477 return p_drive->sendKey( (IOMemoryDescriptor *)p_data->p_buffer,
478 (DVDKeyClass)p_data->i_keyclass,
480 (DVDKeyFormat)p_data->i_keyformat );
485 case IODVD_REPORT_KEY:
487 log( LOG_INFO, "DVD ioctl: report key from `%s', "
488 "buf %d, class %d, lba %d, agid %d, format %d\n",
489 p_drive->getDeviceTypeName(),
490 (int)p_data->p_buffer, p_data->i_keyclass, p_data->i_lba,
491 p_data->i_agid, p_data->i_keyformat );
493 #ifdef ACTIVATE_DANGEROUS_IOCTL
494 return p_drive->reportKey( (IOMemoryDescriptor *)p_data->p_buffer,
495 (DVDKeyClass)p_data->i_keyclass,
496 p_data->i_lba, p_data->i_agid,
497 (DVDKeyFormat)p_data->i_keyformat );
504 log( LOG_INFO, "DVD ioctl: unknown ioctl\n" );
510 /*****************************************************************************
511 * DVDReadWrite: borrowed from IOMediaBSDClient.cpp
512 *****************************************************************************/
513 static int DVDReadWrite(dkr_t dkr, dkrtype_t dkrtype)
515 IOMemoryDescriptor * buffer;
516 register UInt64 byteCount;
517 register UInt64 byteStart;
521 byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);
522 byteStart = DKR_GET_BYTE_START(dkr, dkrtype);
523 mediaSize = p_dvd->getSize();
525 if ( byteStart >= mediaSize )
527 status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError; goto dkreadwriteErr;
530 if ( DKR_IS_RAW(dkr, dkrtype) )
532 UInt64 mediaBlockSize = p_dvd->getPreferredBlockSize();
534 if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
536 status = kIOReturnNotAligned;
541 buffer = DKR_GET_BUFFER(dkr, dkrtype);
545 status = kIOReturnNoMemory;
549 if ( byteCount > mediaSize - byteStart )
551 IOMemoryDescriptor * originalBuffer = buffer;
553 buffer = IOMemoryDescriptor::withSubRange( originalBuffer, 0,
554 mediaSize - byteStart, originalBuffer->getDirection() );
555 originalBuffer->release();
558 status = kIOReturnNoMemory;
563 if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )
565 IOStorageCompletion completion;
567 completion.target = dkr;
568 completion.action = DVDReadWriteCompletion;
569 completion.parameter = (void *) dkrtype;
571 if ( DKR_IS_READ(dkr, dkrtype) )
573 p_dvd->read( p_this, byteStart, buffer, completion );
577 p_dvd->write( p_this, byteStart, buffer, completion );
580 status = kIOReturnSuccess;
584 if ( DKR_IS_READ(dkr, dkrtype) )
586 status = p_dvd->IOStorage::read( p_this, byteStart,
587 buffer, &byteCount );
591 status = p_dvd->IOStorage::write( p_this, byteStart,
592 buffer, &byteCount );
595 DVDReadWriteCompletion(dkr, (void *)dkrtype, status, byteCount);
599 return p_this->errnoFromReturn(status);
602 DVDReadWriteCompletion(dkr, (void *)dkrtype, status, 0);
604 return p_this->errnoFromReturn(status);
607 /*****************************************************************************
608 * DVDReadWriteCompletion: borrowed from IOMediaBSDClient.cpp
609 *****************************************************************************/
610 static void DVDReadWriteCompletion( void * target,
613 UInt64 actualByteCount )
615 dkr_t dkr = (dkr_t) target;
616 dkrtype_t dkrtype = (dkrtype_t) (int) parameter;
617 dev_t dev = DKR_GET_DEV(dkr, dkrtype);
619 if ( status != kIOReturnSuccess )
621 IOLog( "DVD ioctl: %s (is the disc authenticated ?)\n",
622 p_this->stringFromReturn(status) );
625 DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);
626 DKR_RUN_COMPLETION(dkr, dkrtype, status);