1 /*****************************************************************************
2 * libdvdcss.c: DVD reading library.
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: libdvdcss.c,v 1.8 2001/07/25 00:23:40 sam Exp $
7 * Authors: Stéphane Borel <stef@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
32 #include <sys/types.h>
42 # include "input_iovec.h"
44 # include <sys/uio.h> /* struct iovec */
50 #include "videolan/dvdcss.h"
51 #include "libdvdcss.h"
54 /*****************************************************************************
56 *****************************************************************************/
57 static int _dvdcss_open ( dvdcss_handle, char *psz_target );
58 static int _dvdcss_close ( dvdcss_handle );
59 static int _dvdcss_seek ( dvdcss_handle, int i_blocks );
60 static int _dvdcss_read ( dvdcss_handle, void *p_buffer, int i_blocks );
61 static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
63 /*****************************************************************************
64 * Local prototypes, win32 specific
65 *****************************************************************************/
67 static int _win32_dvdcss_readv ( int i_fd, struct iovec *p_iovec,
69 static int _win32_dvdcss_aopen ( char c_drive, dvdcss_handle dvdcss );
70 static int _win32_dvdcss_aclose ( int i_fd );
71 static int _win32_dvdcss_aseek ( int i_fd, int i_blocks, int i_method );
72 static int _win32_dvdcss_aread ( int i_fd, void *p_data, int i_blocks );
75 /*****************************************************************************
76 * dvdcss_open: initialize library, open a DVD device, crack CSS key
77 *****************************************************************************/
78 extern dvdcss_handle dvdcss_open ( char *psz_target, int i_flags )
84 /* Allocate the library structure */
85 dvdcss = malloc( sizeof( struct dvdcss_s ) );
88 if( ! (i_flags & DVDCSS_INIT_QUIET) )
90 DVDCSS_ERROR( "could not initialize library" );
96 /* Initialize structure */
97 dvdcss->p_titles = NULL;
98 dvdcss->b_debug = i_flags & DVDCSS_INIT_DEBUG;
99 dvdcss->b_errors = !(i_flags & DVDCSS_INIT_QUIET);
100 dvdcss->psz_error = "no error";
102 i_ret = _dvdcss_open( dvdcss, psz_target );
109 i_ret = CSSTest( dvdcss );
112 _dvdcss_error( dvdcss, "css test failed" );
113 _dvdcss_close( dvdcss );
118 dvdcss->b_encrypted = i_ret;
120 /* If drive is encrypted, crack its key */
121 if( dvdcss->b_encrypted )
123 i_ret = CSSInit( dvdcss );
127 _dvdcss_close( dvdcss );
136 /*****************************************************************************
137 * dvdcss_error: return the last libdvdcss error message
138 *****************************************************************************/
139 extern char * dvdcss_error ( dvdcss_handle dvdcss )
141 return dvdcss->psz_error;
144 /*****************************************************************************
145 * dvdcss_seek: seek into the device
146 *****************************************************************************/
147 extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
149 return _dvdcss_seek( dvdcss, i_blocks );
152 /*****************************************************************************
153 * dvdcss_crack: crack the current title key
154 *****************************************************************************/
155 extern int dvdcss_crack ( dvdcss_handle dvdcss, int i_block )
157 dvd_title_t *p_title;
161 if( ! dvdcss->b_encrypted )
166 /* Check if we've already cracked this key */
167 p_title = dvdcss->p_titles;
168 while( p_title != NULL
169 && p_title->p_next != NULL
170 && p_title->p_next->i_startlb <= i_block )
172 p_title = p_title->p_next;
176 && p_title->i_startlb == i_block )
178 /* We've already cracked this key, nothing to do */
182 /* Crack CSS title key for current VTS */
183 i_ret = CSSGetKey( dvdcss, i_block, p_key );
187 _dvdcss_error( dvdcss, "fatal error in vts css key" );
192 _dvdcss_error( dvdcss, "decryption unavailable" );
196 /* Add key to keytable if it isn't empty */
197 if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] )
199 dvd_title_t *p_newtitle;
201 /* Find our spot in the list */
203 p_title = dvdcss->p_titles;
204 while( p_title != NULL
205 && p_title->i_startlb < i_block )
207 p_newtitle = p_title;
208 p_title = p_title->p_next;
211 /* Save the found title */
212 p_title = p_newtitle;
214 /* Write in the new title and its key */
215 p_newtitle = malloc( sizeof( dvd_title_t ) );
216 p_newtitle->i_startlb = i_block;
217 memcpy( p_newtitle->p_key, p_key, KEY_SIZE );
219 /* Link the new title, either at the beginning or inside the list */
220 if( p_title == NULL )
222 dvdcss->p_titles = p_newtitle;
223 p_newtitle->p_next = NULL;
227 p_newtitle->p_next = p_title->p_next;
228 p_title->p_next = p_newtitle;
235 /*****************************************************************************
236 * dvdcss_read: read data from the device, decrypt if requested
237 *****************************************************************************/
238 extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
242 dvd_title_t *p_title;
245 i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
248 || !dvdcss->b_encrypted
249 || !(i_flags & DVDCSS_READ_DECRYPT) )
255 p_title = dvdcss->p_titles;
256 while( p_title != NULL
258 && p_title->p_next->i_startlb < dvdcss->i_seekpos )
260 p_title = p_title->p_next;
263 if( p_title == NULL )
265 /* no css key found to use, so no decryption to do */
269 /* Decrypt the blocks we managed to read */
270 for( i_index = i_ret; i_index; i_index-- )
272 CSSDescrambleSector( p_title->p_key, p_buffer );
273 ((u8*)p_buffer)[0x14] &= 0x8f;
274 (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
280 /*****************************************************************************
281 * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
282 *****************************************************************************/
283 extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
287 #define P_IOVEC ((struct iovec*)p_iovec)
288 dvd_title_t *p_title;
293 i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );
296 || !dvdcss->b_encrypted
297 || !(i_flags & DVDCSS_READ_DECRYPT) )
303 p_title = dvdcss->p_titles;
304 while( p_title != NULL
305 && p_title->p_next != NULL
306 && p_title->p_next->i_startlb < dvdcss->i_seekpos )
308 p_title = p_title->p_next;
311 if( p_title == NULL )
313 /* no css key found to use, so no decryption to do */
317 /* Initialize loop for decryption */
318 iov_base = P_IOVEC->iov_base;
319 iov_len = P_IOVEC->iov_len;
321 /* Decrypt the blocks we managed to read */
322 for( i_index = i_ret; i_index; i_index-- )
324 /* Check that iov_len is a multiple of 2048 */
325 if( iov_len & 0x7ff )
330 while( iov_len == 0 )
333 iov_base = P_IOVEC->iov_base;
334 iov_len = P_IOVEC->iov_len;
337 CSSDescrambleSector( p_title->p_key, iov_base );
338 ((u8*)iov_base)[0x14] &= 0x8f;
340 (u8*)iov_base += DVDCSS_BLOCK_SIZE;
341 (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
348 /*****************************************************************************
349 * dvdcss_close: close the DVD device and clean up the library
350 *****************************************************************************/
351 extern int dvdcss_close ( dvdcss_handle dvdcss )
353 dvd_title_t *p_title;
356 /* Free our list of keys */
357 p_title = dvdcss->p_titles;
360 dvd_title_t *p_tmptitle = p_title->p_next;
362 p_title = p_tmptitle;
365 i_ret = _dvdcss_close( dvdcss );
377 /* Following functions are local */
379 static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
385 _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
386 (HANDLE) dvdcss->i_fd =
387 CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
388 FILE_SHARE_READ | FILE_SHARE_WRITE,
389 NULL, OPEN_EXISTING, 0, NULL );
390 if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
392 _dvdcss_error( dvdcss, "failed opening device" );
398 dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
399 if( dvdcss->i_fd == -1 )
401 _dvdcss_error( dvdcss, "failed opening device" );
407 dvdcss->i_fd = open( psz_target, 0 );
409 if( dvdcss->i_fd == -1 )
411 _dvdcss_error( dvdcss, "failed opening device" );
420 static int _dvdcss_close ( dvdcss_handle dvdcss )
425 CloseHandle( (HANDLE) dvdcss->i_fd );
429 _win32_dvdcss_aclose( dvdcss->i_fd );
432 close( dvdcss->i_fd );
438 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
443 LARGE_INTEGER li_read;
445 #ifndef INVALID_SET_FILE_POINTER
446 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
449 li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
451 li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
453 &li_read.HighPart, FILE_BEGIN );
454 if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
455 && GetLastError() != NO_ERROR)
457 li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
460 li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
461 return (int)li_read.QuadPart;
465 return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
470 dvdcss->i_seekpos = i_blocks;
472 i_read = lseek( dvdcss->i_fd,
473 (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
475 return i_read / DVDCSS_BLOCK_SIZE;
480 static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
487 if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
488 i_blocks * DVDCSS_BLOCK_SIZE,
489 (LPDWORD)&i_bytes, NULL ) )
493 return i_bytes / DVDCSS_BLOCK_SIZE;
497 return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
503 i_bytes = read( dvdcss->i_fd, p_buffer,
504 (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
505 return i_bytes / DVDCSS_BLOCK_SIZE;
510 static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
516 i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks );
519 i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
520 return i_read / DVDCSS_BLOCK_SIZE;
526 /*****************************************************************************
527 * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
528 * _win32_dvdcss_aread for win9x
529 *****************************************************************************/
530 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
533 int i_index, i_len, i_total = 0;
534 unsigned char *p_base;
539 for( i_index = i_num_buffers; i_index; i_index-- )
541 i_len = p_iovec->iov_len;
542 p_base = p_iovec->iov_base;
546 unsigned long int i_bytes;
547 if( !ReadFile( (HANDLE) i_fd, p_base, i_len, &i_bytes, NULL ) )
550 /* One of the reads failed, too bad.
551 We won't even bother returning the reads that went well,
552 and like in the posix spec the file postition is left
553 unspecified after a failure */
555 i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
559 if( i_blocks != (i_len / DVDCSS_BLOCK_SIZE) )
561 /* we reached the end of the file */
572 for( i_index = i_num_buffers; i_index; i_index-- )
574 i_len = p_iovec->iov_len / DVDCSS_BLOCK_SIZE;
575 p_base = p_iovec->iov_base;
579 i_blocks = _win32_dvdcss_aread( i_fd, p_base, i_len );
582 return -1; /* idem */
587 if( i_blocks != i_len )
589 /* we reached the end of the file or a signal interrupted
602 /*****************************************************************************
603 * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
605 *****************************************************************************/
606 static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
610 struct w32_aspidev *fd;
611 int i, j, i_hostadapters;
612 long (*lpGetSupport)( void );
613 long (*lpSendCommand)( void* );
615 hASPI = LoadLibrary( "wnaspi32.dll" );
618 _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
622 (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
623 (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
625 if(lpGetSupport == NULL || lpSendCommand == NULL )
627 _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
628 FreeLibrary( hASPI );
632 dwSupportInfo = lpGetSupport();
634 if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
636 _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
637 FreeLibrary( hASPI );
641 if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
643 _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
644 FreeLibrary( hASPI );
648 i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
649 if( i_hostadapters == 0 )
651 FreeLibrary( hASPI );
655 fd = malloc( sizeof( struct w32_aspidev ) );
658 FreeLibrary( hASPI );
663 fd->hASPI = (long) hASPI;
664 fd->lpSendCommand = lpSendCommand;
666 c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
668 for( i = 0; i < i_hostadapters; i++ )
670 for( j = 0; j < 15; j++ )
672 struct SRB_GetDiskInfo srbDiskInfo;
674 srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO;
675 srbDiskInfo.SRB_HaId = i;
676 srbDiskInfo.SRB_Flags = 0;
677 srbDiskInfo.SRB_Hdr_Rsvd = 0;
678 srbDiskInfo.SRB_Target = j;
679 srbDiskInfo.SRB_Lun = 0;
681 lpSendCommand( (void*) &srbDiskInfo );
683 if( srbDiskInfo.SRB_Status == SS_COMP &&
684 srbDiskInfo.SRB_Int13HDriveInfo == c_drive )
686 fd->i_sid = MAKEWORD( i, j );
693 FreeLibrary( hASPI );
694 _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
699 /*****************************************************************************
700 * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
702 *****************************************************************************/
703 static int _win32_dvdcss_aclose( int i_fd )
705 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
707 FreeLibrary( (HMODULE) fd->hASPI );
708 free( (void*) i_fd );
713 /*****************************************************************************
714 * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
716 * returns the number of blocks read.
717 *****************************************************************************/
718 static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
721 char sz_buf[ DVDCSS_BLOCK_SIZE ];
722 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
724 i_old_blocks = fd->i_blocks;
725 fd->i_blocks = i_blocks;
727 if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
729 fd->i_blocks = i_old_blocks;
738 /*****************************************************************************
739 * _win32_dvdcss_aread: aspi version of _dvdcss_read
741 * returns the number of blocks read.
742 *****************************************************************************/
743 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
746 DWORD dwStart, dwLen;
747 struct SRB_ExecSCSICmd ssc;
748 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
750 memset( &ssc, 0, sizeof( ssc ) );
752 dwStart = fd->i_blocks;
755 hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
761 ssc.SRB_Cmd = SC_EXEC_SCSI_CMD;
762 ssc.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
763 ssc.SRB_HaId = LOBYTE( fd->i_sid );
764 ssc.SRB_Target = HIBYTE( fd->i_sid );
765 ssc.SRB_SenseLen = SENSE_LEN;
766 ssc.SRB_PostProc = (LPVOID) hEvent;
768 ssc.SRB_BufLen = dwLen * DVDCSS_BLOCK_SIZE;
769 ssc.SRB_BufPointer = p_data;
772 ssc.CDBByte[0] = 0xA8; /* RAW */
773 ssc.CDBByte[2] = (UCHAR) dwStart >> 24;
774 ssc.CDBByte[3] = (UCHAR) (dwStart >> 16) & 0xff;
775 ssc.CDBByte[4] = (UCHAR) (dwStart >> 8) & 0xff;
776 ssc.CDBByte[5] = (UCHAR) (dwStart) & 0xff;
777 ssc.CDBByte[6] = (UCHAR) dwLen >> 24;
778 ssc.CDBByte[7] = (UCHAR) (dwLen >> 16) & 0xff;
779 ssc.CDBByte[8] = (UCHAR) (dwLen >> 8) & 0xff;
780 ssc.CDBByte[9] = (UCHAR) (dwLen) & 0xff;
782 ResetEvent( hEvent );
783 if( fd->lpSendCommand( (void*) &ssc ) == SS_PENDING )
785 WaitForSingleObject( hEvent, INFINITE );
788 CloseHandle( hEvent );
790 if( ssc.SRB_Status != SS_COMP )
795 fd->i_blocks += i_blocks;