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