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