libdvdcss.c 23.6 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3 4
/*****************************************************************************
 * libdvdcss.c: DVD reading library.
 *****************************************************************************
 * Copyright (C) 1998-2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: libdvdcss.c,v 1.14 2001/08/06 13:28:00 sam Exp $
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * Authors: Stphane Borel <stef@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "defs.h"

#include <stdio.h>
#include <stdlib.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
32
#include <string.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
33 34 35
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
36 37 38 39

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
40 41

#if defined( WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
42
#   include <io.h>                                                 /* read() */
Sam Hocevar's avatar
 
Sam Hocevar committed
43 44 45 46 47 48 49
#else
#   include <sys/uio.h>                                      /* struct iovec */
#endif

#include "config.h"
#include "common.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
50 51 52 53
#if defined( WIN32 )
#   include "input_iovec.h"
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
54 55
#include "videolan/dvdcss.h"
#include "libdvdcss.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
56
#include "ioctl.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
57 58 59 60 61 62 63 64 65 66

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int _dvdcss_open  ( dvdcss_handle, char *psz_target );
static int _dvdcss_close ( dvdcss_handle );
static int _dvdcss_seek  ( dvdcss_handle, int i_blocks );
static int _dvdcss_read  ( dvdcss_handle, void *p_buffer, int i_blocks );
static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );

Sam Hocevar's avatar
 
Sam Hocevar committed
67 68 69 70
/*****************************************************************************
 * Local prototypes, win32 specific
 *****************************************************************************/
#if defined( WIN32 )
Gildas Bazin's avatar
 
Gildas Bazin committed
71
static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
Gildas Bazin's avatar
 
Gildas Bazin committed
72
                                  int i_num_buffers, char *p_tmp_buffer );
Gildas Bazin's avatar
 
Gildas Bazin committed
73 74 75 76
static int _win32_dvdcss_aopen  ( char c_drive, dvdcss_handle dvdcss );
static int _win32_dvdcss_aclose ( int i_fd );
static int _win32_dvdcss_aseek  ( int i_fd, int i_blocks, int i_method );
static int _win32_dvdcss_aread  ( int i_fd, void *p_data, int i_blocks );
Sam Hocevar's avatar
 
Sam Hocevar committed
77 78
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
79
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
80
 * dvdcss_open: initialize library, open a DVD device, crack CSS key
Sam Hocevar's avatar
 
Sam Hocevar committed
81
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
82
extern dvdcss_handle dvdcss_open ( char *psz_target, int i_flags )
Sam Hocevar's avatar
 
Sam Hocevar committed
83
{
Sam Hocevar's avatar
 
Sam Hocevar committed
84 85
    int i_ret;

Sam Hocevar's avatar
 
Sam Hocevar committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    dvdcss_handle dvdcss;

    /* Allocate the library structure */
    dvdcss = malloc( sizeof( struct dvdcss_s ) );
    if( dvdcss == NULL )
    {
        if( ! (i_flags & DVDCSS_INIT_QUIET) )
        {
            DVDCSS_ERROR( "could not initialize library" );
        }

        return NULL;
    }

    /* Initialize structure */
Sam Hocevar's avatar
 
Sam Hocevar committed
101
    dvdcss->p_titles = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
102 103 104 105 106 107 108
    dvdcss->b_debug = i_flags & DVDCSS_INIT_DEBUG;
    dvdcss->b_errors = !(i_flags & DVDCSS_INIT_QUIET);
    dvdcss->psz_error = "no error";

    i_ret = _dvdcss_open( dvdcss, psz_target );
    if( i_ret < 0 )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
109 110
        free( dvdcss );
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
111 112 113 114 115 116 117
    }

    i_ret = CSSTest( dvdcss );
    if( i_ret < 0 )
    {
        _dvdcss_error( dvdcss, "css test failed" );
        _dvdcss_close( dvdcss );
Sam Hocevar's avatar
 
Sam Hocevar committed
118 119
        free( dvdcss );
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
120 121 122 123 124 125 126 127 128 129 130 131
    }

    dvdcss->b_encrypted = i_ret;

    /* If drive is encrypted, crack its key */
    if( dvdcss->b_encrypted )
    {
        i_ret = CSSInit( dvdcss );

        if( i_ret < 0 )
        {
            _dvdcss_close( dvdcss );
Sam Hocevar's avatar
 
Sam Hocevar committed
132 133
            free( dvdcss );
            return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
134 135 136
        }
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
137 138
    return dvdcss;
}
Sam Hocevar's avatar
 
