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