]> git.sesse.net Git - vlc/blob - extras/MacOSX_dvdioctl/DVDioctl.cpp
* compilation fixes for the BeOS DVD ioctls
[vlc] / extras / MacOSX_dvdioctl / DVDioctl.cpp
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.3 2001/04/04 16:33:07 sam Exp $
7  *
8  * Authors: Samuel Hocevar <sam@zoy.org>
9  *
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.
15  * 
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
22  * under the License.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * TODO:
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
29  *   it is still in use
30  *****************************************************************************/
31
32 //XXX: uncomment to activate the key exchange ioctls - may hang the machine
33 //#define ACTIVATE_DANGEROUS_IOCTL 1
34
35 /*****************************************************************************
36  * Preamble
37  *****************************************************************************/
38 extern "C"
39 {
40 #include <sys/param.h>
41 #include <sys/conf.h>
42 #include <sys/syslog.h>
43 #include <sys/systm.h>
44 #include <sys/ioccom.h>
45 #include <sys/fcntl.h>
46 #include <sys/buf.h>
47 #include <sys/uio.h>
48 #include <miscfs/devfs/devfs.h>
49
50 #include <mach/mach_types.h>
51 }
52
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>
58
59 #include "DVDioctl.h"
60
61 /*****************************************************************************
62  * Driver class
63  *****************************************************************************/
64 class DVDioctl : public IOService
65 {
66     OSDeclareDefaultStructors( DVDioctl )
67
68 public:
69
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 );
75 };
76
77 #define super IOService
78 OSDefineMetaClassAndStructors( DVDioctl, IOService )
79
80 /*****************************************************************************
81  * Variable typedefs
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;
86 typedef void *                                       dkr_t;
87
88 /*****************************************************************************
89  * Local prototypes
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 );
98
99 static struct bdevsw device_functions =
100 {
101     DVDOpen, DVDClose, DVDStrategy, DVDBlockIoctl, eno_dump, DVDSize, D_DISK
102 };
103
104 /*****************************************************************************
105  * Local variables
106  *****************************************************************************/
107 static DVDioctl * p_this = NULL;
108
109 static bool b_inuse;
110 static int i_major;
111 static void *p_node;
112 static IODVDMedia *p_dvd;
113 static IODVDBlockStorageDriver *p_drive;
114
115 /*****************************************************************************
116  * DKR_GET_DEV: borrowed from IOMediaBSDClient.cpp
117  *****************************************************************************/
118 static inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
119 {
120     return (dkrtype == DKRTYPE_BUF)
121            ? ((buf_t *)dkr)->b_dev : ((dio_t *)dkr)->dev;
122 }
123
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)
128 {
129     return (dkrtype == DKRTYPE_BUF)
130            ? ((buf_t *)dkr)->b_bcount : ((dio_t *)dkr)->uio->uio_resid;
131 }
132
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)
137 {
138     if (dkrtype == DKRTYPE_BUF)
139     {
140         buf_t * bp    = (buf_t *)dkr;
141         return bp->b_blkno * p_dvd->getPreferredBlockSize();
142     }
143     return ((dio_t *)dkr)->uio->uio_offset;
144 }
145
146 /*****************************************************************************
147  * DKR_IS_READ: borrowed from IOMediaBSDClient.cpp
148  *****************************************************************************/
149 static inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
150 {
151     return (dkrtype == DKRTYPE_BUF)
152            ? ((((buf_t *)dkr)->b_flags & B_READ) == B_READ)
153            : ((((dio_t *)dkr)->uio->uio_rw) == UIO_READ);
154 }
155
156 /*****************************************************************************
157  * DKR_IS_ASYNCHRONOUS: borrowed from IOMediaBSDClient.cpp
158  *****************************************************************************/
159 static inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
160 {
161     return (dkrtype == DKRTYPE_BUF) ? true : false;
162 }
163
164 /*****************************************************************************
165  * DKR_IS_RAW: borrowed from IOMediaBSDClient.cpp
166  *****************************************************************************/
167 static inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
168 {
169     return (dkrtype == DKRTYPE_BUF) ? false : true;
170 }
171
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)
176 {
177     if (dkrtype == DKRTYPE_BUF)
178     {
179         ((buf_t *)dkr)->b_resid = ((buf_t *)dkr)->b_bcount - bcount;
180     }
181     else
182     {
183         ((dio_t *)dkr)->uio->uio_resid -= bcount;
184     }
185 }
186
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)
191 {
192     if (dkrtype == DKRTYPE_BUF)
193     {
194         buf_t * bp = (buf_t *)dkr;
195
196         bp->b_error  = p_this->errnoFromReturn(status);
197         bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0;
198         biodone(bp);
199     }
200 }
201
202 /*****************************************************************************
203  * DKR_GET_BUFFER: borrowed from IOMediaBSDClient.cpp
204  *****************************************************************************/
205 static inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
206 {
207     if (dkrtype == DKRTYPE_BUF)
208     {
209         buf_t * bp = (buf_t *)dkr;
210
211         if ( (bp->b_flags & B_VECTORLIST) )
212         {
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,
220               true );
221         }
222
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 );
228     }
229     else
230     {
231         struct uio * uio = ((dio_t *)dkr)->uio;
232
233         assert(sizeof(IOVirtualRange         ) == sizeof(iovec          ));
234         assert(sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base));
235         assert(sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ));
236
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,
242         true );
243     }
244 }
245
246 /*****************************************************************************
247  * DVDioctl::init: initialize the driver structure
248  *****************************************************************************/
249 bool DVDioctl::init( OSDictionary *p_dict = 0 )
250 {
251     //IOLog( "DVD ioctl: initializing\n" );
252
253     p_this = this;
254
255     p_node  = NULL;
256     p_dvd   = NULL;
257     p_drive = NULL;
258     i_major = -1;
259     b_inuse = false;
260
261     bool res = super::init( p_dict );
262
263     return res;
264 }
265     
266 /*****************************************************************************
267  * DVDioctl::probe: check whether the driver can be safely activated
268  *****************************************************************************/
269 IOService * DVDioctl::probe( IOService *provider, SInt32 *score )
270 {
271     //IOLog( "DVD ioctl: probing\n" );
272     IOService * res = super::probe( provider, score );
273
274     return res;
275 }
276
277 /*****************************************************************************
278  * DVDioctl::start: start the driver
279  *****************************************************************************/
280 bool DVDioctl::start( IOService *provider )
281 {
282     //IOLog( "DVD ioctl: starting\n" );
283
284     if( !super::start( provider ) )
285     {
286         return false;
287     }
288
289     //IOLog( "DVD ioctl: creating device\n" );
290
291     i_major = bdevsw_add( -1, &device_functions );
292
293     if( i_major == -1 )
294     {
295         //log(LOG_INFO, "DVD ioctl: failed to allocate a major number\n");
296         return false;
297     }
298
299     p_node = devfs_make_node ( makedev( i_major, 0 ), DEVFS_BLOCK,
300                                UID_ROOT, GID_WHEEL, 0666, "dvd" );
301
302     if( p_node == NULL )
303     {
304         //log( LOG_INFO, "DVD ioctl: failed creating node\n" );
305
306         if( bdevsw_remove(i_major, &device_functions) == -1 )
307         {
308             //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
309         }
310
311         return false;
312     }
313
314     return true;
315 }
316
317 /*****************************************************************************
318  * DVDioctl::stop: stop the driver
319  *****************************************************************************/
320 void DVDioctl::stop( IOService *provider )
321 {
322     //IOLog( "DVD ioctl: removing device\n" );
323
324     if( p_node != NULL )
325     {
326         devfs_remove( p_node );
327     }
328
329     if( i_major != -1 )
330     {
331         if( bdevsw_remove(i_major, &device_functions) == -1 )
332         {
333             //log( LOG_INFO, "DVD ioctl: bdevsw_remove failed\n" );
334         }
335     }
336
337     //IOLog( "DVD ioctl: stopping\n" );
338     super::stop( provider );
339 }
340   
341 /*****************************************************************************
342  * DVDioctl::free: free all resources allocated by the driver
343  *****************************************************************************/
344 void DVDioctl::free( void )
345 {
346     //IOLog( "DVD ioctl: freeing\n" );
347     super::free( );
348 }
349
350 /* following functions are local */
351
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 * )
356 {
357     IOStorageAccess level;
358
359     /* Check that the device hasn't already been opened */
360     if( b_inuse )
361     {
362         //log( LOG_INFO, "DVD ioctl: already opened\n" );
363         return EBUSY;
364     }
365     else
366     {
367         b_inuse = true;
368     }
369
370     IOService * p_root = IOService::getServiceRoot();
371    
372     if( p_root == NULL )
373     {
374         //log( LOG_INFO, "DVD ioctl: couldn't find root\n" );
375         b_inuse = false;
376         return ENXIO;
377     }
378
379     OSDictionary * p_dict = p_root->serviceMatching( kIODVDMediaClass );
380
381     if( p_dict == NULL )
382     {
383         //log( LOG_INFO, "DVD ioctl: couldn't find dictionary\n" );
384         b_inuse = false;
385         return ENXIO;
386     }
387
388     p_dvd = OSDynamicCast( IODVDMedia, p_root->waitForService( p_dict ) );
389
390     if( p_dvd == NULL )
391     {
392         //log( LOG_INFO, "DVD ioctl: couldn't find service\n" );
393         b_inuse = false;
394         return ENXIO;
395     }
396
397     //log( LOG_INFO, "DVD ioctl: found DVD\n" );
398
399     level = (flags & FWRITE) ? kIOStorageAccessReaderWriter
400                              : kIOStorageAccessReader;
401
402     if( ! p_dvd->open( p_this, 0, level) )
403     {
404         log( LOG_INFO, "DVD ioctl: IODVDMedia object busy\n" );
405         b_inuse = false;
406         return EBUSY;
407     }
408
409     p_drive = p_dvd->getProvider();
410
411     log( LOG_INFO, "DVD ioctl: IODVDMedia->open()\n" );
412
413     return 0;
414 }
415
416 /*****************************************************************************
417  * DVDClose: close the IODVDMedia object
418  *****************************************************************************/
419 static int DVDClose( dev_t dev, int flags, int devtype, struct proc * )
420 {
421     /* Release the device */
422     p_dvd->close( p_this );
423
424     p_dvd   = NULL;
425     p_drive = NULL;
426     b_inuse = false;
427
428     log( LOG_INFO, "DVD ioctl: IODVDMedia->close()\n" );
429
430     return 0;
431 }
432
433 /*****************************************************************************
434  * DVDSize: return the device size
435  *****************************************************************************/
436 static int DVDSize( dev_t dev )
437 {
438     return p_dvd->getPreferredBlockSize();
439 }
440
441 /*****************************************************************************
442  * DVDStrategy: perform read or write operations
443  *****************************************************************************/
444 static void DVDStrategy( buf_t * bp )
445 {
446     DVDReadWrite(bp, DKRTYPE_BUF);
447     return;
448 }
449
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,
454                           struct proc *p )
455 {
456     dvdioctl_data_t * p_data = (dvdioctl_data_t *)addr;
457
458     switch( cmd )
459     {
460         case IODVD_READ_STRUCTURE:
461
462             log( LOG_INFO, "DVD ioctl: IODVD_READ_STRUCTURE\n" );
463
464             return 0;
465
466         case IODVD_SEND_KEY:
467
468             log( LOG_INFO, "DVD ioctl: send key to `%s', "
469                  "buf %d, format %d, class %d, agid %d\n",
470                  p_drive->getDeviceTypeName(),
471                  (int)p_data->p_buffer, p_data->i_keyformat,
472                  p_data->i_keyclass, p_data->i_agid );
473
474 #ifdef ACTIVATE_DANGEROUS_IOCTL
475             return p_drive->sendKey( (IOMemoryDescriptor *)p_data->p_buffer,
476                                      (DVDKeyClass)p_data->i_keyclass,
477                                      p_data->i_agid,
478                                      (DVDKeyFormat)p_data->i_keyformat );
479 #else
480             return -1;
481 #endif
482
483         case IODVD_REPORT_KEY:
484
485             log( LOG_INFO, "DVD ioctl: report key from `%s', "
486                  p_drive->getDeviceTypeName(),
487                  "buf %d, class %d, lba %d, agid %d, format %d\n",
488                  (int)p_data->p_buffer, p_data->i_keyclass, p_data->i_lba,
489                  p_data->i_agid, p_data->i_keyformat );
490
491 #ifdef ACTIVATE_DANGEROUS_IOCTL
492             return p_drive->reportKey( (IOMemoryDescriptor *)p_data->p_buffer,
493                                        (DVDKeyClass)p_data->i_keyclass,
494                                        p_data->i_lba, p_data->i_agid,
495                                        (DVDKeyFormat)p_data->i_keyformat );
496 #else
497             return -1;
498 #endif
499
500         default:
501
502             log( LOG_INFO, "DVD ioctl: unknown ioctl\n" );
503
504             return EINVAL;
505     }
506 }
507
508 /*****************************************************************************
509  * DVDReadWrite: borrowed from IOMediaBSDClient.cpp
510  *****************************************************************************/
511 static int DVDReadWrite(dkr_t dkr, dkrtype_t dkrtype)
512 {
513     IOMemoryDescriptor * buffer;
514     register UInt64      byteCount;
515     register UInt64      byteStart;
516     UInt64               mediaSize;
517     IOReturn             status;
518
519     byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);
520     byteStart = DKR_GET_BYTE_START(dkr, dkrtype);
521     mediaSize = p_dvd->getSize();
522
523     if ( byteStart >= mediaSize )
524     {
525         status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;        goto dkreadwriteErr;
526     }
527
528     if ( DKR_IS_RAW(dkr, dkrtype) )
529     {
530         UInt64 mediaBlockSize = p_dvd->getPreferredBlockSize();
531
532         if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
533         {
534             status = kIOReturnNotAligned;
535             goto dkreadwriteErr;
536         }
537     }
538
539     buffer = DKR_GET_BUFFER(dkr, dkrtype);
540
541     if ( buffer == 0 )
542     {
543         status = kIOReturnNoMemory;
544         goto dkreadwriteErr;
545     }
546
547     if ( byteCount > mediaSize - byteStart )
548     {
549         IOMemoryDescriptor * originalBuffer = buffer;
550
551         buffer = IOMemoryDescriptor::withSubRange( originalBuffer, 0,
552                      mediaSize - byteStart, originalBuffer->getDirection() );
553         originalBuffer->release();
554         if ( buffer == 0 )
555         {
556             status = kIOReturnNoMemory;
557             goto dkreadwriteErr;
558         }
559     }
560
561     if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )
562     {
563         IOStorageCompletion completion;
564
565         completion.target    = dkr;
566         completion.action    = DVDReadWriteCompletion;
567         completion.parameter = (void *) dkrtype;
568
569         if ( DKR_IS_READ(dkr, dkrtype) )
570         {
571             p_dvd->read(  p_this, byteStart, buffer, completion );
572         }
573         else
574         {
575             p_dvd->write( p_this, byteStart, buffer, completion );
576         }
577
578         status = kIOReturnSuccess;
579     }
580     else
581     {
582         if ( DKR_IS_READ(dkr, dkrtype) )
583         {
584             status = p_dvd->IOStorage::read( p_this, byteStart,
585                                              buffer, &byteCount );
586         }
587         else
588         {
589             status = p_dvd->IOStorage::write( p_this, byteStart,
590                                               buffer, &byteCount );
591         }
592
593         DVDReadWriteCompletion(dkr, (void *)dkrtype, status, byteCount);
594     }
595
596     buffer->release();
597     return p_this->errnoFromReturn(status);
598 dkreadwriteErr:
599
600     DVDReadWriteCompletion(dkr, (void *)dkrtype, status, 0);
601
602     return p_this->errnoFromReturn(status);
603 }
604
605 /*****************************************************************************
606  * DVDReadWriteCompletion: borrowed from IOMediaBSDClient.cpp
607  *****************************************************************************/
608 static void DVDReadWriteCompletion( void *   target,
609                                     void *   parameter,
610                                     IOReturn status,
611                                     UInt64   actualByteCount )
612 {
613     dkr_t     dkr      = (dkr_t) target;
614     dkrtype_t dkrtype  = (dkrtype_t) (int) parameter;
615     dev_t     dev      = DKR_GET_DEV(dkr, dkrtype);
616
617     if ( status != kIOReturnSuccess )
618     {
619         IOLog( "DVD ioctl: %s (is the disc authenticated ?)\n",
620                p_this->stringFromReturn(status) );
621     }
622
623     DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);
624     DKR_RUN_COMPLETION(dkr, dkrtype, status);
625 }
626