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