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