]> git.sesse.net Git - vlc/blob - extras/libdvdcss/libdvdcss.c
e9b8f19f6f4ad4420fffff64ccd2fc376caffbe2
[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.8 2001/07/25 00:23:40 sam Exp $
6  *
7  * Authors: Stéphane Borel <stef@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #endif
39
40 #if defined( WIN32 )
41 #   include <io.h>
42 #   include "input_iovec.h"
43 #else
44 #   include <sys/uio.h>                                      /* struct iovec */
45 #endif
46
47 #include "config.h"
48 #include "common.h"
49
50 #include "videolan/dvdcss.h"
51 #include "libdvdcss.h"
52 #include "ioctl.h"
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static int _dvdcss_open  ( dvdcss_handle, char *psz_target );
58 static int _dvdcss_close ( dvdcss_handle );
59 static int _dvdcss_seek  ( dvdcss_handle, int i_blocks );
60 static int _dvdcss_read  ( dvdcss_handle, void *p_buffer, int i_blocks );
61 static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
62
63 /*****************************************************************************
64  * Local prototypes, win32 specific
65  *****************************************************************************/
66 #if defined( WIN32 )
67 static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
68                                   int i_num_buffers );
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 #else
407     dvdcss->i_fd = open( psz_target, 0 );
408
409     if( dvdcss->i_fd == -1 )
410     {
411         _dvdcss_error( dvdcss, "failed opening device" );
412         return -1;
413     }
414
415 #endif
416
417     return 0;
418 }
419
420 static int _dvdcss_close ( dvdcss_handle dvdcss )
421 {
422 #if defined( WIN32 )
423     if( WIN2K )
424     {
425         CloseHandle( (HANDLE) dvdcss->i_fd );
426     }
427     else
428     {
429         _win32_dvdcss_aclose( dvdcss->i_fd );
430     }
431 #else
432     close( dvdcss->i_fd );
433 #endif
434
435     return 0;
436 }
437
438 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
439 {
440 #if defined( WIN32 )
441     if( WIN2K )
442     {
443         LARGE_INTEGER li_read;
444
445 #ifndef INVALID_SET_FILE_POINTER
446 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
447 #endif
448
449         li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
450
451         li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
452                                           li_read.LowPart,
453                                           &li_read.HighPart, FILE_BEGIN );
454         if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
455             && GetLastError() != NO_ERROR)
456         {
457             li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
458         }
459
460         li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
461         return (int)li_read.QuadPart;
462     }
463     else
464     {
465         return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
466     }
467 #else
468     off_t i_read;
469
470     dvdcss->i_seekpos = i_blocks;
471
472     i_read = lseek( dvdcss->i_fd,
473                     (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
474
475     return i_read / DVDCSS_BLOCK_SIZE;
476 #endif
477
478 }
479
480 static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
481 {
482 #if defined( WIN32 ) 
483     if( WIN2K )
484     {
485         int i_bytes;
486
487         if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
488                   i_blocks * DVDCSS_BLOCK_SIZE,
489                   (LPDWORD)&i_bytes, NULL ) )
490         {
491             return -1;
492         }
493         return i_bytes / DVDCSS_BLOCK_SIZE;
494     }
495     else
496     {
497         return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
498     }
499
500 #else
501     int i_bytes;
502
503     i_bytes = read( dvdcss->i_fd, p_buffer,
504                     (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
505     return i_bytes / DVDCSS_BLOCK_SIZE;
506 #endif
507
508 }
509
510 static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
511                            int i_blocks )
512 {
513     int i_read;
514
515 #if defined( WIN32 )
516     i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks );
517     return i_read;
518 #else
519     i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
520     return i_read / DVDCSS_BLOCK_SIZE;
521 #endif
522 }
523
524 #if defined( WIN32 )
525
526 /*****************************************************************************
527  * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
528  *                      _win32_dvdcss_aread for win9x
529  *****************************************************************************/
530 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
531                                 int i_num_buffers )
532 {
533     int i_index, i_len, i_total = 0;
534     unsigned char *p_base;
535     int i_blocks;
536
537     if( WIN2K )
538     {
539         for( i_index = i_num_buffers; i_index; i_index-- )
540         {
541             i_len  = p_iovec->iov_len;
542             p_base = p_iovec->iov_base;
543
544             if( i_len > 0 )
545             {
546                 unsigned long int i_bytes;
547                 if( !ReadFile( (HANDLE) i_fd, p_base, i_len, &i_bytes, NULL ) )
548                 {
549                     return -1;
550                     /* One of the reads failed, too bad.
551                        We won't even bother returning the reads that went well,
552                        and like in the posix spec the file postition is left
553                        unspecified after a failure */
554                 }
555                 i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
556
557                 i_total += i_blocks;
558
559                 if( i_blocks != (i_len / DVDCSS_BLOCK_SIZE) )
560                 {
561                     /* we reached the end of the file */
562                     return i_total;
563                 }
564
565             }
566
567             p_iovec++;
568         }
569     }
570     else /* Win9x */
571     {
572         for( i_index = i_num_buffers; i_index; i_index-- )
573         {
574             i_len  = p_iovec->iov_len / DVDCSS_BLOCK_SIZE;
575             p_base = p_iovec->iov_base;
576
577             if( i_len > 0 )
578             {
579                 i_blocks = _win32_dvdcss_aread( i_fd, p_base, i_len );
580                 if( i_blocks < 0 )
581                 {
582                     return -1;  /* idem */
583                 }
584
585                 i_total += i_blocks;
586
587                 if( i_blocks != i_len )
588                 {
589                     /* we reached the end of the file or a signal interrupted
590                        the read */
591                     return i_total;
592                 }
593             }
594
595             p_iovec++;
596         }
597     }
598
599     return i_total;
600 }
601
602 /*****************************************************************************
603  * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
604  *                      structure)
605  *****************************************************************************/
606 static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
607 {
608     HMODULE hASPI;
609     DWORD dwSupportInfo;
610     struct w32_aspidev *fd;
611     int i, j, i_hostadapters;
612     long (*lpGetSupport)( void );
613     long (*lpSendCommand)( void* );
614     
615     hASPI = LoadLibrary( "wnaspi32.dll" );
616     if( hASPI == NULL )
617     {
618         _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
619         return -1;
620     }
621
622     (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
623     (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
624     
625     if(lpGetSupport == NULL || lpSendCommand == NULL )
626     {
627         _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
628         FreeLibrary( hASPI );
629         return -1;
630     }
631
632     dwSupportInfo = lpGetSupport();
633
634     if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
635     {
636         _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
637         FreeLibrary( hASPI );
638         return -1;
639     }
640
641     if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
642     {
643         _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
644         FreeLibrary( hASPI );
645         return -1;
646     }
647
648     i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
649     if( i_hostadapters == 0 )
650     {
651         FreeLibrary( hASPI );
652         return -1;
653     }
654
655     fd = malloc( sizeof( struct w32_aspidev ) );
656     if( fd == NULL )
657     {
658         FreeLibrary( hASPI );
659         return -1;
660     }
661
662     fd->i_blocks = 0;
663     fd->hASPI = (long) hASPI;
664     fd->lpSendCommand = lpSendCommand;
665
666     c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
667
668     for( i = 0; i < i_hostadapters; i++ )
669     {
670         for( j = 0; j < 15; j++ )
671         {
672             struct SRB_GetDiskInfo srbDiskInfo;
673
674             srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
675             srbDiskInfo.SRB_HaId        = i;
676             srbDiskInfo.SRB_Flags       = 0;
677             srbDiskInfo.SRB_Hdr_Rsvd    = 0;
678             srbDiskInfo.SRB_Target      = j;
679             srbDiskInfo.SRB_Lun         = 0;
680
681             lpSendCommand( (void*) &srbDiskInfo );
682
683             if( srbDiskInfo.SRB_Status == SS_COMP &&
684                 srbDiskInfo.SRB_Int13HDriveInfo == c_drive )
685             {
686                 fd->i_sid = MAKEWORD( i, j );
687                 return (int) fd;
688             }
689         }
690     }
691
692     free( (void*) fd );
693     FreeLibrary( hASPI );
694     _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
695
696     return( -1 );
697 }
698
699 /*****************************************************************************
700  * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
701  *                       structure)
702  *****************************************************************************/
703 static int _win32_dvdcss_aclose( int i_fd )
704 {
705     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
706
707     FreeLibrary( (HMODULE) fd->hASPI );
708     free( (void*) i_fd );
709
710     return 0;
711 }
712
713 /*****************************************************************************
714  * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
715  * 
716  * returns the number of blocks read.
717  *****************************************************************************/
718 static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
719 {
720     int i_old_blocks;
721     char sz_buf[ DVDCSS_BLOCK_SIZE ];
722     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
723     
724     i_old_blocks = fd->i_blocks;
725     fd->i_blocks = i_blocks;
726
727     if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
728     {
729         fd->i_blocks = i_old_blocks;
730         return -1;
731     }
732
733     (fd->i_blocks)--;
734
735     return fd->i_blocks;
736 }
737
738 /*****************************************************************************
739  * _win32_dvdcss_aread: aspi version of _dvdcss_read
740  *
741  * returns the number of blocks read.
742  *****************************************************************************/
743 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
744 {
745     HANDLE hEvent;
746     DWORD dwStart, dwLen;
747     struct SRB_ExecSCSICmd ssc;
748     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
749
750     memset( &ssc, 0, sizeof( ssc ) );
751
752     dwStart = fd->i_blocks;
753     dwLen = i_blocks;
754
755     hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
756     if( hEvent == NULL )
757     {
758         return -1;
759     }
760
761     ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
762     ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
763     ssc.SRB_HaId        = LOBYTE( fd->i_sid );
764     ssc.SRB_Target      = HIBYTE( fd->i_sid );
765     ssc.SRB_SenseLen    = SENSE_LEN;
766     ssc.SRB_PostProc    = (LPVOID) hEvent;
767
768     ssc.SRB_BufLen      = dwLen * DVDCSS_BLOCK_SIZE;
769     ssc.SRB_BufPointer  = p_data;
770     ssc.SRB_CDBLen      = 12;
771
772     ssc.CDBByte[0]      = 0xA8; /* RAW */
773     ssc.CDBByte[2]      = (UCHAR) dwStart >> 24;
774     ssc.CDBByte[3]      = (UCHAR) (dwStart >> 16) & 0xff;
775     ssc.CDBByte[4]      = (UCHAR) (dwStart >> 8) & 0xff;
776     ssc.CDBByte[5]      = (UCHAR) (dwStart) & 0xff;
777     ssc.CDBByte[6]      = (UCHAR) dwLen >> 24;
778     ssc.CDBByte[7]      = (UCHAR) (dwLen >> 16) & 0xff;
779     ssc.CDBByte[8]      = (UCHAR) (dwLen >> 8) & 0xff;
780     ssc.CDBByte[9]      = (UCHAR) (dwLen) & 0xff;
781
782     ResetEvent( hEvent );
783     if( fd->lpSendCommand( (void*) &ssc ) == SS_PENDING )
784     {
785         WaitForSingleObject( hEvent, INFINITE );
786     }
787
788     CloseHandle( hEvent );
789
790     if( ssc.SRB_Status != SS_COMP )
791     {
792         return -1;
793     }
794         
795     fd->i_blocks += i_blocks;
796
797     return i_blocks;
798 }
799
800 #endif
801