]> git.sesse.net Git - vlc/blob - extras/libdvdcss/libdvdcss.c
- Fixed 2 Win32 bugs in libdvdcss (in _win32_dvdcss_readv), one of them
[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.6 2001/07/12 23:06:54 gbazin Exp $
6  *
7  * Authors: Stéphane Borel <stef@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #endif
39
40 #if defined( WIN32 )
41 #   include <io.h>
42 #   include "input_iovec.h"
43 #else
44 #   include <sys/uio.h>                                      /* struct iovec */
45 #endif
46
47 #include "config.h"
48 #include "common.h"
49
50 #include "videolan/dvdcss.h"
51 #include "libdvdcss.h"
52 #include "ioctl.h"
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static int _dvdcss_open  ( dvdcss_handle, char *psz_target );
58 static int _dvdcss_close ( dvdcss_handle );
59 static int _dvdcss_seek  ( dvdcss_handle, int i_blocks );
60 static int _dvdcss_read  ( dvdcss_handle, void *p_buffer, int i_blocks );
61 static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );
62
63 /*****************************************************************************
64  * Local prototypes, win32 specific
65  *****************************************************************************/
66 #if defined( WIN32 )
67 static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
68                                   int i_num_buffers );
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_keys = 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     title_key_t **pp_writekey;
158     title_key_t **pp_currentkey;
159     title_key_t *p_titlekey;
160     dvd_key_t p_key;
161     int i_ret;
162
163     if( ! dvdcss->b_encrypted )
164     {
165         return 0;
166     }
167
168     /* Check if we've already cracked this key */
169     p_titlekey = dvdcss->p_keys;
170     while( p_titlekey != NULL
171             && p_titlekey->p_next != NULL
172             && p_titlekey->p_next->i_startlb < i_block )
173     {
174         p_titlekey = p_titlekey->p_next;
175     }
176
177     if( p_titlekey != NULL && p_titlekey->i_startlb == i_block )
178     {
179         /* We've already cracked this key, nothing to do */
180         return 0;
181     }
182
183     /* Crack CSS title key for current VTS */
184     i_ret = CSSGetKey( dvdcss, i_block, p_key );
185
186     if( i_ret < 0 )
187     {
188         _dvdcss_error( dvdcss, "fatal error in vts css key" );
189         return i_ret;
190     }
191     else if( i_ret > 0 )
192     {
193         _dvdcss_error( dvdcss, "decryption unavailable" );
194         return -1;
195     }
196
197     /* Add key to keytable if it isn't empty */
198     if( p_key[0] || p_key[1] || p_key[2] || p_key[3] || p_key[4] )
199     {
200         /* Find our spot in the list */
201         pp_writekey = &(dvdcss->p_keys);
202         pp_currentkey = pp_writekey;
203         while( *pp_currentkey != NULL
204                 && (*pp_currentkey)->i_startlb < i_block )
205         {
206             pp_writekey = pp_currentkey;
207             pp_currentkey = &((*pp_currentkey)->p_next);
208         }
209
210         /* Write in the new key */
211         p_titlekey = *pp_writekey;
212         *pp_writekey = malloc( sizeof( title_key_t ) );
213         (*pp_writekey)->i_startlb = i_block;
214         memcpy( (*pp_writekey)->p_key, p_key, KEY_SIZE );
215         (*pp_writekey)->p_next = p_titlekey;
216     }
217
218     return 0;
219 }
220
221 /*****************************************************************************
222  * dvdcss_read: read data from the device, decrypt if requested
223  *****************************************************************************/
224 extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
225                                                int i_blocks,
226                                                int i_flags )
227 {
228     title_key_t *p_current;
229     int i_ret, i_index;
230
231     i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );
232
233     if( i_ret <= 0
234          || !dvdcss->b_encrypted
235          || !(i_flags & DVDCSS_READ_DECRYPT) )
236     {
237         return i_ret;
238     }
239
240     /* find our key */
241     p_current = dvdcss->p_keys;
242     while( p_current != NULL
243             && p_current->p_next
244             && p_current->p_next->i_startlb < dvdcss->i_seekpos )
245     {
246         p_current = p_current->p_next;
247     }
248
249     if( p_current == NULL )
250     {
251         /* no css key found to use, so no decryption to do */
252         return 0;
253     }
254
255     /* Decrypt the blocks we managed to read */
256     for( i_index = i_ret; i_index; i_index-- )
257     {
258         CSSDescrambleSector( p_current->p_key, p_buffer );
259         ((u8*)p_buffer)[0x14] &= 0x8f;
260         (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
261     }
262
263     return i_ret;
264 }
265
266 /*****************************************************************************
267  * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
268  *****************************************************************************/
269 extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
270                                                 int i_blocks,
271                                                 int i_flags )
272 {
273 #define P_IOVEC ((struct iovec*)p_iovec)
274     title_key_t *p_current;
275     int i_ret, i_index;
276     void *iov_base;
277     size_t iov_len;
278
279     i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );
280
281     if( i_ret <= 0
282          || !dvdcss->b_encrypted
283          || !(i_flags & DVDCSS_READ_DECRYPT) )
284     {
285         return i_ret;
286     }
287
288     /* Find our key */
289     p_current = dvdcss->p_keys;
290     while( p_current != NULL
291             && p_current->p_next
292             && p_current->p_next->i_startlb < dvdcss->i_seekpos )
293     {
294         p_current = p_current->p_next;
295     }
296
297     if( p_current == NULL )
298     {
299         /* no css key found to use, so no decryption to do */
300         return 0;
301     }
302
303
304     /* Initialize loop for decryption */
305     iov_base = P_IOVEC->iov_base;
306     iov_len = P_IOVEC->iov_len;
307
308     /* Decrypt the blocks we managed to read */
309     for( i_index = i_ret; i_index; i_index-- )
310     {
311         /* Check that iov_len is a multiple of 2048 */
312         if( iov_len & 0x7ff )
313         {
314             return -1;
315         }
316
317         while( iov_len == 0 )
318         {
319             P_IOVEC++;
320             iov_base = P_IOVEC->iov_base;
321             iov_len = P_IOVEC->iov_len;
322         }
323
324         CSSDescrambleSector( p_current->p_key, iov_base );
325         ((u8*)iov_base)[0x14] &= 0x8f;
326
327         (u8*)iov_base += DVDCSS_BLOCK_SIZE;
328         (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
329     }
330
331     return i_ret;
332 #undef P_IOVEC
333 }
334
335 /*****************************************************************************
336  * dvdcss_close: close the DVD device and clean up the library
337  *****************************************************************************/
338 extern int dvdcss_close ( dvdcss_handle dvdcss )
339 {
340     title_key_t *p_currentkey;
341     int i_ret;
342
343     /* Free our list of keys */
344     p_currentkey = dvdcss->p_keys;
345     while( p_currentkey )
346     {
347         title_key_t *p_tmpkey = p_currentkey->p_next;
348         free( p_currentkey );
349         p_currentkey = p_tmpkey;
350     }
351
352     i_ret = _dvdcss_close( dvdcss );
353
354     if( i_ret < 0 )
355     {
356         return i_ret;
357     }
358
359     free( dvdcss );
360
361     return 0;
362 }
363
364 /* Following functions are local */
365
366 static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
367 {
368 #if defined( WIN32 )
369     if( WIN2K )
370     {
371         char psz_dvd[7];
372         _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
373         (HANDLE) dvdcss->i_fd =
374                 CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
375                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
376                                 NULL, OPEN_EXISTING, 0, NULL );
377         if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
378         {
379             _dvdcss_error( dvdcss, "failed opening device" );
380             return -1;
381         }
382     }
383     else
384     {
385         dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
386         if( dvdcss->i_fd == -1 )
387         {
388             _dvdcss_error( dvdcss, "failed opening device" );
389             return -1;
390         }
391     }
392
393 #else
394     dvdcss->i_fd = open( psz_target, 0 );
395
396     if( dvdcss->i_fd == -1 )
397     {
398         _dvdcss_error( dvdcss, "failed opening device" );
399         return -1;
400     }
401
402 #endif
403
404     return 0;
405 }
406
407 static int _dvdcss_close ( dvdcss_handle dvdcss )
408 {
409 #if defined( WIN32 )
410     if( WIN2K )
411     {
412         CloseHandle( (HANDLE) dvdcss->i_fd );
413     }
414     else
415     {
416         _win32_dvdcss_aclose( dvdcss->i_fd );
417     }
418 #else
419     close( dvdcss->i_fd );
420 #endif
421
422     return 0;
423 }
424
425 static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
426 {
427 #if defined( WIN32 )
428     if( WIN2K )
429     {
430         LARGE_INTEGER li_read;
431
432 #ifndef INVALID_SET_FILE_POINTER
433 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
434 #endif
435
436         li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
437
438         li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
439                                           li_read.LowPart,
440                                           &li_read.HighPart, FILE_BEGIN );
441         if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
442             && GetLastError() != NO_ERROR)
443         {
444             li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
445         }
446
447         li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
448         return (int)li_read.QuadPart;
449     }
450     else
451     {
452         return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
453     }
454 #else
455     off_t i_read;
456
457     dvdcss->i_seekpos = i_blocks;
458
459     i_read = lseek( dvdcss->i_fd,
460                     (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );
461
462     return i_read / DVDCSS_BLOCK_SIZE;
463 #endif
464
465 }
466
467 static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
468 {
469 #if defined( WIN32 ) 
470     if( WIN2K )
471     {
472         int i_bytes;
473
474         if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
475                   i_blocks * DVDCSS_BLOCK_SIZE,
476                   (LPDWORD)&i_bytes, NULL ) )
477         {
478             return -1;
479         }
480         return i_bytes / DVDCSS_BLOCK_SIZE;
481     }
482     else
483     {
484         return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
485     }
486
487 #else
488     int i_bytes;
489
490     i_bytes = read( dvdcss->i_fd, p_buffer, (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
491     return i_bytes / DVDCSS_BLOCK_SIZE;
492 #endif
493
494 }
495
496 static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec, int i_blocks )
497 {
498     int i_read;
499
500 #if defined( WIN32 )
501     i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks );
502     return i_read;
503 #else
504     i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
505     return i_read / DVDCSS_BLOCK_SIZE;
506 #endif
507 }
508
509 #if defined( WIN32 )
510
511 /*****************************************************************************
512  * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
513  *                      _win32_dvdcss_aread for win9x
514  *****************************************************************************/
515 static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
516                                 int i_num_buffers )
517 {
518     int i_index, i_len, i_total = 0;
519     unsigned char *p_base;
520     int i_blocks;
521
522     if( WIN2K )
523     {
524         for( i_index = i_num_buffers; i_index; i_index-- )
525         {
526             i_len  = p_iovec->iov_len;
527             p_base = p_iovec->iov_base;
528
529             if( i_len > 0 )
530             {
531                 unsigned long int i_bytes;
532                 if( !ReadFile( (HANDLE) i_fd, p_base, i_len, &i_bytes, NULL ) )
533                 {
534                     return -1;
535                     /* One of the reads failed, too bad.
536                        We won't even bother returning the reads that went well,
537                        and like in the posix spec the file postition is left
538                        unspecified after a failure */
539                 }
540                 i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
541
542                 i_total += i_blocks;
543
544                 if( i_blocks != (i_len / DVDCSS_BLOCK_SIZE) )
545                 {
546                     /* we reached the end of the file */
547                     return i_total;
548                 }
549
550             }
551
552             p_iovec++;
553         }
554     }
555     else /* Win9x */
556     {
557         for( i_index = i_num_buffers; i_index; i_index-- )
558         {
559             i_len  = p_iovec->iov_len / DVDCSS_BLOCK_SIZE;
560             p_base = p_iovec->iov_base;
561
562             if( i_len > 0 )
563             {
564                 i_blocks = _win32_dvdcss_aread( i_fd, p_base, i_len );
565                 if( i_blocks < 0 )
566                 {
567                     return -1;  /* idem */
568                 }
569
570                 i_total += i_blocks;
571
572                 if( i_blocks != i_len )
573                 {
574                     /* we reached the end of the file or a signal interrupted
575                        the read */
576                     return i_total;
577                 }
578             }
579
580             p_iovec++;
581         }
582     }
583
584     return i_total;
585 }
586
587 /*****************************************************************************
588  * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
589  *                      structure)
590  *****************************************************************************/
591 static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
592 {
593     HMODULE hASPI;
594     DWORD dwSupportInfo;
595     struct w32_aspidev *fd;
596     int i, j, i_hostadapters;
597     long (*lpGetSupport)( void );
598     long (*lpSendCommand)( void* );
599     
600     hASPI = LoadLibrary( "wnaspi32.dll" );
601     if( hASPI == NULL )
602     {
603         _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
604         return -1;
605     }
606
607     (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
608     (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
609     
610     if(lpGetSupport == NULL || lpSendCommand == NULL )
611     {
612         _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
613         FreeLibrary( hASPI );
614         return -1;
615     }
616
617     dwSupportInfo = lpGetSupport();
618
619     if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
620     {
621         _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
622         FreeLibrary( hASPI );
623         return -1;
624     }
625
626     if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
627     {
628         _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
629         FreeLibrary( hASPI );
630         return -1;
631     }
632
633     i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
634     if( i_hostadapters == 0 )
635     {
636         FreeLibrary( hASPI );
637         return -1;
638     }
639
640     fd = malloc( sizeof( struct w32_aspidev ) );
641     if( fd == NULL )
642     {
643         FreeLibrary( hASPI );
644         return -1;
645     }
646
647     fd->i_blocks = 0;
648     fd->hASPI = (long) hASPI;
649     fd->lpSendCommand = lpSendCommand;
650
651     if( !WIN2K )
652     {
653         fd->i_sid = MAKEWORD( ASPI_HAID, ASPI_TARGET );
654         return (int) fd;
655     }
656
657     c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
658
659     for( i = 0; i < i_hostadapters; i++ )
660     {
661         for( j = 0; j < 15; j++ )
662         {
663             struct SRB_GetDiskInfo srbDiskInfo;
664
665             srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
666             srbDiskInfo.SRB_HaId        = i;
667             srbDiskInfo.SRB_Flags       = 0;
668             srbDiskInfo.SRB_Hdr_Rsvd    = 0;
669             srbDiskInfo.SRB_Target      = j;
670             srbDiskInfo.SRB_Lun         = 0;
671
672             lpSendCommand( (void*) &srbDiskInfo );
673
674             if( srbDiskInfo.SRB_Status == SS_COMP &&
675                 srbDiskInfo.SRB_Int13HDriveInfo == c_drive )
676             {
677                 fd->i_sid = MAKEWORD( i, j );
678                 return (int) fd;
679             }
680         }
681     }
682
683     free( (void*) fd );
684     FreeLibrary( hASPI );
685     _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
686
687     return( -1 );
688 }
689
690 /*****************************************************************************
691  * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
692  *                       structure)
693  *****************************************************************************/
694 static int _win32_dvdcss_aclose( int i_fd )
695 {
696     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
697
698     FreeLibrary( (HMODULE) fd->hASPI );
699     free( (void*) i_fd );
700
701     return 0;
702 }
703
704 /*****************************************************************************
705  * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
706  * 
707  * returns the number of blocks read.
708  *****************************************************************************/
709 static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
710 {
711     int i_old_blocks;
712     char sz_buf[ DVDCSS_BLOCK_SIZE ];
713     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
714     
715     i_old_blocks = fd->i_blocks;
716     fd->i_blocks = i_blocks;
717
718     if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
719     {
720         fd->i_blocks = i_old_blocks;
721         return -1;
722     }
723
724     (fd->i_blocks)--;
725
726     return fd->i_blocks;
727 }
728
729 /*****************************************************************************
730  * _win32_dvdcss_aread: aspi version of _dvdcss_read
731  *
732  * returns the number of blocks read.
733  *****************************************************************************/
734 static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
735 {
736     HANDLE hEvent;
737     DWORD dwStart, dwLen;
738     struct SRB_ExecSCSICmd ssc;
739     struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
740
741     memset( &ssc, 0, sizeof( ssc ) );
742
743     dwStart = fd->i_blocks;
744     dwLen = i_blocks;
745
746     hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
747     if( hEvent == NULL )
748     {
749         return -1;
750     }
751
752     ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
753     ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
754     ssc.SRB_HaId        = LOBYTE( fd->i_sid );
755     ssc.SRB_Target      = HIBYTE( fd->i_sid );
756     ssc.SRB_SenseLen    = SENSE_LEN;
757     ssc.SRB_PostProc    = (LPVOID) hEvent;
758
759     ssc.SRB_BufLen      = dwLen * DVDCSS_BLOCK_SIZE;
760     ssc.SRB_BufPointer  = p_data;
761     ssc.SRB_CDBLen      = 12;
762
763     ssc.CDBByte[0]      = 0xA8; /* RAW */
764     ssc.CDBByte[2]      = (UCHAR) dwStart >> 24;
765     ssc.CDBByte[3]      = (UCHAR) (dwStart >> 16) & 0xff;
766     ssc.CDBByte[4]      = (UCHAR) (dwStart >> 8) & 0xff;
767     ssc.CDBByte[5]      = (UCHAR) (dwStart) & 0xff;
768     ssc.CDBByte[6]      = (UCHAR) dwLen >> 24;
769     ssc.CDBByte[7]      = (UCHAR) (dwLen >> 16) & 0xff;
770     ssc.CDBByte[8]      = (UCHAR) (dwLen >> 8) & 0xff;
771     ssc.CDBByte[9]      = (UCHAR) (dwLen) & 0xff;
772
773     ResetEvent( hEvent );
774     if( fd->lpSendCommand( (void*) &ssc ) == SS_PENDING )
775     {
776         WaitForSingleObject( hEvent, INFINITE );
777     }
778
779     CloseHandle( hEvent );
780
781     if( ssc.SRB_Status != SS_COMP )
782     {
783         return -1;
784     }
785         
786     fd->i_blocks += i_blocks;
787
788     return i_blocks;
789 }
790
791 #endif
792