]> git.sesse.net Git - vlc/blob - plugins/dvd/dvd_ioctl.c
bcf0d7a70461689f1f52499717d4c1d4f3d80a2f
[vlc] / plugins / dvd / dvd_ioctl.c
1 /*****************************************************************************
2  * dvd_ioctl.c: DVD ioctl replacement function
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id: dvd_ioctl.c,v 1.3 2001/03/05 11:53:44 sam Exp $
6  *
7  * Authors: Markus Kuespert <ltlBeBoy@beosmail.com>
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 <sys/types.h>
31 #include <netinet/in.h>
32 #ifdef HAVE_SYS_IOCTL_H
33 #   include <sys/ioctl.h>
34 #endif
35 #ifdef HAVE_SYS_DVDIO_H
36 #   include <sys/dvdio.h>
37 #endif
38 #ifdef LINUX_DVD
39 #   include <linux/cdrom.h>
40 #endif
41 #ifdef SYS_BEOS
42 #   include <malloc.h>
43 #   include <scsi.h>
44 #endif
45
46 #include "common.h"
47 #include "intf_msg.h"
48
49 #include "dvd_ioctl.h"
50
51 /*****************************************************************************
52  * Local prototypes - BeOS specific
53  *****************************************************************************/
54 #if defined( SYS_BEOS )
55 static int  ReadData          ( int i_fd, dvd_struct *p_dvd );
56 static int  ReadCopyright     ( int i_fd, dvd_struct *p_dvd );
57 static int  ReadKey           ( int i_fd, dvd_struct *p_dvd );
58 static int  ReadBCA           ( int i_fd, dvd_struct *p_dvd );
59 static int  ReadManufacturer  ( int i_fd, dvd_struct *p_dvd );
60
61 static void InitGenericCommand( struct cdrom_generic_command *p_cgc,
62                                 void *buf, int i_len, int i_type );
63 static void InitReadCommand   ( struct cdrom_generic_command *p_cgc,
64                                 unsigned i_agid, unsigned i_type );
65 static void InitWriteCommand  ( struct cdrom_generic_command *p_cgc,
66                                 unsigned i_agid, unsigned i_type );
67
68 static int  SendCommand ( int i_fd, struct cdrom_generic_command *p_cgc );
69 #endif
70
71 /*****************************************************************************
72  * dvd_ioctl: DVD ioctl() wrapper
73  *****************************************************************************
74  * Since the DVD ioctls do not exist on every machine, we provide this wrapper
75  * so that it becomes easier to port them to any architecture.
76  *****************************************************************************/
77 int dvd_ioctl( int i_fd, unsigned long i_op, void *p_arg )
78 {
79 #if defined( HAVE_SYS_DVDIO_H ) || defined( LINUX_DVD )
80     return( ioctl( i_fd, i_op, p_arg ) );
81
82 #elif defined( SYS_BEOS )
83
84     int           i_ret;
85     unsigned char buf[20];
86
87     struct cdrom_generic_command p_cgc;
88
89     dvd_struct *p_dvd = (dvd_struct *)p_arg;
90     dvd_authinfo *p_authinfo = (dvd_authinfo *)p_arg;
91
92     switch ( i_op )
93     {
94         case DVD_AUTH: /* Request type is "authentication" */
95         {
96             memset( buf, 0, sizeof( buf ) );
97             InitGenericCommand( &p_cgc, buf, 0, CGC_DATA_READ );
98
99             switch( p_authinfo->type )
100             {
101                 case DVD_LU_SEND_AGID: /* LU data send */
102
103                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_AGID" );
104
105                     InitReadCommand( &p_cgc, p_authinfo->lsa.agid, 0 );
106
107                     i_ret = SendCommand( i_fd, &p_cgc );
108
109                     p_authinfo->lsa.agid = buf[7] >> 6;
110
111                     return i_ret;
112
113                 case DVD_LU_SEND_KEY1:
114
115                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_KEY1" );
116
117                     InitReadCommand( &p_cgc, p_authinfo->lsk.agid, 2 );
118
119                     i_ret = SendCommand( i_fd, &p_cgc );
120
121                     /* Copy the key */
122                     memcpy( p_authinfo->lsk.key, &buf[4], sizeof(dvd_key) );
123
124                     return i_ret;
125
126                 case DVD_LU_SEND_CHALLENGE:
127
128                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_CHALLENGE" );
129
130                     InitReadCommand( &p_cgc, p_authinfo->lsc.agid, 1 );
131
132                     i_ret = SendCommand( i_fd, &p_cgc );
133
134                     /* Copy the challenge */
135                     memcpy( p_authinfo->lsc.chal, &buf[4],
136                             sizeof(dvd_challenge) );
137
138                     return i_ret;
139
140                 case DVD_LU_SEND_TITLE_KEY: /* Post-auth key */
141
142                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_TITLE_KEY" );
143
144                     InitReadCommand( &p_cgc, p_authinfo->lstk.agid, 4 );
145
146                     p_cgc.cmd[5] = p_authinfo->lstk.lba;
147                     p_cgc.cmd[4] = p_authinfo->lstk.lba >> 8;
148                     p_cgc.cmd[3] = p_authinfo->lstk.lba >> 16;
149                     p_cgc.cmd[2] = p_authinfo->lstk.lba >> 24;
150
151                     i_ret = SendCommand( i_fd, &p_cgc );
152
153                     p_authinfo->lstk.cpm = (buf[4] >> 7) & 1;
154                     p_authinfo->lstk.cp_sec = (buf[4] >> 6) & 1;
155                     p_authinfo->lstk.cgms = (buf[4] >> 4) & 3;
156
157                     /* Copy the key */
158                     memcpy( p_authinfo->lstk.title_key, &buf[5],
159                             sizeof(dvd_key) );
160
161                     return i_ret;
162
163                 case DVD_LU_SEND_ASF:
164
165                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_ASF" );
166
167                     InitReadCommand( &p_cgc, p_authinfo->lsasf.agid, 5 );
168
169                     i_ret = SendCommand( i_fd, &p_cgc );
170
171                     p_authinfo->lsasf.asf = buf[7] & 1;
172
173                     return i_ret;
174
175                 case DVD_HOST_SEND_CHALLENGE: /* LU data receive */
176
177                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_CHALLENGE" );
178
179                     InitWriteCommand( &p_cgc, p_authinfo->hsc.agid, 1 );
180                     buf[1] = 0xe;
181
182                     /* Copy the challenge */
183                     memcpy( &buf[4], p_authinfo->hsc.chal,
184                             sizeof(dvd_challenge) );
185
186                     if( (i_ret = SendCommand(i_fd, &p_cgc)) )
187                     {
188                         return i_ret;
189                     }
190
191                     p_authinfo->type = DVD_LU_SEND_KEY1;
192
193                     return 0;
194
195                 case DVD_HOST_SEND_KEY2:
196
197                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_KEY2" );
198
199                     InitWriteCommand( &p_cgc, p_authinfo->hsk.agid, 3 );
200                     buf[1] = 0xa;
201
202                     /* Copy the key */
203                     memcpy( &buf[4], p_authinfo->hsk.key, sizeof(dvd_key) );
204
205                     if( (i_ret = SendCommand(i_fd, &p_cgc)) )
206                     {
207                         p_authinfo->type = DVD_AUTH_FAILURE;
208                         return i_ret;
209                     }
210
211                     p_authinfo->type = DVD_AUTH_ESTABLISHED;
212
213                     return 0;
214
215                 case DVD_INVALIDATE_AGID: /* Misc */
216
217                     intf_WarnMsg( 2, "css DoAuth: DVD_INVALIDATE_AGID" );
218
219                     InitReadCommand( &p_cgc, p_authinfo->lsa.agid, 0x3f );
220
221                     return SendCommand( i_fd, &p_cgc );
222
223                 case DVD_LU_SEND_RPC_STATE: /* Get region settings */
224
225                     intf_WarnMsg( 2, "css DoAuth: DVD_LU_SEND_RPC_STATE "
226                                      "(unimplemented)" );
227
228         #if 0
229                     p_dvdetup_report_key( &p_cgc, 0, 8 );
230                     memset( &rpc_state, 0, sizeof(rpc_state_t) );
231                     p_cgc.buffer = (char *) &rpc_state;
232
233                     if( (i_ret = SendCommand(i_fd, &p_cgc)) )
234                     {
235                         return i_ret;
236                     }
237
238                     p_authinfo->lrpcs.type = rpc_state.type_code;
239                     p_authinfo->lrpcs.vra = rpc_state.vra;
240                     p_authinfo->lrpcs.ucca = rpc_state.ucca;
241                     p_authinfo->lrpcs.region_mask = rpc_state.region_mask;
242                     p_authinfo->lrpcs.rpc_scheme = rpc_state.rpc_scheme;
243         #endif
244
245                     return 0;
246
247                 case DVD_HOST_SEND_RPC_STATE: /* Set region settings */
248
249                     intf_WarnMsg( 2, "css DoAuth: DVD_HOST_SEND_RPC_STATE" );
250
251                     InitWriteCommand( &p_cgc, 0, 6 );
252                     buf[1] = 6;
253                     buf[4] = p_authinfo->hrpcs.pdrc;
254
255                     return SendCommand( i_fd, &p_cgc );
256
257                 default:
258                     intf_ErrMsg( "css DoAuth: invalid DVD key ioctl" );
259                     return -1;
260
261             }
262         }
263
264         case DVD_READ_STRUCT: /* Request type is "read structure" */
265         {
266             switch( p_dvd->type )
267             {
268                 case DVD_STRUCT_PHYSICAL:
269
270                     intf_WarnMsg( 2, "css ReadStruct: DVD_STRUCT_PHYSICAL" );
271
272                     return ReadData( i_fd, p_dvd );
273
274                 case DVD_STRUCT_COPYRIGHT:
275
276                     intf_WarnMsg( 2, "css ReadStruct: DVD_STRUCT_COPYRIGHT" );
277
278                     return ReadCopyright( i_fd, p_dvd );
279
280                 case DVD_STRUCT_DISCKEY:
281
282                     intf_WarnMsg( 2, "css ReadStruct: DVD_STRUCT_DISCKEY" );
283
284                     return ReadKey( i_fd, p_dvd );
285
286                 case DVD_STRUCT_BCA:
287
288                     intf_WarnMsg( 2, "css ReadStruct: DVD_STRUCT_BCA" );
289
290                     return ReadBCA( i_fd, p_dvd );
291
292                 case DVD_STRUCT_MANUFACT:
293
294                     intf_WarnMsg( 2, "css ReadStruct: DVD_STRUCT_MANUFACT" );
295
296                     return ReadManufacturer( i_fd, p_dvd );
297
298                 default:
299                     intf_WarnMsg( 2, "css ReadStruct: invalid request (%d)",
300                                   p_dvd->type );
301
302                     return -1;
303             }
304         }
305
306         default: /* Unknown request type */
307         {
308             intf_ErrMsg( "css error: unknown command 0x%x", i_op );
309             return -1;
310         }
311     }
312 #else
313
314     return -1;
315 #endif
316 }
317
318 /* Local prototypes */
319
320 #if defined( SYS_BEOS )
321 /*****************************************************************************
322  * ReadData: Get data structure information from the DVD.
323  *****************************************************************************/
324 static int ReadData( int i_fd, dvd_struct *p_dvd )
325 {
326     int i_ret, i;
327     u_char buf[4 + 4 * 20], *base;
328     struct dvd_layer *layer;
329     struct cdrom_generic_command cgc;
330
331     InitGenericCommand( &cgc, buf, sizeof(buf), CGC_DATA_READ );
332
333     cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
334     cgc.cmd[6] = p_dvd->physical.layer_num;
335     cgc.cmd[7] = p_dvd->type;
336     cgc.cmd[9] = cgc.buflen & 0xff;
337
338     if( (i_ret = SendCommand(i_fd, &cgc)) )
339     {
340         return i_ret;
341     }
342
343     base = &buf[4];
344     layer = &p_dvd->physical.layer[0];
345
346     /* place the data... really ugly, but at least we won't have to
347      * worry about endianess in userspace or here. */
348     for( i = 0; i < 4; ++i, base += 20, ++layer )
349     {
350         memset( layer, 0, sizeof(*layer) );
351
352         layer->book_version = base[0] & 0xf;
353         layer->book_type = base[0] >> 4;
354         layer->min_rate = base[1] & 0xf;
355         layer->disc_size = base[1] >> 4;
356         layer->layer_type = base[2] & 0xf;
357         layer->track_path = (base[2] >> 4) & 1;
358         layer->nlayers = (base[2] >> 5) & 3;
359         layer->track_density = base[3] & 0xf;
360         layer->linear_density = base[3] >> 4;
361         layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
362         layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
363         layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
364         layer->bca = base[16] >> 7;
365     }
366
367     return 0;
368 }
369
370 /*****************************************************************************
371  * ReadCopyright: get copyright information from the DVD.
372  *****************************************************************************/
373 static int ReadCopyright( int i_fd, dvd_struct *p_dvd )
374 {
375     int i_ret;
376     u_char buf[8];
377     struct cdrom_generic_command cgc;
378
379     InitGenericCommand( &cgc, buf, sizeof(buf), CGC_DATA_READ );
380
381     cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
382     cgc.cmd[6] = p_dvd->copyright.layer_num;
383     cgc.cmd[7] = p_dvd->type;
384     cgc.cmd[8] = cgc.buflen >> 8;
385     cgc.cmd[9] = cgc.buflen & 0xff;
386
387     if( (i_ret = SendCommand(i_fd, &cgc)) )
388     {
389         return i_ret;
390     }
391
392     p_dvd->copyright.cpst = buf[4];
393     p_dvd->copyright.rmi = buf[5];
394
395     return 0;
396 }
397
398 /*****************************************************************************
399  * ReadKey: get a key from the DVD.
400  *****************************************************************************/
401 static int ReadKey( int i_fd, dvd_struct *p_dvd )
402 {
403     int i_ret, size;
404     u_char *buf;
405     struct cdrom_generic_command cgc;
406
407     size = sizeof( p_dvd->disckey.value ) + 4;
408
409 #if 0
410     if ((buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL)
411     {
412         return -ENOMEM;
413     }
414 #endif
415     buf = (u_char *) malloc( size );
416
417     InitGenericCommand( &cgc, buf, size, CGC_DATA_READ );
418
419     cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
420     cgc.cmd[7] = p_dvd->type;
421     cgc.cmd[8] = size >> 8;
422     cgc.cmd[9] = size & 0xff;
423     cgc.cmd[10] = p_dvd->disckey.agid << 6;
424
425     if( !(i_ret = SendCommand(i_fd, &cgc)) )
426     {
427         memcpy( p_dvd->disckey.value, &buf[4], sizeof(p_dvd->disckey.value) );
428     }
429
430     free( buf );
431     return i_ret;
432 }
433
434 /*****************************************************************************
435  * ReadBCA: read the Burst Cutting Area of a DVD.
436  *****************************************************************************
437  * The BCA is a special part of the DVD which is used to burn additional
438  * data after it has been manufactured. DIVX is an exemple.
439  *****************************************************************************/
440 static int ReadBCA( int i_fd, dvd_struct *p_dvd )
441 {
442     int i_ret;
443     u_char buf[4 + 188];
444     struct cdrom_generic_command cgc;
445
446     InitGenericCommand( &cgc, buf, sizeof(buf), CGC_DATA_READ );
447
448     cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
449     cgc.cmd[7] = p_dvd->type;
450     cgc.cmd[9] = cgc.buflen = 0xff;
451
452     if( (i_ret = SendCommand(i_fd, &cgc)) )
453     {
454         return i_ret;
455     }
456
457     p_dvd->bca.len = buf[0] << 8 | buf[1];
458     if( p_dvd->bca.len < 12 || p_dvd->bca.len > 188 )
459     {
460         intf_ErrMsg( "css error: invalid BCA length (%d)", p_dvd->bca.len );
461         return -1;
462     }
463
464     memcpy( p_dvd->bca.value, &buf[4], p_dvd->bca.len );
465
466     return 0;
467 }
468
469 /*****************************************************************************
470  * ReadManufacturer: get manufacturer information from the DVD.
471  *****************************************************************************/
472 static int ReadManufacturer( int i_fd, dvd_struct *p_dvd )
473 {
474     int i_ret = 0, size;
475     u_char *buf;
476     struct cdrom_generic_command cgc;
477
478     size = sizeof( p_dvd->manufact.value ) + 4;
479
480 #if 0
481     if( (buf = (u_char *) kmalloc(size, GFP_KERNEL)) == NULL )
482     {
483         return -ENOMEM;
484     }
485 #endif
486     buf = (u_char *) malloc(size);
487
488     InitGenericCommand( &cgc, buf, size, CGC_DATA_READ );
489
490     cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
491     cgc.cmd[7] = p_dvd->type;
492     cgc.cmd[8] = size >> 8;
493     cgc.cmd[9] = size & 0xff;
494
495     if( (i_ret = SendCommand(i_fd, &cgc)) )
496     {
497         return i_ret;
498     }
499
500     p_dvd->manufact.len = buf[0] << 8 | buf[1];
501     if( p_dvd->manufact.len < 0 || p_dvd->manufact.len > 2048 )
502     {
503         intf_ErrMsg( "css error: invalid manufacturer info length (%d)",
504                      p_dvd->bca.len );
505         i_ret = -1;
506     }
507     else
508     {
509         memcpy( p_dvd->manufact.value, &buf[4], p_dvd->manufact.len );
510     }
511
512     free( buf );
513     return i_ret;
514 }
515
516 /*****************************************************************************
517  * InitGenericCommand: initialize a CGC structure
518  *****************************************************************************
519  * This function initializes a CDRom Generic Command structure for
520  * future use, either a read command or a write command.
521  *****************************************************************************/
522 static void InitGenericCommand( struct cdrom_generic_command *p_cgc,
523                                 void *buf, int i_len, int i_type )
524 {
525     memset( p_cgc, 0, sizeof( struct cdrom_generic_command ) );
526
527     if( buf != NULL )
528     {
529         memset( buf, 0, i_len );
530     }
531
532     p_cgc->buffer = ( char * )buf;
533     p_cgc->buflen = i_len;
534     p_cgc->data_direction = i_type;
535     p_cgc->timeout = 255;
536 }
537
538 /*****************************************************************************
539  * InitReadCommand: fill a CGC structure for reading purposes.
540  *****************************************************************************
541  * This function fills a CDRom Generic Command for a command which will
542  * read data from the DVD.
543  *****************************************************************************/
544 static void InitReadCommand( struct cdrom_generic_command *p_cgc,
545                              unsigned i_agid, unsigned i_type )
546 {
547     p_cgc->cmd[0] = GPCMD_REPORT_KEY;
548     p_cgc->cmd[10] = i_type | (i_agid << 6);
549
550     /* FIXME: check what i_type means */
551     switch( i_type )
552     {
553         case 0:
554         case 8:
555         case 5:
556             p_cgc->buflen = 8;
557             break;
558
559         case 1:
560             p_cgc->buflen = 16;
561             break;
562
563         case 2:
564         case 4:
565             p_cgc->buflen = 12;
566             break;
567     }
568
569     p_cgc->cmd[9] = p_cgc->buflen;
570     p_cgc->data_direction = CGC_DATA_READ;
571 }
572
573 /*****************************************************************************
574  * InitWriteCommand: fill a CGC structure for writing purposes.
575  *****************************************************************************
576  * This function fills a CDRom Generic Command for a command which will
577  * send data to the DVD.
578  *****************************************************************************/
579 static void InitWriteCommand( struct cdrom_generic_command *p_cgc,
580                               unsigned i_agid, unsigned i_type )
581 {
582     p_cgc->cmd[0] = GPCMD_SEND_KEY;
583     p_cgc->cmd[10] = i_type | (i_agid << 6);
584
585     /* FIXME: check what i_type means */
586     switch( i_type )
587     {
588         case 1:
589             p_cgc->buflen = 16;
590             break;
591
592         case 3:
593             p_cgc->buflen = 12;
594             break;
595
596         case 6:
597             p_cgc->buflen = 8;
598             break;
599     }
600
601     p_cgc->cmd[9] = p_cgc->buflen;
602     p_cgc->data_direction = CGC_DATA_WRITE;
603 }
604
605 /*****************************************************************************
606  * SendCommand: send a raw device command to the DVD drive.
607  *****************************************************************************
608  * This is the most important part of the ioctl emulation, the place where
609  * data is really sent to the DVD.
610  *****************************************************************************/
611 static int SendCommand( int i_fd, struct cdrom_generic_command *p_cgc )
612 {
613     int i;
614
615     raw_device_command rdc;
616     memset( &rdc, 0, sizeof( rdc ) );
617
618     /* fill out our raw device command data */
619     rdc.data = p_cgc->buffer;
620     rdc.data_length = p_cgc->buflen;
621     rdc.sense_data = p_cgc->sense;
622     rdc.sense_data_length = 0;
623     rdc.timeout = 1000000;
624
625     if( p_cgc->data_direction == CGC_DATA_READ )
626     {
627         intf_WarnMsg( 2, "css: data_direction == CGC_DATA_READ" );
628         rdc.flags = B_RAW_DEVICE_DATA_IN;
629     }
630
631     rdc.command_length = 12;
632
633     /* FIXME: check if this _really_ should go up to [12] */
634     for( i = 0 ; i < 13 ; i++ )
635     {
636         rdc.command[i] = p_cgc->cmd[i];
637     }
638
639     return ioctl( i_fd, B_RAW_DEVICE_COMMAND, &rdc, sizeof(rdc) );
640 }
641 #endif
642