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