Sam Hocevar committed
139

Sam Hocevar's avatar
 
Sam Hocevar committed
140 141 142 143 144 145
/*****************************************************************************
 * dvdcss_error: return the last libdvdcss error message
 *****************************************************************************/
extern char * dvdcss_error ( dvdcss_handle dvdcss )
{
    return dvdcss->psz_error;
Sam Hocevar's avatar
 
Sam Hocevar committed
146 147 148 149 150 151 152 153 154 155 156
}

/*****************************************************************************
 * dvdcss_seek: seek into the device
 *****************************************************************************/
extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
{
    return _dvdcss_seek( dvdcss, i_blocks );
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
157
 * dvdcss_title: crack the current title key if needed
Sam Hocevar's avatar
 
Sam Hocevar committed
158
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
159
extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
Sam Hocevar's avatar
 
Sam Hocevar committed
160
{
Sam Hocevar's avatar
 
Sam Hocevar committed
161
    dvd_title_t *p_title;
Sam Hocevar's avatar
 
Sam Hocevar committed
162
    dvd_key_t p_key;
Sam Hocevar's avatar
 
Sam Hocevar committed
163 164 165 166 167 168 169
    int i_ret;

    if( ! dvdcss->b_encrypted )
    {
        return 0;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
170 171
    //fprintf( stderr, "looking for a key for offset %i\n", i_block );

Sam Hocevar's avatar
 
Sam Hocevar committed
172
    /* Check if we've already cracked this key */
Sam Hocevar's avatar
 
Sam Hocevar committed
173 174 175 176
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next != NULL
            && p_title->p_next->i_startlb <= i_block )
Sam Hocevar's avatar
 
Sam Hocevar committed
177
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
178
        p_title = p_title->p_next;
Sam Hocevar's avatar
 
Sam Hocevar committed
179
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
180

Sam Hocevar's avatar
 
Sam Hocevar committed
181 182
    if( p_title != NULL
         && p_title->i_startlb == i_block )
Sam Hocevar's avatar
 
Sam Hocevar committed
183 184 185 186 187 188 189
    {
        /* We've already cracked this key, nothing to do */
        return 0;
    }

    /* Crack CSS title key for current VTS */
    i_ret = CSSGetKey( dvdcss, i_block, p_key );
Sam Hocevar's avatar
 
Sam Hocevar committed
190 191 192 193 194 195 196 197 198 199 200 201

    if( i_ret < 0 )
    {
        _dvdcss_error( dvdcss, "fatal error in vts css key" );
        return i_ret;
    }
    else if( i_ret > 0 )
    {
        _dvdcss_error( dvdcss, "decryption unavailable" );
        return -1;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
202 203 204
    //fprintf( stderr, "cracked key is %.2x %.2x %.2x %.2x %.2x\n",
    //         p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] );

Sam Hocevar's avatar
 
Sam Hocevar committed
205
    /* Add key to keytable if it isn't empty */
Sam Hocevar's avatar
 
Sam Hocevar committed
206
    if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] )
Sam Hocevar's avatar
 
Sam Hocevar committed
207
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
208 209
        dvd_title_t *p_newtitle;

Sam Hocevar's avatar
 
Sam Hocevar committed
210
        /* Find our spot in the list */
Sam Hocevar's avatar
 
Sam Hocevar committed
211 212 213 214
        p_newtitle = NULL;
        p_title = dvdcss->p_titles;
        while( p_title != NULL
                && p_title->i_startlb < i_block )
Sam Hocevar's avatar
 
Sam Hocevar committed
215
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
216 217
            p_newtitle = p_title;
            p_title = p_title->p_next;
Sam Hocevar's avatar
 
Sam Hocevar committed
218 219
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
        /* Save the found title */
        p_title = p_newtitle;

        /* Write in the new title and its key */
        p_newtitle = malloc( sizeof( dvd_title_t ) );
        p_newtitle->i_startlb = i_block;
        memcpy( p_newtitle->p_key, p_key, KEY_SIZE );

        /* Link the new title, either at the beginning or inside the list */
        if( p_title == NULL )
        {
            dvdcss->p_titles = p_newtitle;
            p_newtitle->p_next = NULL;
        }
        else
        {
            p_newtitle->p_next = p_title->p_next;
            p_title->p_next = p_newtitle;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
239 240
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
241 242 243 244 245 246 247 248 249 250
    return 0;
}

/*****************************************************************************
 * dvdcss_read: read data from the device, decrypt if requested
 *****************************************************************************/
extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
                                               int i_blocks,
                                               int i_flags )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
