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