20613
|
1 /*****************************************************************************
|
|
2 * device.h: DVD device access
|
|
3 *****************************************************************************
|
|
4 * Copyright (C) 1998-2006 VideoLAN
|
|
5 * $Id$
|
|
6 *
|
|
7 * Authors: Stéphane Borel <stef@via.ecp.fr>
|
|
8 * Sam 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 "config.h"
|
|
30
|
|
31 #include <stdio.h>
|
|
32 #include <stdlib.h>
|
|
33 #include <string.h>
|
|
34 #ifdef HAVE_ERRNO_H
|
|
35 # include <errno.h>
|
|
36 #endif
|
|
37 #include <sys/types.h>
|
|
38 #include <sys/stat.h>
|
|
39 #ifdef HAVE_SYS_PARAM_H
|
|
40 # include <sys/param.h>
|
|
41 #endif
|
|
42 #include <fcntl.h>
|
|
43
|
|
44 #ifdef HAVE_UNISTD_H
|
|
45 # include <unistd.h>
|
|
46 #endif
|
|
47
|
|
48 #ifdef HAVE_LIMITS_H
|
|
49 # include <limits.h>
|
|
50 #endif
|
|
51
|
|
52 #if defined( WIN32 ) && !defined( SYS_CYGWIN )
|
|
53 # include <io.h> /* read() */
|
|
54 #else
|
|
55 # include <sys/uio.h> /* struct iovec */
|
|
56 #endif
|
|
57
|
|
58 #ifdef DARWIN_DVD_IOCTL
|
|
59 # include <paths.h>
|
|
60 # include <CoreFoundation/CoreFoundation.h>
|
|
61 # include <IOKit/IOKitLib.h>
|
|
62 # include <IOKit/IOBSD.h>
|
|
63 # include <IOKit/storage/IOMedia.h>
|
|
64 # include <IOKit/storage/IOCDMedia.h>
|
|
65 # include <IOKit/storage/IODVDMedia.h>
|
|
66 #endif
|
|
67
|
|
68 #include "dvdcss/dvdcss.h"
|
|
69
|
|
70 #include "common.h"
|
|
71 #include "css.h"
|
|
72 #include "libdvdcss.h"
|
|
73 #include "ioctl.h"
|
|
74 #include "device.h"
|
|
75
|
|
76 /*****************************************************************************
|
|
77 * Device reading prototypes
|
|
78 *****************************************************************************/
|
|
79 static int libc_open ( dvdcss_t, char const * );
|
|
80 static int libc_seek ( dvdcss_t, int );
|
|
81 static int libc_read ( dvdcss_t, void *, int );
|
|
82 static int libc_readv ( dvdcss_t, struct iovec *, int );
|
|
83
|
|
84 #ifdef WIN32
|
|
85 static int win2k_open ( dvdcss_t, char const * );
|
|
86 static int aspi_open ( dvdcss_t, char const * );
|
|
87 static int win2k_seek ( dvdcss_t, int );
|
|
88 static int aspi_seek ( dvdcss_t, int );
|
|
89 static int win2k_read ( dvdcss_t, void *, int );
|
|
90 static int aspi_read ( dvdcss_t, void *, int );
|
|
91 static int win_readv ( dvdcss_t, struct iovec *, int );
|
|
92
|
|
93 static int aspi_read_internal ( int, void *, int );
|
|
94 #endif
|
|
95
|
|
96 int _dvdcss_use_ioctls( dvdcss_t dvdcss )
|
|
97 {
|
|
98 #if defined( WIN32 )
|
|
99 if( dvdcss->b_file )
|
|
100 {
|
|
101 return 0;
|
|
102 }
|
|
103
|
|
104 /* FIXME: implement this for Windows */
|
|
105 if( WIN2K )
|
|
106 {
|
|
107 return 1;
|
|
108 }
|
|
109 else
|
|
110 {
|
|
111 return 1;
|
|
112 }
|
|
113 #else
|
|
114 struct stat fileinfo;
|
|
115 int ret;
|
|
116
|
|
117 ret = fstat( dvdcss->i_fd, &fileinfo );
|
|
118 if( ret < 0 )
|
|
119 {
|
|
120 return 1; /* What to do? Be conservative and try to use the ioctls */
|
|
121 }
|
|
122
|
|
123 /* Complete this list and check that we test for the right things
|
|
124 * (I've assumed for all OSs that 'r', (raw) device, are char devices
|
|
125 * and those that don't contain/use an 'r' in the name are block devices)
|
|
126 *
|
|
127 * Linux needs a block device
|
|
128 * Solaris needs a char device
|
|
129 * Darwin needs a char device
|
|
130 * OpenBSD needs a char device
|
|
131 * NetBSD needs a char device
|
|
132 * FreeBSD can use either the block or the char device
|
|
133 * BSD/OS can use either the block or the char device
|
|
134 */
|
|
135
|
|
136 /* Check if this is a block/char device */
|
|
137 if( S_ISBLK( fileinfo.st_mode ) ||
|
|
138 S_ISCHR( fileinfo.st_mode ) )
|
|
139 {
|
|
140 return 1;
|
|
141 }
|
|
142 else
|
|
143 {
|
|
144 return 0;
|
|
145 }
|
|
146 #endif
|
|
147 }
|
|
148
|
|
149 void _dvdcss_check ( dvdcss_t dvdcss )
|
|
150 {
|
|
151 #if defined( WIN32 )
|
|
152 DWORD drives;
|
|
153 int i;
|
|
154 #elif defined( DARWIN_DVD_IOCTL )
|
|
155 io_object_t next_media;
|
|
156 mach_port_t master_port;
|
|
157 kern_return_t kern_result;
|
|
158 io_iterator_t media_iterator;
|
|
159 CFMutableDictionaryRef classes_to_match;
|
|
160 #else
|
|
161 char *ppsz_devices[] = { "/dev/dvd", "/dev/cdrom", "/dev/hdc", NULL };
|
|
162 int i, i_fd;
|
|
163 #endif
|
|
164
|
|
165 /* If the device name is non-null, return */
|
|
166 if( dvdcss->psz_device[0] )
|
|
167 {
|
|
168 return;
|
|
169 }
|
|
170
|
|
171 #if defined( WIN32 )
|
|
172 drives = GetLogicalDrives();
|
|
173
|
|
174 for( i = 0; drives; i++ )
|
|
175 {
|
|
176 char psz_device[5];
|
|
177 DWORD cur = 1 << i;
|
|
178 UINT i_ret;
|
|
179
|
|
180 if( (drives & cur) == 0 )
|
|
181 {
|
|
182 continue;
|
|
183 }
|
|
184 drives &= ~cur;
|
|
185
|
|
186 sprintf( psz_device, "%c:\\", 'A' + i );
|
|
187 i_ret = GetDriveType( psz_device );
|
|
188 if( i_ret != DRIVE_CDROM )
|
|
189 {
|
|
190 continue;
|
|
191 }
|
|
192
|
|
193 /* Remove trailing backslash */
|
|
194 psz_device[2] = '\0';
|
|
195
|
|
196 /* FIXME: we want to differenciate between CD and DVD drives
|
|
197 * using DeviceIoControl() */
|
|
198 print_debug( dvdcss, "defaulting to drive `%s'", psz_device );
|
|
199 free( dvdcss->psz_device );
|
|
200 dvdcss->psz_device = strdup( psz_device );
|
|
201 return;
|
|
202 }
|
|
203 #elif defined( DARWIN_DVD_IOCTL )
|
|
204
|
|
205 kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
|
|
206 if( kern_result != KERN_SUCCESS )
|
|
207 {
|
|
208 return;
|
|
209 }
|
|
210
|
|
211 classes_to_match = IOServiceMatching( kIODVDMediaClass );
|
|
212 if( classes_to_match == NULL )
|
|
213 {
|
|
214 return;
|
|
215 }
|
|
216
|
|
217 CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectableKey ),
|
|
218 kCFBooleanTrue );
|
|
219
|
|
220 kern_result = IOServiceGetMatchingServices( master_port, classes_to_match,
|
|
221 &media_iterator );
|
|
222 if( kern_result != KERN_SUCCESS )
|
|
223 {
|
|
224 return;
|
|
225 }
|
|
226
|
|
227 next_media = IOIteratorNext( media_iterator );
|
|
228 for( ; ; )
|
|
229 {
|
|
230 char psz_buf[0x32];
|
|
231 size_t i_pathlen;
|
|
232 CFTypeRef psz_path;
|
|
233
|
|
234 next_media = IOIteratorNext( media_iterator );
|
|
235 if( next_media == 0 )
|
|
236 {
|
|
237 break;
|
|
238 }
|
|
239
|
|
240 psz_path = IORegistryEntryCreateCFProperty( next_media,
|
|
241 CFSTR( kIOBSDNameKey ),
|
|
242 kCFAllocatorDefault,
|
|
243 0 );
|
|
244 if( psz_path == NULL )
|
|
245 {
|
|
246 IOObjectRelease( next_media );
|
|
247 continue;
|
|
248 }
|
|
249
|
|
250 snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
|
|
251 i_pathlen = strlen( psz_buf );
|
|
252
|
|
253 if( CFStringGetCString( psz_path,
|
|
254 (char*)&psz_buf + i_pathlen,
|
|
255 sizeof(psz_buf) - i_pathlen,
|
|
256 kCFStringEncodingASCII ) )
|
|
257 {
|
|
258 print_debug( dvdcss, "defaulting to drive `%s'", psz_buf );
|
|
259 CFRelease( psz_path );
|
|
260 IOObjectRelease( next_media );
|
|
261 IOObjectRelease( media_iterator );
|
|
262 free( dvdcss->psz_device );
|
|
263 dvdcss->psz_device = strdup( psz_buf );
|
|
264 return;
|
|
265 }
|
|
266
|
|
267 CFRelease( psz_path );
|
|
268
|
|
269 IOObjectRelease( next_media );
|
|
270 }
|
|
271
|
|
272 IOObjectRelease( media_iterator );
|
|
273 #else
|
|
274 for( i = 0; ppsz_devices[i]; i++ )
|
|
275 {
|
|
276 i_fd = open( ppsz_devices[i], 0 );
|
|
277 if( i_fd != -1 )
|
|
278 {
|
|
279 print_debug( dvdcss, "defaulting to drive `%s'", ppsz_devices[i] );
|
|
280 close( i_fd );
|
|
281 free( dvdcss->psz_device );
|
|
282 dvdcss->psz_device = strdup( ppsz_devices[i] );
|
|
283 return;
|
|
284 }
|
|
285 }
|
|
286 #endif
|
|
287
|
|
288 print_error( dvdcss, "could not find a suitable default drive" );
|
|
289 }
|
|
290
|
|
291 int _dvdcss_open ( dvdcss_t dvdcss )
|
|
292 {
|
|
293 char const *psz_device = dvdcss->psz_device;
|
|
294
|
|
295 print_debug( dvdcss, "opening target `%s'", psz_device );
|
|
296
|
|
297 #if defined( WIN32 )
|
|
298 dvdcss->b_file = 1;
|
|
299 /* If device is "X:" or "X:\", we are not actually opening a file. */
|
|
300 if (psz_device[0] && psz_device[1] == ':' &&
|
|
301 (!psz_device[2] || (psz_device[2] == '\\' && !psz_device[3])))
|
|
302 dvdcss->b_file = 0;
|
|
303
|
|
304 /* Initialize readv temporary buffer */
|
|
305 dvdcss->p_readv_buffer = NULL;
|
|
306 dvdcss->i_readv_buf_size = 0;
|
|
307
|
|
308 if( !dvdcss->b_file && WIN2K )
|
|
309 {
|
|
310 print_debug( dvdcss, "using Win2K API for access" );
|
|
311 dvdcss->pf_seek = win2k_seek;
|
|
312 dvdcss->pf_read = win2k_read;
|
|
313 dvdcss->pf_readv = win_readv;
|
|
314 return win2k_open( dvdcss, psz_device );
|
|
315 }
|
|
316 else if( !dvdcss->b_file )
|
|
317 {
|
|
318 print_debug( dvdcss, "using ASPI for access" );
|
|
319 dvdcss->pf_seek = aspi_seek;
|
|
320 dvdcss->pf_read = aspi_read;
|
|
321 dvdcss->pf_readv = win_readv;
|
|
322 return aspi_open( dvdcss, psz_device );
|
|
323 }
|
|
324 else
|
|
325 #endif
|
|
326 {
|
|
327 print_debug( dvdcss, "using libc for access" );
|
|
328 dvdcss->pf_seek = libc_seek;
|
|
329 dvdcss->pf_read = libc_read;
|
|
330 dvdcss->pf_readv = libc_readv;
|
|
331 return libc_open( dvdcss, psz_device );
|
|
332 }
|
|
333 }
|
|
334
|
|
335 #ifndef WIN32
|
|
336 int _dvdcss_raw_open ( dvdcss_t dvdcss, char const *psz_device )
|
|
337 {
|
|
338 dvdcss->i_raw_fd = open( psz_device, 0 );
|
|
339
|
|
340 if( dvdcss->i_raw_fd == -1 )
|
|
341 {
|
|
342 print_debug( dvdcss, "cannot open %s (%s)",
|
|
343 psz_device, strerror(errno) );
|
|
344 print_error( dvdcss, "failed to open raw device, but continuing" );
|
|
345 return -1;
|
|
346 }
|
|
347 else
|
|
348 {
|
|
349 dvdcss->i_read_fd = dvdcss->i_raw_fd;
|
|
350 }
|
|
351
|
|
352 return 0;
|
|
353 }
|
|
354 #endif
|
|
355
|
|
356 int _dvdcss_close ( dvdcss_t dvdcss )
|
|
357 {
|
|
358 #if defined( WIN32 )
|
|
359 if( dvdcss->b_file )
|
|
360 {
|
|
361 close( dvdcss->i_fd );
|
|
362 }
|
|
363 else if( WIN2K )
|
|
364 {
|
|
365 CloseHandle( (HANDLE) dvdcss->i_fd );
|
|
366 }
|
|
367 else /* ASPI */
|
|
368 {
|
|
369 struct w32_aspidev *fd = (struct w32_aspidev *) dvdcss->i_fd;
|
|
370
|
|
371 /* Unload aspi and free w32_aspidev structure */
|
|
372 FreeLibrary( (HMODULE) fd->hASPI );
|
|
373 free( (void*) dvdcss->i_fd );
|
|
374 }
|
|
375
|
|
376 /* Free readv temporary buffer */
|
|
377 if( dvdcss->p_readv_buffer )
|
|
378 {
|
|
379 free( dvdcss->p_readv_buffer );
|
|
380 dvdcss->p_readv_buffer = NULL;
|
|
381 dvdcss->i_readv_buf_size = 0;
|
|
382 }
|
|
383
|
|
384 return 0;
|
|
385 #else
|
|
386 close( dvdcss->i_fd );
|
|
387
|
|
388 if( dvdcss->i_raw_fd >= 0 )
|
|
389 {
|
|
390 close( dvdcss->i_raw_fd );
|
|
391 dvdcss->i_raw_fd = -1;
|
|
392 }
|
|
393
|
|
394 return 0;
|
|
395 #endif
|
|
396 }
|
|
397
|
|
398 /* Following functions are local */
|
|
399
|
|
400 /*****************************************************************************
|
|
401 * Open commands.
|
|
402 *****************************************************************************/
|
|
403 static int libc_open ( dvdcss_t dvdcss, char const *psz_device )
|
|
404 {
|
|
405 #if !defined( WIN32 )
|
|
406 dvdcss->i_fd = dvdcss->i_read_fd = open( psz_device, 0 );
|
|
407 #else
|
|
408 dvdcss->i_fd = dvdcss->i_read_fd = open( psz_device, O_BINARY );
|
|
409 #endif
|
|
410
|
|
411 if( dvdcss->i_fd == -1 )
|
|
412 {
|
|
413 print_debug( dvdcss, "cannot open %s (%s)",
|
|
414 psz_device, strerror(errno) );
|
|
415 print_error( dvdcss, "failed to open device" );
|
|
416 return -1;
|
|
417 }
|
|
418
|
|
419 dvdcss->i_pos = 0;
|
|
420
|
|
421 return 0;
|
|
422 }
|
|
423
|
|
424 #if defined( WIN32 )
|
|
425 static int win2k_open ( dvdcss_t dvdcss, char const *psz_device )
|
|
426 {
|
|
427 char psz_dvd[7];
|
|
428 _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_device[0] );
|
|
429
|
|
430 /* To work around an M$ bug in IOCTL_DVD_READ_STRUCTURE, we need read
|
|
431 * _and_ write access to the device (so we can make SCSI Pass Through
|
|
432 * Requests). Unfortunately this is only allowed if you have
|
|
433 * administrator priviledges so we allow for a fallback method with
|
|
434 * only read access to the device (in this case ioctl_ReadCopyright()
|
|
435 * won't send back the right result).
|
|
436 * (See Microsoft Q241374: Read and Write Access Required for SCSI
|
|
437 * Pass Through Requests) */
|
|
438 dvdcss->i_fd = (int)
|
|
439 CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
|
|
440 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
441 NULL, OPEN_EXISTING,
|
|
442 FILE_FLAG_RANDOM_ACCESS, NULL );
|
|
443
|
|
444 if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
|
|
445 dvdcss->i_fd = (int)
|
|
446 CreateFile( psz_dvd, GENERIC_READ, FILE_SHARE_READ,
|
|
447 NULL, OPEN_EXISTING,
|
|
448 FILE_FLAG_RANDOM_ACCESS, NULL );
|
|
449
|
|
450 if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
|
|
451 {
|
|
452 print_error( dvdcss, "failed opening device" );
|
|
453 return -1;
|
|
454 }
|
|
455
|
|
456 dvdcss->i_pos = 0;
|
|
457
|
|
458 return 0;
|
|
459 }
|
|
460
|
|
461 static int aspi_open( dvdcss_t dvdcss, char const * psz_device )
|
|
462 {
|
|
463 HMODULE hASPI;
|
|
464 DWORD dwSupportInfo;
|
|
465 struct w32_aspidev *fd;
|
|
466 int i, j, i_hostadapters;
|
|
467 GETASPI32SUPPORTINFO lpGetSupport;
|
|
468 SENDASPI32COMMAND lpSendCommand;
|
|
469 char c_drive = psz_device[0];
|
|
470
|
|
471 /* load aspi and init w32_aspidev structure */
|
|
472 hASPI = LoadLibrary( "wnaspi32.dll" );
|
|
473 if( hASPI == NULL )
|
|
474 {
|
|
475 print_error( dvdcss, "unable to load wnaspi32.dll" );
|
|
476 return -1;
|
|
477 }
|
|
478
|
|
479 lpGetSupport = (GETASPI32SUPPORTINFO) GetProcAddress( hASPI, "GetASPI32SupportInfo" );
|
|
480 lpSendCommand = (SENDASPI32COMMAND) GetProcAddress( hASPI, "SendASPI32Command" );
|
|
481
|
|
482 if(lpGetSupport == NULL || lpSendCommand == NULL )
|
|
483 {
|
|
484 print_error( dvdcss, "unable to get aspi function pointers" );
|
|
485 FreeLibrary( hASPI );
|
|
486 return -1;
|
|
487 }
|
|
488
|
|
489 dwSupportInfo = lpGetSupport();
|
|
490
|
|
491 if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
|
|
492 {
|
|
493 print_error( dvdcss, "no ASPI adapters found" );
|
|
494 FreeLibrary( hASPI );
|
|
495 return -1;
|
|
496 }
|
|
497
|
|
498 if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
|
|
499 {
|
|
500 print_error( dvdcss, "unable to initalize aspi layer" );
|
|
501 FreeLibrary( hASPI );
|
|
502 return -1;
|
|
503 }
|
|
504
|
|
505 i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
|
|
506 if( i_hostadapters == 0 )
|
|
507 {
|
|
508 print_error( dvdcss, "no ASPI adapters ready" );
|
|
509 FreeLibrary( hASPI );
|
|
510 return -1;
|
|
511 }
|
|
512
|
|
513 fd = malloc( sizeof( struct w32_aspidev ) );
|
|
514 if( fd == NULL )
|
|
515 {
|
|
516 print_error( dvdcss, "not enough memory" );
|
|
517 FreeLibrary( hASPI );
|
|
518 return -1;
|
|
519 }
|
|
520
|
|
521 fd->i_blocks = 0;
|
|
522 fd->hASPI = (long) hASPI;
|
|
523 fd->lpSendCommand = lpSendCommand;
|
|
524
|
|
525 c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';
|
|
526
|
|
527 for( i = 0; i < i_hostadapters; i++ )
|
|
528 {
|
|
529 for( j = 0; j < 15; j++ )
|
|
530 {
|
|
531 struct SRB_GetDiskInfo srbDiskInfo;
|
|
532
|
|
533 srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO;
|
|
534 srbDiskInfo.SRB_HaId = i;
|
|
535 srbDiskInfo.SRB_Flags = 0;
|
|
536 srbDiskInfo.SRB_Hdr_Rsvd = 0;
|
|
537 srbDiskInfo.SRB_Target = j;
|
|
538 srbDiskInfo.SRB_Lun = 0;
|
|
539
|
|
540 lpSendCommand( (void*) &srbDiskInfo );
|
|
541
|
|
542 if( (srbDiskInfo.SRB_Status == SS_COMP) &&
|
|
543 (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
|
|
544 {
|
|
545 /* Make sure this is a cdrom device */
|
|
546 struct SRB_GDEVBlock srbGDEVBlock;
|
|
547
|
|
548 memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) );
|
|
549 srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
|
|
550 srbGDEVBlock.SRB_HaId = i;
|
|
551 srbGDEVBlock.SRB_Target = j;
|
|
552
|
|
553 lpSendCommand( (void*) &srbGDEVBlock );
|
|
554
|
|
555 if( ( srbGDEVBlock.SRB_Status == SS_COMP ) &&
|
|
556 ( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) )
|
|
557 {
|
|
558 fd->i_sid = MAKEWORD( i, j );
|
|
559 dvdcss->i_fd = (int) fd;
|
|
560 dvdcss->i_pos = 0;
|
|
561 return 0;
|
|
562 }
|
|
563 else
|
|
564 {
|
|
565 free( (void*) fd );
|
|
566 FreeLibrary( hASPI );
|
|
567 print_error( dvdcss,"this is not a cdrom drive" );
|
|
568 return -1;
|
|
569 }
|
|
570 }
|
|
571 }
|
|
572 }
|
|
573
|
|
574 free( (void*) fd );
|
|
575 FreeLibrary( hASPI );
|
|
576 print_error( dvdcss, "unable to get haid and target (aspi)" );
|
|
577 return -1;
|
|
578 }
|
|
579 #endif
|
|
580
|
|
581 /*****************************************************************************
|
|
582 * Seek commands.
|
|
583 *****************************************************************************/
|
|
584 static int libc_seek( dvdcss_t dvdcss, int i_blocks )
|
|
585 {
|
|
586 off_t i_seek;
|
|
587
|
|
588 if( dvdcss->i_pos == i_blocks )
|
|
589 {
|
|
590 /* We are already in position */
|
|
591 return i_blocks;
|
|
592 }
|
|
593
|
|
594 i_seek = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
|
|
595 i_seek = lseek64( dvdcss->i_read_fd, i_seek, SEEK_SET );
|
|
596
|
|
597 if( i_seek < 0 )
|
|
598 {
|
|
599 print_error( dvdcss, "seek error" );
|
|
600 dvdcss->i_pos = -1;
|
|
601 return i_seek;
|
|
602 }
|
|
603
|
|
604 dvdcss->i_pos = i_seek / DVDCSS_BLOCK_SIZE;
|
|
605
|
|
606 return dvdcss->i_pos;
|
|
607 }
|
|
608
|
|
609 #if defined( WIN32 )
|
|
610 static int win2k_seek( dvdcss_t dvdcss, int i_blocks )
|
|
611 {
|
|
612 LARGE_INTEGER li_seek;
|
|
613
|
|
614 #ifndef INVALID_SET_FILE_POINTER
|
|
615 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
|
616 #endif
|
|
617
|
|
618 if( dvdcss->i_pos == i_blocks )
|
|
619 {
|
|
620 /* We are already in position */
|
|
621 return i_blocks;
|
|
622 }
|
|
623
|
|
624 li_seek.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;
|
|
625
|
|
626 li_seek.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
|
|
627 li_seek.LowPart,
|
|
628 &li_seek.HighPart, FILE_BEGIN );
|
|
629 if( (li_seek.LowPart == INVALID_SET_FILE_POINTER)
|
|
630 && GetLastError() != NO_ERROR)
|
|
631 {
|
|
632 dvdcss->i_pos = -1;
|
|
633 return -1;
|
|
634 }
|
|
635
|
|
636 dvdcss->i_pos = li_seek.QuadPart / DVDCSS_BLOCK_SIZE;
|
|
637
|
|
638 return dvdcss->i_pos;
|
|
639 }
|
|
640
|
|
641 static int aspi_seek( dvdcss_t dvdcss, int i_blocks )
|
|
642 {
|
|
643 int i_old_blocks;
|
|
644 char sz_buf[ DVDCSS_BLOCK_SIZE ];
|
|
645 struct w32_aspidev *fd = (struct w32_aspidev *) dvdcss->i_fd;
|
|
646
|
|
647 if( dvdcss->i_pos == i_blocks )
|
|
648 {
|
|
649 /* We are already in position */
|
|
650 return i_blocks;
|
|
651 }
|
|
652
|
|
653 i_old_blocks = fd->i_blocks;
|
|
654 fd->i_blocks = i_blocks;
|
|
655
|
|
656 if( aspi_read_internal( dvdcss->i_fd, sz_buf, 1 ) == -1 )
|
|
657 {
|
|
658 fd->i_blocks = i_old_blocks;
|
|
659 dvdcss->i_pos = -1;
|
|
660 return -1;
|
|
661 }
|
|
662
|
|
663 (fd->i_blocks)--;
|
|
664
|
|
665 dvdcss->i_pos = fd->i_blocks;
|
|
666
|
|
667 return dvdcss->i_pos;
|
|
668 }
|
|
669 #endif
|
|
670
|
|
671 /*****************************************************************************
|
|
672 * Read commands.
|
|
673 *****************************************************************************/
|
|
674 static int libc_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
|
|
675 {
|
|
676 off_t i_size, i_ret;
|
|
677
|
|
678 i_size = (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE;
|
|
679 i_ret = read( dvdcss->i_read_fd, p_buffer, i_size );
|
|
680
|
|
681 if( i_ret < 0 )
|
|
682 {
|
|
683 print_error( dvdcss, "read error" );
|
|
684 dvdcss->i_pos = -1;
|
|
685 return i_ret;
|
|
686 }
|
|
687
|
|
688 /* Handle partial reads */
|
|
689 if( i_ret != i_size )
|
|
690 {
|
|
691 int i_seek;
|
|
692
|
|
693 dvdcss->i_pos = -1;
|
|
694 i_seek = libc_seek( dvdcss, i_ret / DVDCSS_BLOCK_SIZE );
|
|
695 if( i_seek < 0 )
|
|
696 {
|
|
697 return i_seek;
|
|
698 }
|
|
699
|
|
700 /* We have to return now so that i_pos isn't clobbered */
|
|
701 return i_ret / DVDCSS_BLOCK_SIZE;
|
|
702 }
|
|
703
|
|
704 dvdcss->i_pos += i_ret / DVDCSS_BLOCK_SIZE;
|
|
705 return i_ret / DVDCSS_BLOCK_SIZE;
|
|
706 }
|
|
707
|
|
708 #if defined( WIN32 )
|
|
709 static int win2k_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
|
|
710 {
|
|
711 int i_bytes;
|
|
712
|
|
713 if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
|
|
714 i_blocks * DVDCSS_BLOCK_SIZE,
|
|
715 (LPDWORD)&i_bytes, NULL ) )
|
|
716 {
|
|
717 dvdcss->i_pos = -1;
|
|
718 return -1;
|
|
719 }
|
|
720
|
|
721 dvdcss->i_pos += i_bytes / DVDCSS_BLOCK_SIZE;
|
|
722 return i_bytes / DVDCSS_BLOCK_SIZE;
|
|
723 }
|
|
724
|
|
725 static int aspi_read ( dvdcss_t dvdcss, void *p_buffer, int i_blocks )
|
|
726 {
|
|
727 int i_read = aspi_read_internal( dvdcss->i_fd, p_buffer, i_blocks );
|
|
728
|
|
729 if( i_read < 0 )
|
|
730 {
|
|
731 dvdcss->i_pos = -1;
|
|
732 return i_read;
|
|
733 }
|
|
734
|
|
735 dvdcss->i_pos += i_read;
|
|
736 return i_read;
|
|
737 }
|
|
738 #endif
|
|
739
|
|
740 /*****************************************************************************
|
|
741 * Readv commands.
|
|
742 *****************************************************************************/
|
|
743 static int libc_readv ( dvdcss_t dvdcss, struct iovec *p_iovec, int i_blocks )
|
|
744 {
|
|
745 #if defined( WIN32 )
|
|
746 int i_index, i_len, i_total = 0;
|
|
747 unsigned char *p_base;
|
|
748 int i_bytes;
|
|
749
|
|
750 for( i_index = i_blocks;
|
|
751 i_index;
|
|
752 i_index--, p_iovec++ )
|
|
753 {
|
|
754 i_len = p_iovec->iov_len;
|
|
755 p_base = p_iovec->iov_base;
|
|
756
|
|
757 if( i_len <= 0 )
|
|
758 {
|
|
759 continue;
|
|
760 }
|
|
761
|
|
762 i_bytes = read( dvdcss->i_fd, p_base, i_len );
|
|
763
|
|
764 if( i_bytes < 0 )
|
|
765 {
|
|
766 /* One of the reads failed, too bad.
|
|
767 * We won't even bother returning the reads that went ok,
|
|
768 * and as in the posix spec the file postition is left
|
|
769 * unspecified after a failure */
|
|
770 dvdcss->i_pos = -1;
|
|
771 return -1;
|
|
772 }
|
|
773
|
|
774 i_total += i_bytes;
|
|
775
|
|
776 if( i_bytes != i_len )
|
|
777 {
|
|
778 /* We reached the end of the file or a signal interrupted
|
|
779 * the read. Return a partial read. */
|
|
780 int i_seek;
|
|
781
|
|
782 dvdcss->i_pos = -1;
|
|
783 i_seek = libc_seek( dvdcss, i_total / DVDCSS_BLOCK_SIZE );
|
|
784 if( i_seek < 0 )
|
|
785 {
|
|
786 return i_seek;
|
|
787 }
|
|
788
|
|
789 /* We have to return now so that i_pos isn't clobbered */
|
|
790 return i_total / DVDCSS_BLOCK_SIZE;
|
|
791 }
|
|
792 }
|
|
793
|
|
794 dvdcss->i_pos += i_total / DVDCSS_BLOCK_SIZE;
|
|
795 return i_total / DVDCSS_BLOCK_SIZE;
|
|
796 #else
|
|
797 int i_read = readv( dvdcss->i_read_fd, p_iovec, i_blocks );
|
|
798
|
|
799 if( i_read < 0 )
|
|
800 {
|
|
801 dvdcss->i_pos = -1;
|
|
802 return i_read;
|
|
803 }
|
|
804
|
|
805 dvdcss->i_pos += i_read / DVDCSS_BLOCK_SIZE;
|
|
806 return i_read / DVDCSS_BLOCK_SIZE;
|
|
807 #endif
|
|
808 }
|
|
809
|
|
810 #if defined( WIN32 )
|
|
811 /*****************************************************************************
|
|
812 * win_readv: vectored read using ReadFile for Win2K and ASPI for win9x
|
|
813 *****************************************************************************/
|
|
814 static int win_readv ( dvdcss_t dvdcss, struct iovec *p_iovec, int i_blocks )
|
|
815 {
|
|
816 int i_index;
|
|
817 int i_blocks_read, i_blocks_total = 0;
|
|
818
|
|
819 /* Check the size of the readv temp buffer, just in case we need to
|
|
820 * realloc something bigger */
|
|
821 if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
|
|
822 {
|
|
823 dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;
|
|
824
|
|
825 if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );
|
|
826
|
|
827 /* Allocate a buffer which will be used as a temporary storage
|
|
828 * for readv */
|
|
829 dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
|
|
830 if( !dvdcss->p_readv_buffer )
|
|
831 {
|
|
832 print_error( dvdcss, " failed (readv)" );
|
|
833 dvdcss->i_pos = -1;
|
|
834 return -1;
|
|
835 }
|
|
836 }
|
|
837
|
|
838 for( i_index = i_blocks; i_index; i_index-- )
|
|
839 {
|
|
840 i_blocks_total += p_iovec[i_index-1].iov_len;
|
|
841 }
|
|
842
|
|
843 if( i_blocks_total <= 0 ) return 0;
|
|
844
|
|
845 i_blocks_total /= DVDCSS_BLOCK_SIZE;
|
|
846
|
|
847 if( WIN2K )
|
|
848 {
|
|
849 unsigned long int i_bytes;
|
|
850 if( !ReadFile( (HANDLE)dvdcss->i_fd, dvdcss->p_readv_buffer,
|
|
851 i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
|
|
852 {
|
|
853 /* The read failed... too bad.
|
|
854 * As in the posix spec the file postition is left
|
|
855 * unspecified after a failure */
|
|
856 dvdcss->i_pos = -1;
|
|
857 return -1;
|
|
858 }
|
|
859 i_blocks_read = i_bytes / DVDCSS_BLOCK_SIZE;
|
|
860 }
|
|
861 else /* Win9x */
|
|
862 {
|
|
863 i_blocks_read = aspi_read_internal( dvdcss->i_fd,
|
|
864 dvdcss->p_readv_buffer,
|
|
865 i_blocks_total );
|
|
866 if( i_blocks_read < 0 )
|
|
867 {
|
|
868 /* See above */
|
|
869 dvdcss->i_pos = -1;
|
|
870 return -1;
|
|
871 }
|
|
872 }
|
|
873
|
|
874 /* We just have to copy the content of the temp buffer into the iovecs */
|
|
875 for( i_index = 0, i_blocks_total = i_blocks_read;
|
|
876 i_blocks_total > 0;
|
|
877 i_index++ )
|
|
878 {
|
|
879 memcpy( p_iovec[i_index].iov_base,
|
|
880 dvdcss->p_readv_buffer + (i_blocks_read - i_blocks_total)
|
|
881 * DVDCSS_BLOCK_SIZE,
|
|
882 p_iovec[i_index].iov_len );
|
|
883 /* if we read less blocks than asked, we'll just end up copying
|
|
884 * garbage, this isn't an issue as we return the number of
|
|
885 * blocks actually read */
|
|
886 i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
|
|
887 }
|
|
888
|
|
889 dvdcss->i_pos += i_blocks_read;
|
|
890 return i_blocks_read;
|
|
891 }
|
|
892
|
|
893 static int aspi_read_internal( int i_fd, void *p_data, int i_blocks )
|
|
894 {
|
|
895 HANDLE hEvent;
|
|
896 struct SRB_ExecSCSICmd ssc;
|
|
897 struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
|
|
898
|
|
899 /* Create the transfer completion event */
|
|
900 hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
901 if( hEvent == NULL )
|
|
902 {
|
|
903 return -1;
|
|
904 }
|
|
905
|
|
906 memset( &ssc, 0, sizeof( ssc ) );
|
|
907
|
|
908 ssc.SRB_Cmd = SC_EXEC_SCSI_CMD;
|
|
909 ssc.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
|
|
910 ssc.SRB_HaId = LOBYTE( fd->i_sid );
|
|
911 ssc.SRB_Target = HIBYTE( fd->i_sid );
|
|
912 ssc.SRB_SenseLen = SENSE_LEN;
|
|
913
|
|
914 ssc.SRB_PostProc = (LPVOID) hEvent;
|
|
915 ssc.SRB_BufPointer = p_data;
|
|
916 ssc.SRB_CDBLen = 12;
|
|
917
|
|
918 ssc.CDBByte[0] = 0xA8; /* RAW */
|
|
919 ssc.CDBByte[2] = (UCHAR) (fd->i_blocks >> 24);
|
|
920 ssc.CDBByte[3] = (UCHAR) (fd->i_blocks >> 16) & 0xff;
|
|
921 ssc.CDBByte[4] = (UCHAR) (fd->i_blocks >> 8) & 0xff;
|
|
922 ssc.CDBByte[5] = (UCHAR) (fd->i_blocks) & 0xff;
|
|
923
|
|
924 /* We have to break down the reads into 64kb pieces (ASPI restriction) */
|
|
925 if( i_blocks > 32 )
|
|
926 {
|
|
927 ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
|
|
928 ssc.CDBByte[9] = 32;
|
|
929 fd->i_blocks += 32;
|
|
930
|
|
931 /* Initiate transfer */
|
|
932 ResetEvent( hEvent );
|
|
933 fd->lpSendCommand( (void*) &ssc );
|
|
934
|
|
935 /* transfer the next 64kb (aspi_read_internal is called recursively)
|
|
936 * We need to check the status of the read on return */
|
|
937 if( aspi_read_internal( i_fd,
|
|
938 (uint8_t*) p_data + 32 * DVDCSS_BLOCK_SIZE,
|
|
939 i_blocks - 32) < 0 )
|
|
940 {
|
|
941 return -1;
|
|
942 }
|
|
943 }
|
|
944 else
|
|
945 {
|
|
946 /* This is the last transfer */
|
|
947 ssc.SRB_BufLen = i_blocks * DVDCSS_BLOCK_SIZE;
|
|
948 ssc.CDBByte[9] = (UCHAR) i_blocks;
|
|
949 fd->i_blocks += i_blocks;
|
|
950
|
|
951 /* Initiate transfer */
|
|
952 ResetEvent( hEvent );
|
|
953 fd->lpSendCommand( (void*) &ssc );
|
|
954
|
|
955 }
|
|
956
|
|
957 /* If the command has still not been processed, wait until it's finished */
|
|
958 if( ssc.SRB_Status == SS_PENDING )
|
|
959 {
|
|
960 WaitForSingleObject( hEvent, INFINITE );
|
|
961 }
|
|
962 CloseHandle( hEvent );
|
|
963
|
|
964 /* check that the transfer went as planned */
|
|
965 if( ssc.SRB_Status != SS_COMP )
|
|
966 {
|
|
967 return -1;
|
|
968 }
|
|
969
|
|
970 return i_blocks;
|
|
971 }
|
|
972 #endif
|
|
973
|