251
    dvd_title_t *p_title;
Sam Hocevar's avatar
 
Sam Hocevar committed
252
    int i_ret, i_index;
Sam Hocevar's avatar
 
Sam Hocevar committed
253 254 255

    i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );

Sam Hocevar's avatar
 
Sam Hocevar committed
256
    if( i_ret <= 0
Sam Hocevar's avatar
 
Sam Hocevar committed
257 258 259 260 261 262
         || !dvdcss->b_encrypted
         || !(i_flags & DVDCSS_READ_DECRYPT) )
    {
        return i_ret;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
263
    /* find our key */
Sam Hocevar's avatar
 
Sam Hocevar committed
264 265 266
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next
Sam Hocevar's avatar
 
Sam Hocevar committed
267
            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
Sam Hocevar's avatar
 
Sam Hocevar committed
268
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
269
        p_title = p_title->p_next;
Sam Hocevar's avatar
 
Sam Hocevar committed
270 271
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
272
    if( p_title == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
273 274 275 276 277 278 279
    {
        /* no css key found to use, so no decryption to do */
        return 0;
    }

    /* Decrypt the blocks we managed to read */
    for( i_index = i_ret; i_index; i_index-- )
Sam Hocevar's avatar
 
Sam Hocevar committed
280
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
281
        CSSDescrambleSector( p_title->p_key, p_buffer );
Sam Hocevar's avatar
 
Sam Hocevar committed
282 283 284 285
        ((u8*)p_buffer)[0x14] &= 0x8f;
        (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
286
    return i_ret;
Sam Hocevar's avatar
 
Sam Hocevar committed
287 288 289 290 291 292 293 294 295 296
}

/*****************************************************************************
 * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
 *****************************************************************************/
extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
                                                int i_blocks,
                                                int i_flags )
{
#define P_IOVEC ((struct iovec*)p_iovec)
Sam Hocevar's avatar
 
Sam Hocevar committed
297
    dvd_title_t *p_title;
Sam Hocevar's avatar
 
Sam Hocevar committed
298
    int i_ret, i_index;
Sam Hocevar's avatar
 
Sam Hocevar committed
299 300 301 302 303
    void *iov_base;
    size_t iov_len;

    i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );

Sam Hocevar's avatar
 
Sam Hocevar committed
304
    if( i_ret <= 0
Sam Hocevar's avatar
 
Sam Hocevar committed
305 306 307 308 309 310
         || !dvdcss->b_encrypted
         || !(i_flags & DVDCSS_READ_DECRYPT) )
    {
        return i_ret;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
311
    /* Find our key */
Sam Hocevar's avatar
 
Sam Hocevar committed
312 313 314
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next != NULL
Sam Hocevar's avatar
 
Sam Hocevar committed
315
            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
Sam Hocevar's avatar
 
Sam Hocevar committed
316
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
317
        p_title = p_title->p_next;
Sam Hocevar's avatar
 
Sam Hocevar committed
318 319
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
320
    if( p_title == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
321 322 323 324 325
    {
        /* no css key found to use, so no decryption to do */
        return 0;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
326 327 328 329
    /* Initialize loop for decryption */
    iov_base = P_IOVEC->iov_base;
    iov_len = P_IOVEC->iov_len;

Sam Hocevar's avatar
 
Sam Hocevar committed
330 331
    /* Decrypt the blocks we managed to read */
    for( i_index = i_ret; i_index; i_index-- )
Sam Hocevar's avatar
 
Sam Hocevar committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345
    {
        /* Check that iov_len is a multiple of 2048 */
        if( iov_len & 0x7ff )
        {
            return -1;
        }

        while( iov_len == 0 )
        {
            P_IOVEC++;
            iov_base = P_IOVEC->iov_base;
            iov_len = P_IOVEC->iov_len;
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
346
        CSSDescrambleSector( p_title->p_key, iov_base );
Sam Hocevar's avatar
 
Sam Hocevar committed
347 348 349 350 351 352
        ((u8*)iov_base)[0x14] &= 0x8f;

        (u8*)iov_base += DVDCSS_BLOCK_SIZE;
        (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
353
    return i_ret;
Sam Hocevar's avatar
 
Sam Hocevar committed
354 355 356 357
#undef P_IOVEC
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
358
 * dvdcss_close: close the DVD device and clean up the library
Sam Hocevar's avatar
 
Sam Hocevar committed
359 360 361
 *****************************************************************************/
extern int dvdcss_close ( dvdcss_handle dvdcss )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
362
    dvd_title_t *p_title;
Sam Hocevar's avatar
 
Sam Hocevar committed
363 364
    int i_ret;

Sam Hocevar's avatar
 
Sam Hocevar committed
365
    /* Free our list of keys */
Sam Hocevar's avatar
 
Sam Hocevar committed
366 367
    p_title = dvdcss->p_titles;
    while( p_title )
Sam Hocevar's avatar
 
Sam Hocevar committed
368
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
369 370 371
        dvd_title_t *p_tmptitle = p_title->p_next;
        free( p_title );
        p_title = p_tmptitle;
Sam Hocevar's avatar
 
Sam Hocevar committed
372 373
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
    i_ret = _dvdcss_close( dvdcss );

    if( i_ret < 0 )
    {
        return i_ret;
    }

    free( dvdcss );

    return 0;
}

/* Following functions are local */

static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
{
#if defined( WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
391
    if( WIN2K )
Sam Hocevar's avatar
 
Sam Hocevar committed
392
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406
        char psz_dvd[7];
        _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
        (HANDLE) dvdcss->i_fd =
                CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL, OPEN_EXISTING, 0, NULL );
        if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
        {
            _dvdcss_error( dvdcss, "failed opening device" );
            return -1;
        }
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
407
        dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
Sam Hocevar's avatar
 
Sam Hocevar committed
408 409 410 411 412
        if( dvdcss->i_fd == -1 )
        {
            _dvdcss_error( dvdcss, "failed opening device" );
            return -1;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
413 414
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
415 416 417 418
    /* initialise readv temporary buffer */
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
#else
    dvdcss->i_fd = open( psz_target, 0 );

    if( dvdcss->i_fd == -1 )
    {
        _dvdcss_error( dvdcss, "failed opening device" );
        return -1;
    }

#endif

    return 0;
}

static int _dvdcss_close ( dvdcss_handle dvdcss )
{
#if defined( WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
436 437 438 439 440 441
    if( WIN2K )
    {
        CloseHandle( (HANDLE) dvdcss->i_fd );
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
442
        _win32_dvdcss_aclose( dvdcss->i_fd );
Sam Hocevar's avatar
 
Sam Hocevar committed
443
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
444 445 446 447 448 449 450 451 452

    /* Free readv temporary buffer */
    if( dvdcss->p_readv_buffer )
    {
        free( dvdcss->p_readv_buffer );
        dvdcss->p_readv_buffer   = NULL;
        dvdcss->i_readv_buf_size = 0;
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
453 454
#else
    close( dvdcss->i_fd );
Gildas Bazin's avatar
 
Gildas Bazin committed
455

Sam Hocevar's avatar
 
Sam Hocevar committed
456 457 458 459 460 461 462 463
#endif

    return 0;
}

static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
{
#if defined( WIN32 )
Gildas Bazin's avatar
 
Gildas Bazin committed
464 465
    dvdcss->i_seekpos = i_blocks;

Sam Hocevar's avatar
 
Sam Hocevar committed
466 467
    if( WIN2K )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
        LARGE_INTEGER li_read;

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

        li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;

        li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
                                          li_read.LowPart,
                                          &li_read.HighPart, FILE_BEGIN );
        if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
            && GetLastError() != NO_ERROR)
        {
            li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
        }

        li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
        return (int)li_read.QuadPart;
Sam Hocevar's avatar
 
Sam Hocevar committed
487 488 489
    }
    else
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
490
        return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
Sam Hocevar's avatar
 
Sam Hocevar committed
491
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
492
#else
Gildas Bazin's avatar
 
Gildas Bazin committed
493 494
    off_t i_read;

Sam Hocevar's avatar
 
Sam Hocevar committed
495 496
    dvdcss->i_seekpos = i_blocks;

Sam Hocevar's avatar
 
Sam Hocevar committed
497 498 499
    i_read = lseek( dvdcss->i_fd,
                    (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );

Gildas Bazin's avatar
 
Gildas Bazin committed
500
    return i_read / DVDCSS_BLOCK_SIZE;
Sam Hocevar's avatar
 
Sam Hocevar committed
501 502 503 504 505 506
#endif

}

static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
507 508 509
#if defined( WIN32 ) 
    if( WIN2K )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
510 511
        int i_bytes;

Gildas Bazin's avatar
 
Gildas Bazin committed
512 513
        if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
                  i_blocks * DVDCSS_BLOCK_SIZE,
Sam Hocevar's avatar
 
Sam Hocevar committed
514
                  (LPDWORD)&i_bytes, NULL ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
515
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
516
            return -1;
Sam Hocevar's avatar
 
Sam Hocevar committed
517
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
518
        return i_bytes / DVDCSS_BLOCK_SIZE;
Sam Hocevar's avatar
 
Sam Hocevar committed
519 520
    }
    else
Sam Hocevar's avatar
 
Sam Hocevar committed
521
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
522
        return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
Sam Hocevar's avatar
 
Sam Hocevar committed
523 524
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
525 526 527
#else
    int i_bytes;

Sam Hocevar's avatar
 
Sam Hocevar committed
528 529
    i_bytes = read( dvdcss->i_fd, p_buffer,
                    (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
Sam Hocevar's avatar
 
Sam Hocevar committed
530
    return i_bytes / DVDCSS_BLOCK_SIZE;
Sam Hocevar's avatar
 
Sam Hocevar committed
531 532 533 534
#endif

}

Sam Hocevar's avatar
 
Sam Hocevar committed
535 536
static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
                           int i_blocks )
Sam Hocevar's avatar
 
Sam Hocevar committed
537
{
Gildas Bazin's avatar
 
Gildas Bazin committed
538
    int i_read;
Sam Hocevar's avatar
 
Sam Hocevar committed
539 540

#if defined( WIN32 )
Gildas Bazin's avatar
 
Gildas Bazin committed
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
    /* Check the size of the readv temp buffer, just in case we need to
     * realloc something bigger */
    if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
    {
        dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;

        if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );

        /* Allocate a buffer which will be used as a temporary storage
         * for readv */
        dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
        if( !dvdcss->p_readv_buffer )
        {
            _dvdcss_error( dvdcss, " failed (readv)" );
            return -1;
        }
    }

    i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks,
                                  dvdcss->p_readv_buffer );
Gildas Bazin's avatar
 
Gildas Bazin committed
561
    return i_read;
Gildas Bazin's avatar
 
Gildas Bazin committed
562

Sam Hocevar's avatar
 
Sam Hocevar committed
563
#else
Sam Hocevar's avatar
 
Sam Hocevar committed
564 565
    i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
    return i_read / DVDCSS_BLOCK_SIZE;
Gildas Bazin's avatar
 
Gildas Bazin committed
566

Gildas Bazin's avatar
 
Gildas Bazin committed
567
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
568 569
}

Gildas Bazin's avatar
 
Gildas Bazin committed
570

Sam Hocevar's avatar
 
Sam Hocevar committed
571 572 573
#if defined( WIN32 )

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
574 575
 * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
 *                      _win32_dvdcss_aread for win9x
Sam Hocevar's avatar
 
Sam Hocevar committed
576
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
577
static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
Gildas Bazin's avatar
 
Gildas Bazin committed
578
                                int i_num_buffers, char *p_tmp_buffer )
Sam Hocevar's avatar
 
Sam Hocevar committed
579
{
Gildas Bazin's avatar
 
Gildas Bazin committed
580 581
    int i_index;
    int i_blocks, i_blocks_total = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
582

Gildas Bazin's avatar
 
Gildas Bazin committed
583
    for( i_index = i_num_buffers; i_index; i_index-- )
Sam Hocevar's avatar
 
Sam Hocevar committed
584
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
585 586
        i_blocks_total += p_iovec[i_index-1].iov_len; 
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
587

Gildas Bazin's avatar
 
Gildas Bazin committed
588
    if( i_blocks_total <= 0 ) return 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
589

Gildas Bazin's avatar
 
Gildas Bazin committed
590
    i_blocks_total /= DVDCSS_BLOCK_SIZE;
Gildas Bazin's avatar
 
Gildas Bazin committed
591

Gildas Bazin's avatar
 
Gildas Bazin committed
592 593 594 595 596 597 598 599 600 601
    if( WIN2K )
    {
        unsigned long int i_bytes;
        if( !ReadFile( (HANDLE)i_fd, p_tmp_buffer,
                       i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
        {
            return -1;
            /* The read failed... too bad.
               As in the posix spec the file postition is left
               unspecified after a failure */
Sam Hocevar's avatar
 
Sam Hocevar committed
602
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
603
        i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
Gildas Bazin's avatar
 
Gildas Bazin committed
604 605 606
    }
    else /* Win9x */
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
607 608
        i_blocks = _win32_dvdcss_aread( i_fd, p_tmp_buffer, i_blocks_total );
        if( i_blocks < 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
609
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
610
            return -1;  /* idem */
Sam Hocevar's avatar
 
Sam Hocevar committed
611
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
612 613
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
    /* We just have to copy the content of the temp buffer into the iovecs */
    i_index = 0;
    i_blocks_total = i_blocks;
    while( i_blocks_total > 0 )
    {
        memcpy( p_iovec[i_index].iov_base,
                &p_tmp_buffer[(i_blocks - i_blocks_total) * DVDCSS_BLOCK_SIZE],
                p_iovec[i_index].iov_len );
        /* if we read less blocks than asked, we'll just end up copying
           garbage, this isn't an issue as we return the number of
           blocks actually read */
        i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
        i_index++;
    } 

    return i_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
630 631 632
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
633 634
 * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
 *                      structure)
Sam Hocevar's avatar
 
Sam Hocevar committed
635
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
636
static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
Sam Hocevar's avatar
 
Sam Hocevar committed
637 638 639 640 641 642 643
{
    HMODULE hASPI;
    DWORD dwSupportInfo;
    struct w32_aspidev *fd;
    int i, j, i_hostadapters;
    long (*lpGetSupport)( void );
    long (*lpSendCommand)( void* );
Gildas Bazin's avatar
 
Gildas Bazin committed
644
     
Sam Hocevar's avatar
 
Sam Hocevar committed
645 646 647 648 649 650 651 652 653
    hASPI = LoadLibrary( "wnaspi32.dll" );
    if( hASPI == NULL )
    {
        _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
        return -1;
    }

    (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
    (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
Gildas Bazin's avatar
 
Gildas Bazin committed
654
 
Sam Hocevar's avatar
 
Sam Hocevar committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
    if(lpGetSupport == NULL || lpSendCommand == NULL )
    {
        _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
        FreeLibrary( hASPI );
        return -1;
    }

    dwSupportInfo = lpGetSupport();

    if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
    {
        _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
        FreeLibrary( hASPI );
        return -1;
    }

    if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
    {
        _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
        FreeLibrary( hASPI );
        return -1;
    }

    i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
    if( i_hostadapters == 0 )
    {
        FreeLibrary( hASPI );
        return -1;
    }

    fd = malloc( sizeof( struct w32_aspidev ) );
    if( fd == NULL )
    {
        FreeLibrary( hASPI );
        return -1;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
692
    fd->i_blocks = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    fd->hASPI = (long) hASPI;
    fd->lpSendCommand = lpSendCommand;

    c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';

    for( i = 0; i < i_hostadapters; i++ )
    {
        for( j = 0; j < 15; j++ )
        {
            struct SRB_GetDiskInfo srbDiskInfo;

            srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
            srbDiskInfo.SRB_HaId        = i;
            srbDiskInfo.SRB_Flags       = 0;
            srbDiskInfo.SRB_Hdr_Rsvd    = 0;
            srbDiskInfo.SRB_Target      = j;
            srbDiskInfo.SRB_Lun         = 0;

            lpSendCommand( (void*) &srbDiskInfo );

Gildas Bazin's avatar
 
Gildas Bazin committed
713 714
            if( (srbDiskInfo.SRB_Status == SS_COMP) &&
                (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
Sam Hocevar's avatar
 
Sam Hocevar committed
715 716 717 718 719 720 721 722 723 724
            {
                fd->i_sid = MAKEWORD( i, j );
                return (int) fd;
            }
        }
    }

    free( (void*) fd );
    FreeLibrary( hASPI );
    _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
Gildas Bazin's avatar
 
Gildas Bazin committed
725
    return( -1 );        
Sam Hocevar's avatar
 
Sam Hocevar committed
726 727 728
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
729 730
 * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
 *                       structure)
Sam Hocevar's avatar
 
Sam Hocevar committed
731
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
732
static int _win32_dvdcss_aclose( int i_fd )
Sam Hocevar's avatar
 
Sam Hocevar committed
733 734 735 736 737 738 739 740 741 742
{
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;

    FreeLibrary( (HMODULE) fd->hASPI );
    free( (void*) i_fd );

    return 0;
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
743 744 745
 * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
 * 
 * returns the number of blocks read.
Sam Hocevar's avatar
 
Sam Hocevar committed
746
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
747
static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
Sam Hocevar's avatar
 
Sam Hocevar committed
748
{
Gildas Bazin's avatar
 
Gildas Bazin committed
749 750
    int i_old_blocks;
    char sz_buf[ DVDCSS_BLOCK_SIZE ];
Sam Hocevar's avatar
 
Sam Hocevar committed
751 752
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
    
Gildas Bazin's avatar
 
Gildas Bazin committed
753 754
    i_old_blocks = fd->i_blocks;
    fd->i_blocks = i_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
755

Gildas Bazin's avatar
 
Gildas Bazin committed
756
    if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
Sam Hocevar's avatar
 
Sam Hocevar committed
757
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
758
        fd->i_blocks = i_old_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
759 760 761
        return -1;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
762
    (fd->i_blocks)--;
Sam Hocevar's avatar
 
Sam Hocevar committed
763

Gildas Bazin's avatar
 
Gildas Bazin committed
764
    return fd->i_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
765 766 767
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
768 769 770
 * _win32_dvdcss_aread: aspi version of _dvdcss_read
 *
 * returns the number of blocks read.
Sam Hocevar's avatar
 
Sam Hocevar committed
771
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
772
static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
Sam Hocevar's avatar
 
Sam Hocevar committed
773 774 775 776 777
{
    HANDLE hEvent;
    struct SRB_ExecSCSICmd ssc;
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;

Gildas Bazin's avatar
 
Gildas Bazin committed
778
    /* Create the transfer completion event */
Sam Hocevar's avatar
 
Sam Hocevar committed
779 780 781 782 783 784
    hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    if( hEvent == NULL )
    {
        return -1;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
785 786
    memset( &ssc, 0, sizeof( ssc ) );

Sam Hocevar's avatar
 
Sam Hocevar committed
787 788 789 790 791
    ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
    ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
    ssc.SRB_HaId        = LOBYTE( fd->i_sid );
    ssc.SRB_Target      = HIBYTE( fd->i_sid );
    ssc.SRB_SenseLen    = SENSE_LEN;
Gildas Bazin's avatar
 
Gildas Bazin committed
792 793
    
    ssc.SRB_PostProc = (LPVOID) hEvent;
Gildas Bazin's avatar
 
Gildas Bazin committed
794
    ssc.SRB_BufPointer  = p_data;
Sam Hocevar's avatar
 
Sam Hocevar committed
795
    ssc.SRB_CDBLen      = 12;
Gildas Bazin's avatar
 
Gildas Bazin committed
796
    
Sam Hocevar's avatar
 
Sam Hocevar committed
797
    ssc.CDBByte[0]      = 0xA8; /* RAW */
Gildas Bazin's avatar
 
Gildas Bazin committed
798 799 800 801 802 803 804
    ssc.CDBByte[2]      = (UCHAR) (fd->i_blocks >> 24);
    ssc.CDBByte[3]      = (UCHAR) (fd->i_blocks >> 16) & 0xff;
    ssc.CDBByte[4]      = (UCHAR) (fd->i_blocks >> 8) & 0xff;
    ssc.CDBByte[5]      = (UCHAR) (fd->i_blocks) & 0xff;
    
    /* We have to break down the reads into 64kb pieces (ASPI restriction) */
    if( i_blocks > 32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
805
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
806 807 808 809 810 811 812 813 814 815
        ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
        ssc.CDBByte[9] = 32;
        fd->i_blocks  += 32;

        /* Initiate transfer */  
        ResetEvent( hEvent );
        fd->lpSendCommand( (void*) &ssc );

        /* transfer the next 64kb (_win32_dvdcss_aread is called recursively)
         * We need to check the status of the read on return */
Sam Hocevar's avatar
 
Sam Hocevar committed
816
        if( _win32_dvdcss_aread( i_fd, (u8*) p_data + 32 * DVDCSS_BLOCK_SIZE,
Gildas Bazin's avatar
 
Gildas Bazin committed
817 818 819 820
                                 i_blocks - 32) < 0 )
        {
            return -1;
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
821
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
822 823 824 825 826 827
    else
    {
        /* This is the last transfer */
        ssc.SRB_BufLen   = i_blocks * DVDCSS_BLOCK_SIZE;
        ssc.CDBByte[9]   = (UCHAR) i_blocks;
        fd->i_blocks += i_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
828

Gildas Bazin's avatar
 
Gildas Bazin committed
829 830 831 832 833 834 835 836 837 838 839
        /* Initiate transfer */  
        ResetEvent( hEvent );
        fd->lpSendCommand( (void*) &ssc );

    }

    /* If the command has still not been processed, wait until it's finished */
    if( ssc.SRB_Status == SS_PENDING )
    {
        WaitForSingleObject( hEvent, INFINITE );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
840 841
    CloseHandle( hEvent );

Gildas Bazin's avatar
 
Gildas Bazin committed
842
    /* check that the transfer went as planned */
Sam Hocevar's avatar
 
Sam Hocevar committed
843
    if( ssc.SRB_Status != SS_COMP )
Sam Hocevar's avatar
 
Sam Hocevar committed
844
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
845
      return -1;
Sam Hocevar's avatar
 
Sam Hocevar committed
846 847
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
848
    return i_blocks;
Sam Hocevar's avatar
 
Sam Hocevar committed
849 850 851
}

#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
852