]> git.sesse.net Git - vlc/blob - extras/libdvdcss/libdvdcss.c
* Fix in libdvdcss in the key caching code.
[vlc] / extras / libdvdcss / libdvdcss.c
1 /*****************************************************************************
2  * libdvdcss.c: DVD reading library.
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  * $Id: libdvdcss.c,v 1.11 2001/07/28 02:17:37 sam Exp $
6  *
7  * Authors: Stéphane Borel <stef@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
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 "defs.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #endif
39
40 #if defined( WIN32 )
41 #   include <io.h>
42 #   include "input_iovec.h"
43 #else
44 #   include <sys/uio.h>                                      /* struct iovec */
45 #endif
46
47 #include "config.h"
48 #include "common.h"
49
50 #include "videolan/dvdcss.h"
51 #include "libdvdcss.h"
52 #include "ioctl.h"
53
54 /*****************************************************************************
55  * Local prototypes
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 );
62
63 /*****************************************************************************
64  * Local prototypes, win32 specific
65  *****************************************************************************/
66 #if defined( WIN32 )
67 static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
68                                   int i_num_buffers, char *p_tmp_buffer );
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 );
73 #endif
74
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 )
79 {
80     int i_ret;
81
82     dvdcss_handle dvdcss;
83
84     /* Allocate the library structure */
85     dvdcss = malloc( sizeof( struct dvdcss_s ) );
86     if( dvdcss == NULL )
87     {
88         if( ! (i_flags & DVDCSS_INIT_QUIET) )
89         {
90             DVDCSS_ERROR( "could not initialize library" );
91         }
92
93         return NULL;
94     }
95
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";
101
102     i_ret = _dvdcss_open( dvdcss, psz_target );
103     if( i_ret < 0 )
104     {
105         free( dvdcss );
106         return NULL;
107     }
108
109     i_ret = CSSTest( dvdcss );
110     if( i_ret < 0 )
111     {
112         _dvdcss_error( dvdcss, "css test failed" );
113         _dvdcss_close( dvdcss );
114         free( dvdcss );
115         return NULL;
116     }
117
118     dvdcss->b_encrypted = i_ret;
119
120     /* If drive is encrypted, crack its key */
121     if( dvdcss->b_encrypted )
122     {
123         i_ret = CSSInit( dvdcss );
124
125         if( i_ret < 0 )
126         {
127             _dvdcss_close( dvdcss );
128             free( dvdcss );
129             return NULL;
130         }
131     }
132
133     return dvdcss;
134 }
135
136 /*****************************************************************************
137  * dvdcss_error: return the last libdvdcss error message
138  *****************************************************************************/
139 extern char * dvdcss_error ( dvdcss_handle dvdcss )
140 {
141     return dvdcss->psz_error;
142 }
143
144 /*****************************************************************************
145  * dvdcss_seek: seek into the device
146  *****************************************************************************/
147 extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
148 {
149     return _dvdcss_seek( dvdcss, i_blocks );
150 }
151
152 /*****************************************************************************
153  * dvdcss_title: crack the current title key if needed
154  *****************************************************************************/
155 extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
156 {
157     dvd_title_t *p_title;
158     dvd_key_t p_key;
159     int i_ret;
160
161     if( ! dvdcss->b_encrypted )
162     {
163         return 0;
164     }
165
166     //fprintf( stderr, "looking for a key for offset %i\n", i_block );
167
168     /* Check if we've already cracked this key */
169     p_title = dvdcss->p_titles;
170     while( p_title != NULL
171             && p_title->p_next != NULL
172             && p_title->p_next->i_startlb <= i_block )
173     {
174         p_title = p_title->p_next;
175     }
176
177     if( p_title != NULL
178          && p_title->i_startlb == i_block )
179     {
180         /* We've already cracked this key, nothing to do */
181         return 0;
182     }
183
184     /* Crack CSS title key for current VTS */
185     i_ret = CSSGetKey( dvdcss, i_block, p_key );
186
187     if( i_ret < 0 )
188     {
189         _dvdcss_error( dvdcss, "fatal error in vts css key" );
190         return i_ret;
191     }
192     else if( i_ret > 0 )
193     {
194         _dvdcss_error( dvdcss, "decryption unavailable" );
195         return -1;
196     }
197
198     //fprintf( stderr, "cracked key is %.2x %.2x %.2x %.2x %.2x\n",
199     //         p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] );
200
201     /* Add key to keytable if it isn't empty */
202     if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] )
203     {
204         dvd_title_t *p_newtitle;
205
206         /* Find our spot in the list */
207         p_newtitle = NULL;
208         p_title = dvdcss->p_titles;
209         while( p_title != NULL
210                 && p_title->i_startlb < i_block )
211         {
212             p_newtitle = p_title;
213             p_title = p_title->p_next;
214         }
215
216         /* Save the found title */
217         p_title = p_newtitle;
218
219         /* Write in the new title and its key */
220         p_newtitle = malloc( sizeof( dvd_title_t ) );
221         p_newtitle->i_startlb = i_block;
222         memcpy( p_newtitle->p_key, p_key, KEY_SIZE );
223
224         /* Link the new title, either at the beginning or inside the list */
225         if( p_title == NULL )
226         {
227             dvdcss->p_titles = p_newtitle;
228             p_newtitle->p_next = NULL;
229         }
230         else
231         {
232             p_newtitle->p_next = p_title->p_next;
233             p_title->p_next = p_newtitle;
234         }
235     }
236
237     return 0;
238 }
239
240 /*****************************************************************************
241  * dvdcss_read: read data from the device, decrypt if requested
242  *****************************************************************************/
243 extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
244                                                int i_blocks,
245                                                int i_flags )
246 {
247     dvd_title_t *p_title;
248     int i_ret, i_index;
249
250     i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
251
252     if( i_ret <= 0
253          || !dvdcss->b_encrypted
254          || !(i_flags & DVDCSS_READ_DECRYPT) )
255     {
256         return i_ret;
257     }
258
259     /* find our key */
260     p_title = dvdcss->p_titles;
261     while( p_title != NULL
262             && p_title->p_next
263             && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
264     {
265         p_title = p_title->p_next;
266     }
267
268     if( p_title == NULL )
269     {
270         /* no css key found to use, so no decryption to do */
271         return 0;
272     }
273
274     /* Decrypt the blocks we managed to read */
275     for( i_index = i_ret; i_index; i_index-- )
276     {
277         CSSDescrambleSector( p_title->p_key, p_buffer );
278         ((u8*)p_buffer)[0x14] &= 0x8f;
279         (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
280     }
281
282     return i_ret;
283 }
284
285 /*****************************************************************************
286  * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
287  *****************************************************************************/
288 extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
289                                                 int i_blocks,
290                                                 int i_flags )
291 {
292 #define P_IOVEC ((struct iovec*)p_iovec)
293     dvd_title_t *p_title;
294     int i_ret, i_index;
295     void *iov_base;
296     size_t iov_len;
297
298     i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );
299
300     if( i_ret <= 0
301          || !dvdcss->b_encrypted
302          || !(i_flags & DVDCSS_READ_DECRYPT) )
303     {
304         return i_ret;
305     }
306
307     /* Find our key */
308     p_title = dvdcss->p_titles;
309     while( p_title != NULL
310             && p_title->p_next != NULL
311             && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
312     {
313         p_title = p_title->p_next;
314     }
315
316     if( p_title == NULL )
317     {
318         /* no css key found to use, so no decryption to do */
319         return 0;
320     }
321
322     /* Initialize loop for decryption */
323     iov_base = P_IOVEC->iov_base;
324     iov_len = P_IOVEC->iov_len;
325
326     /* Decrypt the blocks we managed to read */
327     for( i_index = i_ret; i_index; i_index-- )
328     {
329         /* Check that iov_len is a multiple of 2048 */
330         if( iov_len & 0x7ff )
331         {
332             return -1;
333         }
334
335         while( iov_len == 0 )
336         {
337             P_IOVEC++;
338             iov_base = P_IOVEC->iov_base;
339             iov_len = P_IOVEC->iov_len;
340         }
341
342         CSSDescrambleSector( p_title->p_key, iov_base );
343         ((u8*)iov_base)[0x14] &= 0x8f;
344
345         (u8*)iov_base += DVDCSS_BLOCK_SIZE;
346         (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
347     }
348
349     return i_ret;
350 #undef P_IOVEC
351 }
352
353 /*****************************************************************************
354  * dvdcss_close: close the DVD device and clean up the library
355  *****************************************************************************/
356 extern int dvdcss_close ( dvdcss_handle dvdcss )
357 {
358     dvd_title_t *p_title;
359     int i_ret;
360
361     /* Free our list of keys */
362     p_title = dvdcss->p_titles;
363     while( p_title )
364     {
365         dvd_title_t *p_tmptitle = p_title->p_next;
366         free( p_title );
367         p_title = p_tmptitle;
368     }
369
370     i_ret = _dvdcss_close( dvdcss );
371
372     if( i_ret < 0 )
373     {
374         return i_ret;
375     }
376
377     free( dvdcss );
378
379     return 0;
380 }
381
382 /* Following functions are local */
383
384 static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
385 {
386 #if defined( WIN32 )
387     if( WIN2K )
388     {
389         char psz_dvd[7];
390         _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
391         (HANDLE) dvdcss->i_fd =
392                 CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
393                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
394                                 NULL, OPEN_EXISTING, 0, NULL );
395         if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
396         {
397             _dvdcss_error( dvdcss, "failed opening device" );
398             return -1;
399         }
400     }
401     else
402     {
403         dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
404         if( dvdcss->i_fd == -1 )
405         {
406             _dvdcss_error( dvdcss, "failed opening device" );
407             return -1;
408         }
409     }
410
411     /* initialise readv temporary buffer */
412     dvdcss->p_readv_buffer   = NULL;
413     dvdcss->i_readv_buf_size = 0;
414
415 #else
416     dvdcss->i_fd = open( psz_target, 0 );
417
418     if( dvdcss->i_fd == -1 )
419     {
420         _dvdcss_error( dvdcss, "failed opening device" );
421         return -1;
422     }
423
424 #endif
425
426     return 0;
427 }
428
429 static int _dvdcss_close ( dvdcss_handle dvdcss )
430 {
431 #if defined( WIN32 )
432     if( WIN2K )
433     {
434         CloseHandle( (HANDLE) dvdcss->i_fd );
435     }
436     else
437     {
438         _win32_dvdcss_aclose( dvdcss->i_fd );
439     }
440
441     /* Free readv temporary buffer */
442     if( dvdcss->p_readv_buffer )
443     {
444         free( dvdcss->p_readv_buffer );
445         dvdcss->p_readv_buffer   = NULL;
446         dvdcss->i_readv_buf_size = 0;
447     }
448
449 #else
450     close( dvdcss->i_fd );
451
452 #endif
453
454     return 0;
455 }
456
457 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
458 {
459 #if defined( WIN32 )
460     if( WIN2K )
461     {
462         LARGE_INTEGER li_read;
463
464 #ifndef INVALID_SET_FILE_POINTER
465 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
466 #endif
467
468         li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
469
470         li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
471                                           li_read.LowPart,
472                                           &li_read.HighPart, FILE_BEGIN );
473         if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
474             && GetLastError() != NO_ERROR)
475         {
476             li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
477         }
478
479         li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
480         return (int)li_read.QuadPart;
481     }
482     else
483     {
484         return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
485     }
486 #else
487     off_t i_read;
488
489     dvdcss->i_seekpos = i_blocks;
490
491     i_read = lseek( dvdcss->i_fd,
492                     (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
493
494     return i_read / DVDCSS_BLOCK_SIZE;
495 #endif
496
497 }
498
499 static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
500 {
501 #if defined( WIN32 ) 
502     if( WIN2K )
503     {
504         int i_bytes;
505
506         if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
507                   i_blocks * DVDCSS_BLOCK_SIZE,
508                   (LPDWORD)&i_bytes, NULL ) )
509         {
510             return -1;
511         }
512         return i_bytes / DVDCSS_BLOCK_SIZE;
513     }
514     else
515     {
516         return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
517     }
518
519 #else
520     int i_bytes;
521
522     i_bytes = read( dvdcss->i_fd, p_buffer,
523                     (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
524     return i_bytes / DVDCSS_BLOCK_SIZE;
525 #endif
526
527 }
528
529 static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
530                            int i_blocks )
531 {
532     int i_read;
533
534 #if defined( WIN32 )
535     /* Check the size of the readv temp buffer, just in case we need to
536      * realloc something bigger */
537     if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
538     {
539         dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;
540
541         if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );
542
543         /* Allocate a buffer which will be used as a temporary storage
544          * for readv */
545         dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
546         if( !dvdcss->p_readv_buffer )
547         {
548             _dvdcss_error( dvdcss, " failed (readv)" );
549             return -1;
550         }
551     }
552
553     i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks,
554                                   dvdcss->p_readv_buffer );
555     return i_read;
556
557 #else
558     i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
559     return i_read / DVDCSS_BLOCK_SIZE;
560
561 #endif
562 }
563
564
565 #if defined( WIN32 )
566
567 /*****************************************************************************
568  * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
569  *                      _win32_dvdcss_aread for win9x
570  *****************************************************************************/
571 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
572                                 int i_num_buffers, char *p_tmp_buffer )
573 {
574     int i_index;
575     int i_blocks, i_blocks_total = 0;
576
577     for( i_index = i_num_buffers; i_index; i_index-- )
578     {
579         i_blocks_total += p_iovec[i_index-1].iov_len; 
580     }
581
582     if( i_blocks_total <= 0 ) return 0;
583
584     i_blocks_total /= DVDCSS_BLOCK_SIZE;
585
586     if( WIN2K )
587     {
588         unsigned long int i_bytes;
589         if( !ReadFile( (HANDLE)i_fd, p_tmp_buffer,
590                        i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
591         {
592             return -1;
593             /* The read failed... too bad.
594                As in the posix spec the file postition is left
595                unspecified after a failure */
596         }
597         i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
598     }
599     else /* Win9x */
600     {
601         i_blocks = _win32_dvdcss_aread( i_fd, p_tmp_buffer, i_blocks_total );
602         if( i_blocks < 0 )
603         {
604             return -1;  /* idem */
605         }
606     }
607
608     /* We just have to copy the content of the temp buffer into the iovecs */
609     i_index = 0;
610     i_blocks_total = i_blocks;
611     while( i_blocks_total > 0 )
612     {
613         memcpy( p_iovec[i_index].iov_base,
614                 &p_tmp_buffer[(i_blocks - i_blocks_total) * DVDCSS_BLOCK_SIZE],
615                 p_iovec[i_index].iov_len );
616         /* if we read less blocks than asked, we'll just end up copying
617            garbage, this isn't an issue as we return the number of
618            blocks actually read */
619         i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
620         i_index++;
621     } 
622
623     return i_blocks;
624 }
625
626 /*****************************************************************************
627  * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
628  *                      structure)
629  *****************************************************************************/
630 static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
631 {
632     HMODULE hASPI;
633     DWORD dwSupportInfo;
634     struct w32_aspidev *fd;
635     int i, j, i_hostadapters;
636     long (*lpGetSupport)( void );
637     long (*lpSendCommand)( void* );
638      
639     hASPI = LoadLibrary( "wnaspi32.dll" );
640     if( hASPI == NULL )
641     {
642         _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
643         return -1;
644     }
645
646     (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
647     (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
648  
649     if(lpGetSupport == NULL || lpSendCommand == NULL )
650     {
651         _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
652         FreeLibrary( hASPI );
653         return -1;
654     }
655
656     dwSupportInfo = lpGetSupport();
657
658     if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
659     {
660         _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
661         FreeLibrary( hASPI );
662         return -1;
663     }
664
665     if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
666     {
667         _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
668         FreeLibrary( hASPI );
669         return -1;
670     }
671
672     i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
673     if( i_hostadapters == 0 )
674     {
675         FreeLibrary( hASPI );
676         return -1;
677     }
678
679     fd = malloc( sizeof( struct w32_aspidev ) );
680     if( fd == NULL )
681     {
682         FreeLibrary( hASPI );
683         return -1;
684     }
685
686     fd->i_blocks = 0;
687     fd->hASPI = (long) hASPI;
688     fd->lpSendCommand = lpSendCommand;
689
690     c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
691
692     for( i = 0; i < i_hostadapters; i++ )
693     {
694         for( j = 0; j < 15; j++ )
695         {
696             struct SRB_GetDiskInfo srbDiskInfo;
697
698             srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
699             srbDiskInfo.SRB_HaId        = i;
700             srbDiskInfo.SRB_Flags       = 0;
701             srbDiskInfo.SRB_Hdr_Rsvd    = 0;
702             srbDiskInfo.SRB_Target      = j;
703             srbDiskInfo.SRB_Lun         = 0;
704
705             lpSendCommand( (void*) &srbDiskInfo );
706
707             if( (srbDiskInfo.SRB_Status == SS_COMP) &&
708                 (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
709             {
710                 fd->i_sid = MAKEWORD( i, j );
711                 return (int) fd;
712             }
713         }
714     }
715
716     free( (void*) fd );
717     FreeLibrary( hASPI );
718     _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
719     return( -1 );        
720 }
721
722 /*****************************************************************************
723  * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
724  *                       structure)
725  *****************************************************************************/
726 static int _win32_dvdcss_aclose( int i_fd )
727 {
728     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
729
730     FreeLibrary( (HMODULE) fd->hASPI );
731     free( (void*) i_fd );
732
733     return 0;
734 }
735
736 /*****************************************************************************
737  * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
738  * 
739  * returns the number of blocks read.
740  *****************************************************************************/
741 static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
742 {
743     int i_old_blocks;
744     char sz_buf[ DVDCSS_BLOCK_SIZE ];
745     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
746     
747     i_old_blocks = fd->i_blocks;
748     fd->i_blocks = i_blocks;
749
750     if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
751     {
752         fd->i_blocks = i_old_blocks;
753         return -1;
754     }
755
756     (fd->i_blocks)--;
757
758     return fd->i_blocks;
759 }
760
761 /*****************************************************************************
762  * _win32_dvdcss_aread: aspi version of _dvdcss_read
763  *
764  * returns the number of blocks read.
765  *****************************************************************************/
766 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
767 {
768     HANDLE hEvent;
769     struct SRB_ExecSCSICmd ssc;
770     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
771
772     /* Create the transfer completion event */
773     hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
774     if( hEvent == NULL )
775     {
776         return -1;
777     }
778
779     memset( &ssc, 0, sizeof( ssc ) );
780
781     ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
782     ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
783     ssc.SRB_HaId        = LOBYTE( fd->i_sid );
784     ssc.SRB_Target      = HIBYTE( fd->i_sid );
785     ssc.SRB_SenseLen    = SENSE_LEN;
786     
787     ssc.SRB_PostProc = (LPVOID) hEvent;
788     ssc.SRB_BufPointer  = p_data;
789     ssc.SRB_CDBLen      = 12;
790     
791     ssc.CDBByte[0]      = 0xA8; /* RAW */
792     ssc.CDBByte[2]      = (UCHAR) (fd->i_blocks >> 24);
793     ssc.CDBByte[3]      = (UCHAR) (fd->i_blocks >> 16) & 0xff;
794     ssc.CDBByte[4]      = (UCHAR) (fd->i_blocks >> 8) & 0xff;
795     ssc.CDBByte[5]      = (UCHAR) (fd->i_blocks) & 0xff;
796     
797     /* We have to break down the reads into 64kb pieces (ASPI restriction) */
798     if( i_blocks > 32 )
799     {
800         ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
801         ssc.CDBByte[9] = 32;
802         fd->i_blocks  += 32;
803
804         /* Initiate transfer */  
805         ResetEvent( hEvent );
806         fd->lpSendCommand( (void*) &ssc );
807
808         /* transfer the next 64kb (_win32_dvdcss_aread is called recursively)
809          * We need to check the status of the read on return */
810         if( _win32_dvdcss_aread( i_fd, p_data + 32 * DVDCSS_BLOCK_SIZE,
811                                  i_blocks - 32) < 0 )
812         {
813             return -1;
814         }
815     }
816     else
817     {
818         /* This is the last transfer */
819         ssc.SRB_BufLen   = i_blocks * DVDCSS_BLOCK_SIZE;
820         ssc.CDBByte[9]   = (UCHAR) i_blocks;
821         fd->i_blocks += i_blocks;
822
823         /* Initiate transfer */  
824         ResetEvent( hEvent );
825         fd->lpSendCommand( (void*) &ssc );
826
827     }
828
829     /* If the command has still not been processed, wait until it's finished */
830     if( ssc.SRB_Status == SS_PENDING )
831     {
832         WaitForSingleObject( hEvent, INFINITE );
833     }
834     CloseHandle( hEvent );
835
836     /* check that the transfer went as planned */
837     if( ssc.SRB_Status != SS_COMP )
838     {
839       return -1;
840     }
841
842     return i_blocks;
843 }
844
845 #endif
846