1 /*****************************************************************************
2 * libdvdcss.c: DVD reading library.
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: libdvdcss.c,v 1.15 2001/09/09 13:43:25 sam Exp $
7 * Authors: Stéphane Borel <stef@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Håkan Hjort <d95hjort@dtek.chalmers.se>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <sys/types.h>
43 # include <io.h> /* read() */
45 # include <sys/uio.h> /* struct iovec */
52 # include "input_iovec.h"
55 #include "videolan/dvdcss.h"
56 #include "libdvdcss.h"
59 /*****************************************************************************
61 *****************************************************************************/
62 static int _dvdcss_open ( dvdcss_handle, char *psz_target );
63 static int _dvdcss_close ( dvdcss_handle );
64 static int _dvdcss_seek ( dvdcss_handle, int i_blocks );
65 static int _dvdcss_read ( dvdcss_handle, void *p_buffer, int i_blocks );
66 static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
68 /*****************************************************************************
69 * Local prototypes, win32 specific
70 *****************************************************************************/
72 static int _win32_dvdcss_readv ( int i_fd, struct iovec *p_iovec,
73 int i_num_buffers, char *p_tmp_buffer );
74 static int _win32_dvdcss_aopen ( char c_drive, dvdcss_handle dvdcss );
75 static int _win32_dvdcss_aclose ( int i_fd );
76 static int _win32_dvdcss_aseek ( int i_fd, int i_blocks, int i_method );
77 static int _win32_dvdcss_aread ( int i_fd, void *p_data, int i_blocks );
80 /*****************************************************************************
81 * dvdcss_open: initialize library, open a DVD device, crack CSS key
82 *****************************************************************************/
83 extern dvdcss_handle dvdcss_open ( char *psz_target, int i_flags )
89 /* Allocate the library structure */
90 dvdcss = malloc( sizeof( struct dvdcss_s ) );
93 if( ! (i_flags & DVDCSS_INIT_QUIET) )
95 DVDCSS_ERROR( "could not initialize library" );
101 /* Initialize structure */
102 dvdcss->p_titles = NULL;
103 dvdcss->b_debug = i_flags & DVDCSS_INIT_DEBUG;
104 dvdcss->b_errors = !(i_flags & DVDCSS_INIT_QUIET);
105 dvdcss->psz_error = "no error";
107 i_ret = _dvdcss_open( dvdcss, psz_target );
114 i_ret = CSSTest( dvdcss );
117 _dvdcss_error( dvdcss, "CSS test failed" );
118 /* Disable the CSS ioctls and hope that it works? */
119 dvdcss->b_ioctls = 0;
120 dvdcss->b_encrypted = 1;
124 dvdcss->b_ioctls = 1;
125 dvdcss->b_encrypted = i_ret;
128 /* If disc is CSS protected and the ioctls work, authenticate the drive */
129 if( dvdcss->b_encrypted && dvdcss->b_ioctls )
131 i_ret = CSSInit( dvdcss );
135 _dvdcss_close( dvdcss );
144 /*****************************************************************************
145 * dvdcss_error: return the last libdvdcss error message
146 *****************************************************************************/
147 extern char * dvdcss_error ( dvdcss_handle dvdcss )
149 return dvdcss->psz_error;
152 /*****************************************************************************
153 * dvdcss_seek: seek into the device
154 *****************************************************************************/
155 extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
157 return _dvdcss_seek( dvdcss, i_blocks );
160 /*****************************************************************************
161 * dvdcss_title: crack the current title key if needed
162 *****************************************************************************/
163 extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
165 dvd_title_t *p_title;
169 if( ! dvdcss->b_encrypted )
174 //fprintf( stderr, "looking for a key for offset %i\n", i_block );
176 /* Check if we've already cracked this key */
177 p_title = dvdcss->p_titles;
178 while( p_title != NULL
179 && p_title->p_next != NULL
180 && p_title->p_next->i_startlb <= i_block )
182 p_title = p_title->p_next;
186 && p_title->i_startlb == i_block )
188 /* We've already cracked this key, nothing to do */
192 /* Crack CSS title key for current VTS */
193 i_ret = CSSGetKey( dvdcss, i_block, p_key );
197 _dvdcss_error( dvdcss, "fatal error in vts css key" );
202 _dvdcss_error( dvdcss, "decryption unavailable" );
206 //fprintf( stderr, "cracked key is %.2x %.2x %.2x %.2x %.2x\n",
207 // p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] );
209 /* Add key to keytable if it isn't empty */
210 /* We need to cache the fact that a title is unencrypted
211 if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] ) */
213 dvd_title_t *p_newtitle;
215 /* Find our spot in the list */
217 p_title = dvdcss->p_titles;
218 while( p_title != NULL
219 && p_title->i_startlb < i_block )
221 p_newtitle = p_title;
222 p_title = p_title->p_next;
225 /* Save the found title */
226 p_title = p_newtitle;
228 /* Write in the new title and its key */
229 p_newtitle = malloc( sizeof( dvd_title_t ) );
230 p_newtitle->i_startlb = i_block;
231 memcpy( p_newtitle->p_key, p_key, KEY_SIZE );
233 /* Link the new title, either at the beginning or inside the list */
234 if( p_title == NULL )
236 dvdcss->p_titles = p_newtitle;
237 p_newtitle->p_next = NULL;
241 p_newtitle->p_next = p_title->p_next;
242 p_title->p_next = p_newtitle;
249 /*****************************************************************************
250 * dvdcss_read: read data from the device, decrypt if requested
251 *****************************************************************************/
252 extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
256 dvd_title_t *p_title;
259 i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
262 || !dvdcss->b_encrypted
263 || !(i_flags & DVDCSS_READ_DECRYPT) )
269 p_title = dvdcss->p_titles;
270 while( p_title != NULL
272 && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
274 p_title = p_title->p_next;
277 if( p_title == NULL )
279 /* no css key found to use, so no decryption to do */
283 /* For what we believe is an unencrypted title,
284 check that there are no encrypted blocks */
285 if( !( p_title->p_key[0] | p_title->p_key[1] | p_title->p_key[2] |
286 p_title->p_key[3] | p_title->p_key[4] ) )
288 for( i_index = i_ret; i_index; i_index-- )
290 if( ((u8*)p_buffer)[0x14] & 0x30 )
292 _dvdcss_error( dvdcss, "no key but found encrypted block" );
293 /* Only return the initial range of unscrambled blocks? */
295 /* or fail completely? return 0; */
297 (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
301 /* Decrypt the blocks we managed to read */
302 for( i_index = i_ret; i_index; i_index-- )
304 CSSDescrambleSector( p_title->p_key, p_buffer );
305 ((u8*)p_buffer)[0x14] &= 0x8f;
306 (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
312 /*****************************************************************************
313 * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
314 *****************************************************************************/
315 extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
319 #define P_IOVEC ((struct iovec*)p_iovec)
320 dvd_title_t *p_title;
325 i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );
328 || !dvdcss->b_encrypted
329 || !(i_flags & DVDCSS_READ_DECRYPT) )
335 p_title = dvdcss->p_titles;
336 while( p_title != NULL
337 && p_title->p_next != NULL
338 && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
340 p_title = p_title->p_next;
343 if( p_title == NULL )
345 /* no css key found to use, so no decryption to do */
349 /* Initialize loop for decryption */
350 iov_base = P_IOVEC->iov_base;
351 iov_len = P_IOVEC->iov_len;
353 /* Decrypt the blocks we managed to read */
354 for( i_index = i_ret; i_index; i_index-- )
356 /* Check that iov_len is a multiple of 2048 */
357 if( iov_len & 0x7ff )
362 while( iov_len == 0 )
365 iov_base = P_IOVEC->iov_base;
366 iov_len = P_IOVEC->iov_len;
369 CSSDescrambleSector( p_title->p_key, iov_base );
370 ((u8*)iov_base)[0x14] &= 0x8f;
372 (u8*)iov_base += DVDCSS_BLOCK_SIZE;
373 (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
380 /*****************************************************************************
381 * dvdcss_close: close the DVD device and clean up the library
382 *****************************************************************************/
383 extern int dvdcss_close ( dvdcss_handle dvdcss )
385 dvd_title_t *p_title;
388 /* Free our list of keys */
389 p_title = dvdcss->p_titles;
392 dvd_title_t *p_tmptitle = p_title->p_next;
394 p_title = p_tmptitle;
397 i_ret = _dvdcss_close( dvdcss );
409 /* Following functions are local */
411 static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
417 _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
418 (HANDLE) dvdcss->i_fd =
419 CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
420 FILE_SHARE_READ | FILE_SHARE_WRITE,
421 NULL, OPEN_EXISTING, 0, NULL );
422 if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
424 _dvdcss_error( dvdcss, "failed opening device" );
430 dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
431 if( dvdcss->i_fd == -1 )
433 _dvdcss_error( dvdcss, "failed opening device" );
438 /* initialise readv temporary buffer */
439 dvdcss->p_readv_buffer = NULL;
440 dvdcss->i_readv_buf_size = 0;
443 dvdcss->i_fd = open( psz_target, 0 );
445 if( dvdcss->i_fd == -1 )
447 _dvdcss_error( dvdcss, "failed opening device" );
456 static int _dvdcss_close ( dvdcss_handle dvdcss )
461 CloseHandle( (HANDLE) dvdcss->i_fd );
465 _win32_dvdcss_aclose( dvdcss->i_fd );
468 /* Free readv temporary buffer */
469 if( dvdcss->p_readv_buffer )
471 free( dvdcss->p_readv_buffer );
472 dvdcss->p_readv_buffer = NULL;
473 dvdcss->i_readv_buf_size = 0;
477 close( dvdcss->i_fd );
484 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
487 dvdcss->i_seekpos = i_blocks;
491 LARGE_INTEGER li_read;
493 #ifndef INVALID_SET_FILE_POINTER
494 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
497 li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
499 li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
501 &li_read.HighPart, FILE_BEGIN );
502 if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
503 && GetLastError() != NO_ERROR)
505 li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
508 li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
509 return (int)li_read.QuadPart;
513 return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
518 dvdcss->i_seekpos = i_blocks;
520 i_read = lseek( dvdcss->i_fd,
521 (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
523 return i_read / DVDCSS_BLOCK_SIZE;
528 static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
535 if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
536 i_blocks * DVDCSS_BLOCK_SIZE,
537 (LPDWORD)&i_bytes, NULL ) )
541 return i_bytes / DVDCSS_BLOCK_SIZE;
545 return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
551 i_bytes = read( dvdcss->i_fd, p_buffer,
552 (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
553 return i_bytes / DVDCSS_BLOCK_SIZE;
558 static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
564 /* Check the size of the readv temp buffer, just in case we need to
565 * realloc something bigger */
566 if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
568 dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;
570 if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );
572 /* Allocate a buffer which will be used as a temporary storage
574 dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
575 if( !dvdcss->p_readv_buffer )
577 _dvdcss_error( dvdcss, " failed (readv)" );
582 i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks,
583 dvdcss->p_readv_buffer );
587 i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
588 return i_read / DVDCSS_BLOCK_SIZE;
596 /*****************************************************************************
597 * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
598 * _win32_dvdcss_aread for win9x
599 *****************************************************************************/
600 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
601 int i_num_buffers, char *p_tmp_buffer )
604 int i_blocks, i_blocks_total = 0;
606 for( i_index = i_num_buffers; i_index; i_index-- )
608 i_blocks_total += p_iovec[i_index-1].iov_len;
611 if( i_blocks_total <= 0 ) return 0;
613 i_blocks_total /= DVDCSS_BLOCK_SIZE;
617 unsigned long int i_bytes;
618 if( !ReadFile( (HANDLE)i_fd, p_tmp_buffer,
619 i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
622 /* The read failed... too bad.
623 As in the posix spec the file postition is left
624 unspecified after a failure */
626 i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
630 i_blocks = _win32_dvdcss_aread( i_fd, p_tmp_buffer, i_blocks_total );
633 return -1; /* idem */
637 /* We just have to copy the content of the temp buffer into the iovecs */
639 i_blocks_total = i_blocks;
640 while( i_blocks_total > 0 )
642 memcpy( p_iovec[i_index].iov_base,
643 &p_tmp_buffer[(i_blocks - i_blocks_total) * DVDCSS_BLOCK_SIZE],
644 p_iovec[i_index].iov_len );
645 /* if we read less blocks than asked, we'll just end up copying
646 garbage, this isn't an issue as we return the number of
647 blocks actually read */
648 i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
655 /*****************************************************************************
656 * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
658 *****************************************************************************/
659 static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
663 struct w32_aspidev *fd;
664 int i, j, i_hostadapters;
665 long (*lpGetSupport)( void );
666 long (*lpSendCommand)( void* );
668 hASPI = LoadLibrary( "wnaspi32.dll" );
671 _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
675 (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
676 (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
678 if(lpGetSupport == NULL || lpSendCommand == NULL )
680 _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
681 FreeLibrary( hASPI );
685 dwSupportInfo = lpGetSupport();
687 if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
689 _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
690 FreeLibrary( hASPI );
694 if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
696 _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
697 FreeLibrary( hASPI );
701 i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
702 if( i_hostadapters == 0 )
704 FreeLibrary( hASPI );
708 fd = malloc( sizeof( struct w32_aspidev ) );
711 FreeLibrary( hASPI );
716 fd->hASPI = (long) hASPI;
717 fd->lpSendCommand = lpSendCommand;
719 c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
721 for( i = 0; i < i_hostadapters; i++ )
723 for( j = 0; j < 15; j++ )
725 struct SRB_GetDiskInfo srbDiskInfo;
727 srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO;
728 srbDiskInfo.SRB_HaId = i;
729 srbDiskInfo.SRB_Flags = 0;
730 srbDiskInfo.SRB_Hdr_Rsvd = 0;
731 srbDiskInfo.SRB_Target = j;
732 srbDiskInfo.SRB_Lun = 0;
734 lpSendCommand( (void*) &srbDiskInfo );
736 if( (srbDiskInfo.SRB_Status == SS_COMP) &&
737 (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
739 fd->i_sid = MAKEWORD( i, j );
746 FreeLibrary( hASPI );
747 _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
751 /*****************************************************************************
752 * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
754 *****************************************************************************/
755 static int _win32_dvdcss_aclose( int i_fd )
757 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
759 FreeLibrary( (HMODULE) fd->hASPI );
760 free( (void*) i_fd );
765 /*****************************************************************************
766 * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
768 * returns the number of blocks read.
769 *****************************************************************************/
770 static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
773 char sz_buf[ DVDCSS_BLOCK_SIZE ];
774 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
776 i_old_blocks = fd->i_blocks;
777 fd->i_blocks = i_blocks;
779 if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
781 fd->i_blocks = i_old_blocks;
790 /*****************************************************************************
791 * _win32_dvdcss_aread: aspi version of _dvdcss_read
793 * returns the number of blocks read.
794 *****************************************************************************/
795 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
798 struct SRB_ExecSCSICmd ssc;
799 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
801 /* Create the transfer completion event */
802 hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
808 memset( &ssc, 0, sizeof( ssc ) );
810 ssc.SRB_Cmd = SC_EXEC_SCSI_CMD;
811 ssc.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
812 ssc.SRB_HaId = LOBYTE( fd->i_sid );
813 ssc.SRB_Target = HIBYTE( fd->i_sid );
814 ssc.SRB_SenseLen = SENSE_LEN;
816 ssc.SRB_PostProc = (LPVOID) hEvent;
817 ssc.SRB_BufPointer = p_data;
820 ssc.CDBByte[0] = 0xA8; /* RAW */
821 ssc.CDBByte[2] = (UCHAR) (fd->i_blocks >> 24);
822 ssc.CDBByte[3] = (UCHAR) (fd->i_blocks >> 16) & 0xff;
823 ssc.CDBByte[4] = (UCHAR) (fd->i_blocks >> 8) & 0xff;
824 ssc.CDBByte[5] = (UCHAR) (fd->i_blocks) & 0xff;
826 /* We have to break down the reads into 64kb pieces (ASPI restriction) */
829 ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
833 /* Initiate transfer */
834 ResetEvent( hEvent );
835 fd->lpSendCommand( (void*) &ssc );
837 /* transfer the next 64kb (_win32_dvdcss_aread is called recursively)
838 * We need to check the status of the read on return */
839 if( _win32_dvdcss_aread( i_fd, (u8*) p_data + 32 * DVDCSS_BLOCK_SIZE,
847 /* This is the last transfer */
848 ssc.SRB_BufLen = i_blocks * DVDCSS_BLOCK_SIZE;
849 ssc.CDBByte[9] = (UCHAR) i_blocks;
850 fd->i_blocks += i_blocks;
852 /* Initiate transfer */
853 ResetEvent( hEvent );
854 fd->lpSendCommand( (void*) &ssc );
858 /* If the command has still not been processed, wait until it's finished */
859 if( ssc.SRB_Status == SS_PENDING )
861 WaitForSingleObject( hEvent, INFINITE );
863 CloseHandle( hEvent );
865 /* check that the transfer went as planned */
866 if( ssc.SRB_Status != SS_COMP )