]> git.sesse.net Git - vlc/blob - plugins/vcd/cdrom_tools.c
* ./plugins/dummy/input_dummy.c: fixed `vlc vlc:quit'.
[vlc] / plugins / vcd / cdrom_tools.c
1 /****************************************************************************
2  * cdrom_tools.c: cdrom tools
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  * $Id: cdrom_tools.c,v 1.1 2002/02/20 05:50:00 jlj Exp $
6  *
7  * Author: Johan Bilien <jobi@via.ecp.fr>
8  *         Jon Lech Johansen <jon-vl@nanocrew.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include <videolan/vlc.h>
32
33 #ifdef HAVE_UNISTD_H
34 #   include <unistd.h>
35 #endif
36
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <string.h>
40 #include <errno.h>
41
42 #include <sys/ioctl.h>
43
44 #if defined(HAVE_BSD_DVD_STRUCT) || \
45  defined(DVD_STRUCT_IN_BSDI_DVDIOCTL_DVD_H) || \
46  defined(DVD_STRUCT_IN_DVD_H)
47 #   include <dvd.h>
48 #elif defined ( SYS_DARWIN )
49 #   include <CoreFoundation/CFBase.h>
50 #   include <IOKit/IOKitLib.h>
51 #   include <IOKit/storage/IOCDTypes.h>
52 #   include <IOKit/storage/IOCDMedia.h>
53 #   include <IOKit/storage/IOCDMediaBSDClient.h>
54 #else
55 #   include <linux/cdrom.h>
56 #endif
57
58 #include "cdrom_tools.h"
59
60 /*****************************************************************************
61  * Platform specific 
62  *****************************************************************************/
63 #if defined( SYS_DARWIN )
64 CDTOC *getTOC( const char * );
65 #define freeTOC( p ) free( (void*)p )
66 int getNumberOfDescriptors( CDTOC * );
67 int getNumberOfTracks( CDTOC *, int );
68 #define CD_MIN_TRACK_NO 01
69 #define CD_MAX_TRACK_NO 99
70 #endif
71
72 /*****************************************************************************
73  * ioctl_ReadTocHeader: Read the TOC header and return the track number.
74  *****************************************************************************/
75 int ioctl_GetTrackCount( int i_fd, const char *psz_dev )
76 {
77     int i_count = -1;
78
79 #if defined( SYS_DARWIN )
80     CDTOC *pTOC;
81     int i_descriptors;
82
83     if( ( pTOC = getTOC( psz_dev ) ) == NULL )
84     {
85         intf_ErrMsg( "vcd error: failed to get the TOC" );
86         return( -1 );
87     }
88
89     i_descriptors = getNumberOfDescriptors( pTOC );
90     i_count = getNumberOfTracks( pTOC, i_descriptors );
91
92     freeTOC( pTOC );
93
94 #else
95     struct cdrom_tochdr   tochdr;
96
97     /* First we read the TOC header */
98     if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
99     {
100         intf_ErrMsg( "vcd error: could not read TOCHDR" );
101         return -1;
102     }
103
104     i_count = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
105 #endif
106
107     return( i_count );
108 }
109
110 /*****************************************************************************
111  * ioctl_GetSectors: Read the Table of Contents and fill p_vcd.
112  *****************************************************************************/
113 int * ioctl_GetSectors( int i_fd, const char *psz_dev )
114 {
115     int i, i_tracks;
116     int *p_sectors = NULL;
117
118 #if defined( SYS_DARWIN )
119     CDTOC *pTOC;
120     u_char track;
121     int i_descriptors;
122     int i_leadout = -1;
123     CDTOCDescriptor *pTrackDescriptors;
124
125     if( ( pTOC = getTOC( psz_dev ) ) == NULL )
126     {
127         intf_ErrMsg( "vcd error: failed to get the TOC" );
128         return( NULL );
129     }
130
131     i_descriptors = getNumberOfDescriptors( pTOC );
132     i_tracks = getNumberOfTracks( pTOC, i_descriptors );
133
134     p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
135     if( p_sectors == NULL )
136     {
137         intf_ErrMsg( "vcd error: could not allocate p_sectors" );
138         freeTOC( pTOC );
139         return NULL;
140     }
141     
142     pTrackDescriptors = pTOC->descriptors;
143
144     for( i_tracks = 0, i = 0; i <= i_descriptors; i++ )
145     {
146         track = pTrackDescriptors[i].point;
147
148         if( track == 0xA2 )
149             i_leadout = i;
150
151         if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
152             continue;
153
154         p_sectors[i_tracks++] = 
155             CDConvertMSFToLBA( pTrackDescriptors[i].p );
156     }
157
158     if( i_leadout == -1 )
159     {
160         intf_ErrMsg( "vcd error: leadout not found" );
161         free( p_sectors );
162         freeTOC( pTOC );
163         return( NULL );
164     } 
165
166     /* set leadout sector */
167     p_sectors[i_tracks] = 
168         CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p ); 
169
170     freeTOC( pTOC );
171
172 #else
173     struct cdrom_tochdr   tochdr;
174     struct cdrom_tocentry tocent;
175
176     /* First we read the TOC header */
177     if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
178     {
179         intf_ErrMsg( "vcd error: could not read TOCHDR" );
180         return NULL;
181     }
182
183     i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
184
185     p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
186     if( p_sectors == NULL )
187     {
188         intf_ErrMsg( "vcd error: could not allocate p_sectors" );
189         return NULL;
190     }
191
192     /* Fill the p_sectors structure with the track/sector matches */
193     for( i = 0 ; i <= i_tracks ; i++ )
194     {
195         tocent.cdte_format = CDROM_LBA;
196         tocent.cdte_track =
197             ( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
198
199         if( ioctl( i_fd, CDROMREADTOCENTRY, &tocent ) == -1 )
200         {
201             intf_ErrMsg( "vcd error: could not read TOCENTRY" );
202             free( p_sectors );
203             return NULL;
204         }
205
206         p_sectors[ i ] = tocent.cdte_addr.lba;
207     }
208 #endif
209
210     return p_sectors;
211 }
212
213 /****************************************************************************
214  * ioctl_ReadSector: Read a sector (2324 bytes)
215  ****************************************************************************/
216 int ioctl_ReadSector( int i_fd, int i_sector, byte_t * p_buffer )
217 {
218     byte_t p_block[ VCD_SECTOR_SIZE ];
219
220 #if defined( SYS_DARWIN )
221     dk_cd_read_t cd_read;
222
223     memset( &cd_read, 0, sizeof(cd_read) );
224
225     cd_read.offset = i_sector * VCD_SECTOR_SIZE;
226     cd_read.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader |
227                          kCDSectorAreaSubHeader | kCDSectorAreaUser |
228                          kCDSectorAreaAuxiliary;
229     cd_read.sectorType = kCDSectorTypeUnknown;
230
231     cd_read.buffer = p_block;
232     cd_read.bufferLength = sizeof(p_block);
233
234     if( ioctl( i_fd, DKIOCCDREAD, &cd_read ) == -1 )
235     {
236         intf_ErrMsg( "vcd error: could not read block %d", i_sector );
237         return( -1 );
238     }
239
240 #else
241     int i_dummy = i_sector + 2 * CD_FRAMES;
242
243 #define p_msf ((struct cdrom_msf0 *)p_block)
244     p_msf->minute =   i_dummy / (CD_FRAMES * CD_SECS);
245     p_msf->second = ( i_dummy % (CD_FRAMES * CD_SECS) ) / CD_FRAMES;
246     p_msf->frame =  ( i_dummy % (CD_FRAMES * CD_SECS) ) % CD_FRAMES;
247 #undef p_msf
248
249     if( ioctl(i_fd, CDROMREADRAW, p_block) == -1 )
250     {
251         intf_ErrMsg( "vcd error: could not read block %i from disc",
252                      i_sector );
253         return( -1 );
254     }
255 #endif
256
257     /* We don't want to keep the header of the read sector */
258     FAST_MEMCPY( p_buffer, p_block + VCD_DATA_START, VCD_DATA_SIZE );
259
260     return( 0 );
261 }
262
263 #if defined( SYS_DARWIN )
264 /****************************************************************************
265  * getTOC: get the TOC
266  ****************************************************************************/
267 CDTOC *getTOC( const char *psz_dev )
268 {
269     mach_port_t port;
270     char *psz_devname;
271     kern_return_t ret;
272     CDTOC *pTOC = NULL;
273     io_iterator_t iterator;
274     io_registry_entry_t service;
275     CFDictionaryRef properties;
276     CFDataRef data;
277
278     if( psz_dev == NULL )
279     {
280         intf_ErrMsg( "vcd error: invalid device path" );
281         return( NULL );
282     }
283
284     /* get the device name */
285     if( ( psz_devname = strrchr( psz_dev, '/') ) != NULL )
286         ++psz_devname;
287     else
288         psz_devname = (char *)psz_dev;
289
290     /* unraw the device name */
291     if( *psz_devname == 'r' )
292         ++psz_devname;
293
294     /* get port for IOKit communication */
295     if( ( ret = IOMasterPort( MACH_PORT_NULL, &port ) ) != KERN_SUCCESS )
296     {
297         intf_ErrMsg( "vcd error: IOMasterPort: 0x%08x", ret );
298         return( NULL );
299     }
300
301     /* get service iterator for the device */
302     if( ( ret = IOServiceGetMatchingServices( 
303                     port, IOBSDNameMatching( port, 0, psz_devname ),
304                     &iterator ) ) != KERN_SUCCESS )
305     {
306         intf_ErrMsg( "vcd error: IOServiceGetMatchingServices: 0x%08x", ret );
307         return( NULL );
308     }
309
310     /* first service */
311     service = IOIteratorNext( iterator );
312     IOObjectRelease( iterator );
313
314     /* search for kIOCDMediaClass */ 
315     while( service && !IOObjectConformsTo( service, kIOCDMediaClass ) )
316     {
317         if( ( ret = IORegistryEntryGetParentIterator( service, 
318                         kIOServicePlane, &iterator ) ) != KERN_SUCCESS )
319         {
320             intf_ErrMsg( "vcd error: " \
321                          "IORegistryEntryGetParentIterator: 0x%08x", ret );
322             IOObjectRelease( service );
323             return( NULL );
324         }
325
326         IOObjectRelease( service );
327         service = IOIteratorNext( iterator );
328         IOObjectRelease( iterator );
329     }
330
331     if( service == NULL )
332     {
333         intf_ErrMsg( "vcd error: search for kIOCDMediaClass came up empty" );
334         return( NULL );
335     }
336
337     /* create a CF dictionary containing the TOC */
338     if( ( ret = IORegistryEntryCreateCFProperties( service, &properties,
339                     kCFAllocatorDefault, kNilOptions ) ) != KERN_SUCCESS )
340     {
341         intf_ErrMsg( "vcd error: " \
342                      " IORegistryEntryCreateCFProperties: 0x%08x", ret );
343         IOObjectRelease( service );
344         return( NULL );
345     }
346
347     /* get the TOC from the dictionary */
348     if( ( data = (CFDataRef) CFDictionaryGetValue( properties,
349                                     CFSTR(kIOCDMediaTOCKey) ) ) != NULL )
350     {
351         CFRange range;
352         CFIndex buf_len;
353
354         buf_len = CFDataGetLength( data ) + 1;
355         range = CFRangeMake( 0, buf_len );
356
357         if( ( pTOC = (CDTOC *)malloc( buf_len ) ) != NULL )
358         {
359             CFDataGetBytes( data, range, (u_char *)pTOC );
360         }
361     }
362     else
363     {
364         intf_ErrMsg( "vcd error: CFDictionaryGetValue failed" );
365     }
366
367     CFRelease( properties );
368     IOObjectRelease( service ); 
369
370     return( pTOC ); 
371 }
372
373 /****************************************************************************
374  * getNumberOfDescriptors: get number of descriptors in TOC 
375  ****************************************************************************/
376 int getNumberOfDescriptors( CDTOC *pTOC )
377 {
378     int i_descriptors;
379
380     /* get TOC length */
381     i_descriptors = pTOC->length;
382
383     /* remove the first and last session */
384     i_descriptors -= ( sizeof(pTOC->sessionFirst) +
385                        sizeof(pTOC->sessionLast) );
386
387     /* divide the length by the size of a single descriptor */
388     i_descriptors /= sizeof(CDTOCDescriptor);
389
390     return( i_descriptors );
391 }
392
393 /****************************************************************************
394  * getNumberOfTracks: get number of tracks in TOC 
395  ****************************************************************************/
396 int getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
397 {
398     u_char track;
399     int i, i_tracks = 0; 
400     CDTOCDescriptor *pTrackDescriptors;
401
402     pTrackDescriptors = pTOC->descriptors;
403
404     for( i = i_descriptors; i >= 0; i-- )
405     {
406         track = pTrackDescriptors[i].point;
407
408         if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
409             continue;
410
411         i_tracks++; 
412     }
413
414     return( i_tracks );
415 }
416 #endif