]> git.sesse.net Git - vlc/blob - modules/access/vcd/cdrom.c
* ./modules/*: moved plugins to the new tree. Yet untested builds include
[vlc] / modules / access / vcd / cdrom.c
1 /****************************************************************************
2  * cdrom.c: cdrom tools
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  * $Id: cdrom.c,v 1.1 2002/08/04 17:23:42 sam 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 <vlc/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( SYS_BSDI )
45 #   include <dvd.h>
46 #elif defined ( SYS_DARWIN )
47 #   include <CoreFoundation/CFBase.h>
48 #   include <IOKit/IOKitLib.h>
49 #   include <IOKit/storage/IOCDTypes.h>
50 #   include <IOKit/storage/IOCDMedia.h>
51 #   include <IOKit/storage/IOCDMediaBSDClient.h>
52 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
53 #   include <sys/cdio.h>
54 #   include <sys/cdrio.h>
55 #else
56 #   include <linux/cdrom.h>
57 #endif
58
59 #include "cdrom.h"
60
61 /*****************************************************************************
62  * Platform specific 
63  *****************************************************************************/
64 #if defined( SYS_DARWIN )
65 CDTOC *getTOC( const char * );
66 #define freeTOC( p ) free( (void*)p )
67 int getNumberOfDescriptors( CDTOC * );
68 int getNumberOfTracks( CDTOC *, int );
69 #define CD_MIN_TRACK_NO 01
70 #define CD_MAX_TRACK_NO 99
71 #endif
72
73 /*****************************************************************************
74  * ioctl_ReadTocHeader: Read the TOC header and return the track number.
75  *****************************************************************************/
76 int ioctl_GetTrackCount( int i_fd, const char *psz_dev )
77 {
78     int i_count = -1;
79
80 #if defined( SYS_DARWIN )
81     CDTOC *pTOC;
82     int i_descriptors;
83
84     if( ( pTOC = getTOC( psz_dev ) ) == NULL )
85     {
86 //X        intf_ErrMsg( "vcd error: failed to get the TOC" );
87         return( -1 );
88     }
89
90     i_descriptors = getNumberOfDescriptors( pTOC );
91     i_count = getNumberOfTracks( pTOC, i_descriptors );
92
93     freeTOC( pTOC );
94
95 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
96     struct ioc_toc_header tochdr;
97     
98     if( ioctl( i_fd, CDIOREADTOCHEADER, &tochdr ) == -1 )
99     {
100 //X        intf_ErrMsg( "vcd error: could not read TOCHDR" );
101         return -1;
102     }
103
104     i_count = tochdr.ending_track - tochdr.starting_track + 1;
105     
106 #else
107     struct cdrom_tochdr   tochdr;
108
109     /* First we read the TOC header */
110     if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
111     {
112 //X        intf_ErrMsg( "vcd error: could not read TOCHDR" );
113         return -1;
114     }
115
116     i_count = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
117 #endif
118
119     return( i_count );
120 }
121
122 /*****************************************************************************
123  * ioctl_GetSectors: Read the Table of Contents and fill p_vcd.
124  *****************************************************************************/
125 int * ioctl_GetSectors( int i_fd, const char *psz_dev )
126 {
127     int i, i_tracks;
128     int *p_sectors = NULL;
129
130 #if defined( SYS_DARWIN )
131     CDTOC *pTOC;
132     u_char track;
133     int i_descriptors;
134     int i_leadout = -1;
135     CDTOCDescriptor *pTrackDescriptors;
136
137     if( ( pTOC = getTOC( psz_dev ) ) == NULL )
138     {
139 //X        intf_ErrMsg( "vcd error: failed to get the TOC" );
140         return( NULL );
141     }
142
143     i_descriptors = getNumberOfDescriptors( pTOC );
144     i_tracks = getNumberOfTracks( pTOC, i_descriptors );
145
146     p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
147     if( p_sectors == NULL )
148     {
149 //X        intf_ErrMsg( "vcd error: could not allocate p_sectors" );
150         freeTOC( pTOC );
151         return NULL;
152     }
153     
154     pTrackDescriptors = pTOC->descriptors;
155
156     for( i_tracks = 0, i = 0; i <= i_descriptors; i++ )
157     {
158         track = pTrackDescriptors[i].point;
159
160         if( track == 0xA2 )
161             i_leadout = i;
162
163         if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
164             continue;
165
166         p_sectors[i_tracks++] = 
167             CDConvertMSFToLBA( pTrackDescriptors[i].p );
168     }
169
170     if( i_leadout == -1 )
171     {
172 //X        intf_ErrMsg( "vcd error: leadout not found" );
173         free( p_sectors );
174         freeTOC( pTOC );
175         return( NULL );
176     } 
177
178     /* set leadout sector */
179     p_sectors[i_tracks] = 
180         CDConvertMSFToLBA( pTrackDescriptors[i_leadout].p ); 
181
182     freeTOC( pTOC );
183
184 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
185     struct ioc_read_toc_entry toc_entries;
186
187     i_tracks = ioctl_GetTrackCount( i_fd, psz_dev );
188     p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
189     if( p_sectors == NULL )
190     {
191 //X        intf_ErrMsg( "vcd error: could not allocate p_sectors" );
192         return NULL;
193     }
194
195     toc_entries.address_format = CD_LBA_FORMAT;
196     toc_entries.starting_track = 0;
197     toc_entries.data_len = ( i_tracks + 1 ) * sizeof( struct cd_toc_entry ); 
198     toc_entries.data = (struct cd_toc_entry *) malloc( toc_entries.data_len );
199     if( toc_entries.data == NULL )
200     {
201 //X        intf_ErrMsg( "vcd error: not enoug memory" );
202         free( p_sectors );
203         return NULL;
204     }
205  
206     /* Read the TOC */
207     if( ioctl( i_fd, CDIOREADTOCENTRYS, &toc_entries ) == -1 )
208     {
209 //X        intf_ErrMsg( "vcd error: could not read the TOC" );
210         free( p_sectors );
211         free( toc_entries.data );
212         return NULL;
213     }
214     
215     /* Fill the p_sectors structure with the track/sector matches */
216     for( i = 0 ; i <= i_tracks ; i++ )
217     {
218         p_sectors[ i ] = ntohl( toc_entries.data[i].addr.lba );
219     }
220 #else
221     struct cdrom_tochdr   tochdr;
222     struct cdrom_tocentry tocent;
223
224     /* First we read the TOC header */
225     if( ioctl( i_fd, CDROMREADTOCHDR, &tochdr ) == -1 )
226     {
227 //X        intf_ErrMsg( "vcd error: could not read TOCHDR" );
228         return NULL;
229     }
230
231     i_tracks = tochdr.cdth_trk1 - tochdr.cdth_trk0 + 1;
232
233     p_sectors = malloc( (i_tracks + 1) * sizeof(int) );
234     if( p_sectors == NULL )
235     {
236 //X        intf_ErrMsg( "vcd error: could not allocate p_sectors" );
237         return NULL;
238     }
239
240     /* Fill the p_sectors structure with the track/sector matches */
241     for( i = 0 ; i <= i_tracks ; i++ )
242     {
243         tocent.cdte_format = CDROM_LBA;
244         tocent.cdte_track =
245             ( i == i_tracks ) ? CDROM_LEADOUT : tochdr.cdth_trk0 + i;
246
247         if( ioctl( i_fd, CDROMREADTOCENTRY, &tocent ) == -1 )
248         {
249 //X            intf_ErrMsg( "vcd error: could not read TOCENTRY" );
250             free( p_sectors );
251             return NULL;
252         }
253
254         p_sectors[ i ] = tocent.cdte_addr.lba;
255     }
256 #endif
257
258     return p_sectors;
259 }
260
261 /****************************************************************************
262  * ioctl_ReadSector: Read a sector (2324 bytes)
263  ****************************************************************************/
264 int ioctl_ReadSector( int i_fd, int i_sector, byte_t * p_buffer )
265 {
266     byte_t p_block[ VCD_SECTOR_SIZE ];
267
268 #if defined( SYS_DARWIN )
269     dk_cd_read_t cd_read;
270
271     memset( &cd_read, 0, sizeof(cd_read) );
272
273     cd_read.offset = i_sector * VCD_SECTOR_SIZE;
274     cd_read.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader |
275                          kCDSectorAreaSubHeader | kCDSectorAreaUser |
276                          kCDSectorAreaAuxiliary;
277     cd_read.sectorType = kCDSectorTypeUnknown;
278
279     cd_read.buffer = p_block;
280     cd_read.bufferLength = sizeof(p_block);
281
282     if( ioctl( i_fd, DKIOCCDREAD, &cd_read ) == -1 )
283     {
284 //X        intf_ErrMsg( "vcd error: could not read block %d", i_sector );
285         return( -1 );
286     }
287
288 #elif defined( HAVE_IOC_TOC_HEADER_IN_SYS_CDIO_H )
289     
290     int i_size = VCD_SECTOR_SIZE;
291
292     if( ioctl( i_fd, CDRIOCSETBLOCKSIZE, &i_size ) == -1 )
293     {
294 //X        intf_ErrMsg( "vcd error: Could not set block size" );
295         return( -1 );
296     }
297
298     if( lseek( i_fd, i_sector * VCD_SECTOR_SIZE, SEEK_SET ) == -1 )
299     {
300 //X        intf_ErrMsg( "vcd error: Could not lseek to sector %d", i_sector );
301         return( -1 );
302     }
303
304     if( read( i_fd, p_block, VCD_SECTOR_SIZE ) == -1 )
305     {
306 //X        intf_ErrMsg( "vcd error: Could not read sector %d", i_sector );
307         return( -1 );
308     }
309
310 #else
311     int i_dummy = i_sector + 2 * CD_FRAMES;
312
313 #define p_msf ((struct cdrom_msf0 *)p_block)
314     p_msf->minute =   i_dummy / (CD_FRAMES * CD_SECS);
315     p_msf->second = ( i_dummy % (CD_FRAMES * CD_SECS) ) / CD_FRAMES;
316     p_msf->frame =  ( i_dummy % (CD_FRAMES * CD_SECS) ) % CD_FRAMES;
317 #undef p_msf
318
319     if( ioctl(i_fd, CDROMREADRAW, p_block) == -1 )
320     {
321 //X        intf_ErrMsg( "vcd error: could not read block %i from disc",
322 //X                     i_sector );
323         return( -1 );
324     }
325 #endif
326
327     /* We don't want to keep the header of the read sector */
328     memcpy( p_buffer, p_block + VCD_DATA_START, VCD_DATA_SIZE );
329
330     return( 0 );
331 }
332
333 #if defined( SYS_DARWIN )
334 /****************************************************************************
335  * getTOC: get the TOC
336  ****************************************************************************/
337 CDTOC *getTOC( const char *psz_dev )
338 {
339     mach_port_t port;
340     char *psz_devname;
341     kern_return_t ret;
342     CDTOC *pTOC = NULL;
343     io_iterator_t iterator;
344     io_registry_entry_t service;
345     CFDictionaryRef properties;
346     CFDataRef data;
347
348     if( psz_dev == NULL )
349     {
350 //X        intf_ErrMsg( "vcd error: invalid device path" );
351         return( NULL );
352     }
353
354     /* get the device name */
355     if( ( psz_devname = strrchr( psz_dev, '/') ) != NULL )
356         ++psz_devname;
357     else
358         psz_devname = (char *)psz_dev;
359
360     /* unraw the device name */
361     if( *psz_devname == 'r' )
362         ++psz_devname;
363
364     /* get port for IOKit communication */
365     if( ( ret = IOMasterPort( MACH_PORT_NULL, &port ) ) != KERN_SUCCESS )
366     {
367 //X        intf_ErrMsg( "vcd error: IOMasterPort: 0x%08x", ret );
368         return( NULL );
369     }
370
371     /* get service iterator for the device */
372     if( ( ret = IOServiceGetMatchingServices( 
373                     port, IOBSDNameMatching( port, 0, psz_devname ),
374                     &iterator ) ) != KERN_SUCCESS )
375     {
376 //X        intf_ErrMsg( "vcd error: IOServiceGetMatchingServices: 0x%08x", ret );
377         return( NULL );
378     }
379
380     /* first service */
381     service = IOIteratorNext( iterator );
382     IOObjectRelease( iterator );
383
384     /* search for kIOCDMediaClass */ 
385     while( service && !IOObjectConformsTo( service, kIOCDMediaClass ) )
386     {
387         if( ( ret = IORegistryEntryGetParentIterator( service, 
388                         kIOServicePlane, &iterator ) ) != KERN_SUCCESS )
389         {
390 //X            intf_ErrMsg( "vcd error: "
391 //X                         "IORegistryEntryGetParentIterator: 0x%08x", ret );
392             IOObjectRelease( service );
393             return( NULL );
394         }
395
396         IOObjectRelease( service );
397         service = IOIteratorNext( iterator );
398         IOObjectRelease( iterator );
399     }
400
401     if( service == NULL )
402     {
403 //X        intf_ErrMsg( "vcd error: search for kIOCDMediaClass came up empty" );
404         return( NULL );
405     }
406
407     /* create a CF dictionary containing the TOC */
408     if( ( ret = IORegistryEntryCreateCFProperties( service, &properties,
409                     kCFAllocatorDefault, kNilOptions ) ) != KERN_SUCCESS )
410     {
411 //X        intf_ErrMsg( "vcd error: "
412 //X                     " IORegistryEntryCreateCFProperties: 0x%08x", ret );
413         IOObjectRelease( service );
414         return( NULL );
415     }
416
417     /* get the TOC from the dictionary */
418     if( ( data = (CFDataRef) CFDictionaryGetValue( properties,
419                                     CFSTR(kIOCDMediaTOCKey) ) ) != NULL )
420     {
421         CFRange range;
422         CFIndex buf_len;
423
424         buf_len = CFDataGetLength( data ) + 1;
425         range = CFRangeMake( 0, buf_len );
426
427         if( ( pTOC = (CDTOC *)malloc( buf_len ) ) != NULL )
428         {
429             CFDataGetBytes( data, range, (u_char *)pTOC );
430         }
431     }
432     else
433     {
434 //X        intf_ErrMsg( "vcd error: CFDictionaryGetValue failed" );
435     }
436
437     CFRelease( properties );
438     IOObjectRelease( service ); 
439
440     return( pTOC ); 
441 }
442
443 /****************************************************************************
444  * getNumberOfDescriptors: get number of descriptors in TOC 
445  ****************************************************************************/
446 int getNumberOfDescriptors( CDTOC *pTOC )
447 {
448     int i_descriptors;
449
450     /* get TOC length */
451     i_descriptors = pTOC->length;
452
453     /* remove the first and last session */
454     i_descriptors -= ( sizeof(pTOC->sessionFirst) +
455                        sizeof(pTOC->sessionLast) );
456
457     /* divide the length by the size of a single descriptor */
458     i_descriptors /= sizeof(CDTOCDescriptor);
459
460     return( i_descriptors );
461 }
462
463 /****************************************************************************
464  * getNumberOfTracks: get number of tracks in TOC 
465  ****************************************************************************/
466 int getNumberOfTracks( CDTOC *pTOC, int i_descriptors )
467 {
468     u_char track;
469     int i, i_tracks = 0; 
470     CDTOCDescriptor *pTrackDescriptors;
471
472     pTrackDescriptors = pTOC->descriptors;
473
474     for( i = i_descriptors; i >= 0; i-- )
475     {
476         track = pTrackDescriptors[i].point;
477
478         if( track > CD_MAX_TRACK_NO || track < CD_MIN_TRACK_NO )
479             continue;
480
481         i_tracks++; 
482     }
483
484     return( i_tracks );
485 }
486 #endif