Mercurial > mplayer.hg
annotate libdvdcss/device.c @ 30811:50e0f6942e43
Implement Win32 mutexes.
Implement Win32 mutexes; they used to just be mapped on top of events, which
is not the same thing at all.
The implementation is pretty much the obvious one, similar to the
current critical section implementation and the semaphore implementation;
a single lock count protected by a pthread mutex, and an event lockers can
sleep on to know when the mutex is available.
Also make CreateMutexA and ReleaseMutex available even if QuickTime codecs
support is not configured.
author | sesse |
---|---|
date | Sat, 06 Mar 2010 10:13:37 +0000 |
parents | afa2cc0166be |
children | 9e9595c779cf |
rev | line source |
---|---|
20613 | 1 /***************************************************************************** |
2 * device.h: DVD device access | |
3 ***************************************************************************** | |
4 * Copyright (C) 1998-2006 VideoLAN | |
5 * $Id$ | |
6 * | |
27442 | 7 * Authors: Stéphane Borel <stef@via.ecp.fr> |
20613 | 8 * Sam Hocevar <sam@zoy.org> |
27442 | 9 * Håkan Hjort <d95hjort@dtek.chalmers.se> |
20613 | 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; | |
20730
66c2f233ccff
Fix linking on Cygwin and remove nonsense lseek64 --> lseek indirection,
diego
parents:
20613
diff
changeset
|
595 i_seek = lseek( dvdcss->i_read_fd, i_seek, SEEK_SET ); |
20613 | 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 |