1/*
2 * Copyright (c) 1998-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/errno.h>
25#include <sys/proc.h>
26#include <IOKit/storage/IODVDMediaBSDClient.h>
27
28#define super IOMediaBSDClient
29OSDefineMetaClassAndStructors(IODVDMediaBSDClient, IOMediaBSDClient)
30
31typedef struct
32{
33    uint8_t       format;
34
35    uint8_t       reserved0008[3];
36
37    uint32_t      address;
38    uint8_t       grantID;
39    uint8_t       layer;
40
41    uint16_t      bufferLength;
42    user32_addr_t buffer;
43} dk_dvd_read_structure_32_t;
44
45typedef struct
46{
47    uint8_t       format;
48
49    uint8_t       reserved0008[3];
50
51    uint32_t      address;
52    uint8_t       grantID;
53    uint8_t       layer;
54
55    uint8_t       reserved0080[4];
56
57    uint16_t      bufferLength;
58    user64_addr_t buffer;
59} dk_dvd_read_structure_64_t;
60
61typedef struct
62{
63    uint8_t       format;
64    uint8_t       keyClass;
65
66    uint8_t       reserved0016[2];
67
68    uint32_t      address;
69    uint8_t       grantID;
70
71    uint8_t       reserved0072[1];
72
73    uint16_t      bufferLength;
74    user32_addr_t buffer;
75} dk_dvd_report_key_32_t;
76
77typedef struct
78{
79    uint8_t       format;
80    uint8_t       keyClass;
81
82    uint8_t       reserved0016[2];
83
84    uint32_t      address;
85    uint8_t       grantID;
86
87    uint8_t       reserved0072[5];
88
89    uint16_t      bufferLength;
90    user64_addr_t buffer;
91} dk_dvd_report_key_64_t;
92
93typedef struct
94{
95    uint8_t       format;
96    uint8_t       keyClass;
97
98    uint8_t       reserved0016[6];
99
100    uint8_t       grantID;
101
102    uint8_t       reserved0072[1];
103
104    uint16_t      bufferLength;
105    user32_addr_t buffer;
106} dk_dvd_send_key_32_t;
107
108typedef struct
109{
110    uint8_t       format;
111    uint8_t       keyClass;
112
113    uint8_t       reserved0016[6];
114
115    uint8_t       grantID;
116
117    uint8_t       reserved0072[5];
118
119    uint16_t      bufferLength;
120    user64_addr_t buffer;
121} dk_dvd_send_key_64_t;
122
123typedef struct
124{
125    uint8_t       reserved0000[10];
126
127    uint16_t      bufferLength;
128    user32_addr_t buffer;
129} dk_dvd_read_disc_info_32_t;
130
131typedef struct
132{
133    uint8_t       reserved0000[14];
134
135    uint16_t      bufferLength;
136    user64_addr_t buffer;
137} dk_dvd_read_disc_info_64_t;
138
139typedef struct
140{
141    uint8_t       reserved0000[4];
142
143    uint32_t      address;
144    uint8_t       addressType;
145
146    uint8_t       reserved0072[1];
147
148    uint16_t      bufferLength;
149    user32_addr_t buffer;
150} dk_dvd_read_rzone_info_32_t;
151
152typedef struct
153{
154    uint8_t       reserved0000[4];
155
156    uint32_t      address;
157    uint8_t       addressType;
158
159    uint8_t       reserved0072[5];
160
161    uint16_t      bufferLength;
162    user64_addr_t buffer;
163} dk_dvd_read_rzone_info_64_t;
164
165#define DKIOCDVDREADSTRUCTURE32 _IOW('d', 128, dk_dvd_read_structure_32_t)
166#define DKIOCDVDREADSTRUCTURE64 _IOW('d', 128, dk_dvd_read_structure_64_t)
167#define DKIOCDVDREPORTKEY32     _IOW('d', 129, dk_dvd_report_key_32_t)
168#define DKIOCDVDREPORTKEY64     _IOW('d', 129, dk_dvd_report_key_64_t)
169#define DKIOCDVDSENDKEY32       _IOW('d', 130, dk_dvd_send_key_32_t)
170#define DKIOCDVDSENDKEY64       _IOW('d', 130, dk_dvd_send_key_64_t)
171
172#define DKIOCDVDREADDISCINFO32  _IOWR('d', 132, dk_dvd_read_disc_info_32_t)
173#define DKIOCDVDREADDISCINFO64  _IOWR('d', 132, dk_dvd_read_disc_info_64_t)
174#define DKIOCDVDREADRZONEINFO32 _IOWR('d', 133, dk_dvd_read_rzone_info_32_t)
175#define DKIOCDVDREADRZONEINFO64 _IOWR('d', 133, dk_dvd_read_rzone_info_64_t)
176
177static bool DKIOC_IS_RESERVED(caddr_t data, uint32_t reserved)
178{
179    UInt32 index;
180
181    for ( index = 0; index < sizeof(reserved) * 8; index++, reserved >>= 1 )
182    {
183        if ( (reserved & 1) )
184        {
185            if ( data[index] )  return true;
186        }
187    }
188
189    return false;
190}
191
192static IOMemoryDescriptor * DKIOC_PREPARE_BUFFER( user_addr_t address,
193                                                  UInt32      length,
194                                                  IODirection direction,
195                                                  proc_t      proc )
196{
197    IOMemoryDescriptor * buffer = 0;
198
199    if ( address && length )
200    {
201        buffer = IOMemoryDescriptor::withAddressRange(    // (create the buffer)
202            /* address */ address,
203            /* length  */ length,
204            /* options */ direction,
205            /* task    */ (proc == kernproc) ? kernel_task : current_task() );
206    }
207
208    if ( buffer )
209    {
210        if ( buffer->prepare() != kIOReturnSuccess )     // (prepare the buffer)
211        {
212            buffer->release();
213            buffer = 0;
214        }
215    }
216
217    return buffer;
218}
219
220static void DKIOC_COMPLETE_BUFFER(IOMemoryDescriptor * buffer)
221{
222    if ( buffer )
223    {
224        buffer->complete();                             // (complete the buffer)
225        buffer->release();                               // (release the buffer)
226    }
227}
228
229IODVDMedia * IODVDMediaBSDClient::getProvider() const
230{
231    //
232    // Obtain this object's provider.   We override the superclass's method
233    // to return a more specific subclass of IOService -- IODVDMedia.  This
234    // method serves simply as a convenience to subclass developers.
235    //
236
237    return (IODVDMedia *) IOService::getProvider();
238}
239
240int IODVDMediaBSDClient::ioctl( dev_t   dev,
241                                u_long  cmd,
242                                caddr_t data,
243                                int     flags,
244                                proc_t  proc )
245{
246    //
247    // Process a DVD-specific ioctl.
248    //
249
250    IOMemoryDescriptor * buffer = 0;
251    int                  error  = 0;
252    IOReturn             status = kIOReturnSuccess;
253
254    switch ( cmd )
255    {
256        case DKIOCDVDREADSTRUCTURE32:          // (dk_dvd_read_structure_32_t *)
257        {
258            dk_dvd_read_structure_32_t * request;
259
260            request = (dk_dvd_read_structure_32_t *) data;
261
262            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
263
264            if ( DKIOC_IS_RESERVED(data, 0xE) )  { error = EINVAL;  break; }
265
266            buffer = DKIOC_PREPARE_BUFFER(
267                       /* address   */ request->buffer,
268                       /* length    */ request->bufferLength,
269                       /* direction */ kIODirectionIn,
270                       /* proc      */ proc );
271
272            status = getProvider()->readStructure(
273                       /* buffer    */                      buffer,
274                       /* format    */ (DVDStructureFormat) request->format,
275                       /* address   */                      request->address,
276                       /* layer     */                      request->layer,
277                       /* grantID   */                      request->grantID );
278
279            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
280
281            DKIOC_COMPLETE_BUFFER(buffer);
282
283        } break;
284
285        case DKIOCDVDREADSTRUCTURE64:          // (dk_dvd_read_structure_64_t *)
286        {
287            dk_dvd_read_structure_64_t * request;
288
289            request = (dk_dvd_read_structure_64_t *) data;
290
291            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
292
293            if ( DKIOC_IS_RESERVED(data, 0x3C0E) )  { error = EINVAL;  break; }
294
295            buffer = DKIOC_PREPARE_BUFFER(
296                       /* address   */ request->buffer,
297                       /* length    */ request->bufferLength,
298                       /* direction */ kIODirectionIn,
299                       /* proc      */ proc );
300
301            status = getProvider()->readStructure(
302                       /* buffer    */                      buffer,
303                       /* format    */ (DVDStructureFormat) request->format,
304                       /* address   */                      request->address,
305                       /* layer     */                      request->layer,
306                       /* grantID   */                      request->grantID );
307
308            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
309
310            DKIOC_COMPLETE_BUFFER(buffer);
311
312        } break;
313
314        case DKIOCDVDREPORTKEY32:                  // (dk_dvd_report_key_32_t *)
315        {
316            dk_dvd_report_key_32_t * request = (dk_dvd_report_key_32_t *) data;
317
318            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
319
320            if ( DKIOC_IS_RESERVED(data, 0x20C) )  { error = EINVAL;  break; }
321
322            buffer = DKIOC_PREPARE_BUFFER(
323                       /* address   */ request->buffer,
324                       /* length    */ request->bufferLength,
325                       /* direction */ kIODirectionIn,
326                       /* proc      */ proc );
327
328            status = getProvider()->reportKey(
329                       /* buffer    */                buffer,
330                       /* keyClass  */ (DVDKeyClass)  request->keyClass,
331                       /* address   */                request->address,
332                       /* grantID   */                request->grantID,
333                       /* format    */ (DVDKeyFormat) request->format );
334
335            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
336
337            DKIOC_COMPLETE_BUFFER(buffer);
338
339        } break;
340
341        case DKIOCDVDREPORTKEY64:                  // (dk_dvd_report_key_64_t *)
342        {
343            dk_dvd_report_key_64_t * request = (dk_dvd_report_key_64_t *) data;
344
345            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
346
347            if ( DKIOC_IS_RESERVED(data, 0x3E0C) )  { error = EINVAL;  break; }
348
349            buffer = DKIOC_PREPARE_BUFFER(
350                       /* address   */ request->buffer,
351                       /* length    */ request->bufferLength,
352                       /* direction */ kIODirectionIn,
353                       /* proc      */ proc );
354
355            status = getProvider()->reportKey(
356                       /* buffer    */                buffer,
357                       /* keyClass  */ (DVDKeyClass)  request->keyClass,
358                       /* address   */                request->address,
359                       /* grantID   */                request->grantID,
360                       /* format    */ (DVDKeyFormat) request->format );
361
362            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
363
364            DKIOC_COMPLETE_BUFFER(buffer);
365
366        } break;
367
368        case DKIOCDVDSENDKEY32:                      // (dk_dvd_send_key_32_t *)
369        {
370            dk_dvd_send_key_32_t * request = (dk_dvd_send_key_32_t *) data;
371
372            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
373
374            if ( DKIOC_IS_RESERVED(data, 0x2FC) )  { error = EINVAL;  break; }
375
376            buffer = DKIOC_PREPARE_BUFFER(
377                       /* address   */ request->buffer,
378                       /* length    */ request->bufferLength,
379                       /* direction */ kIODirectionOut,
380                       /* proc      */ proc );
381
382            status = getProvider()->sendKey(
383                       /* buffer    */                buffer,
384                       /* keyClass  */ (DVDKeyClass)  request->keyClass,
385                       /* grantID   */                request->grantID,
386                       /* format    */ (DVDKeyFormat) request->format );
387
388            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
389
390            DKIOC_COMPLETE_BUFFER(buffer);
391
392        } break;
393
394        case DKIOCDVDSENDKEY64:                      // (dk_dvd_send_key_64_t *)
395        {
396            dk_dvd_send_key_64_t * request = (dk_dvd_send_key_64_t *) data;
397
398            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
399
400            if ( DKIOC_IS_RESERVED(data, 0x3EFC) )  { error = EINVAL;  break; }
401
402            buffer = DKIOC_PREPARE_BUFFER(
403                       /* address   */ request->buffer,
404                       /* length    */ request->bufferLength,
405                       /* direction */ kIODirectionOut,
406                       /* proc      */ proc );
407
408            status = getProvider()->sendKey(
409                       /* buffer    */                buffer,
410                       /* keyClass  */ (DVDKeyClass)  request->keyClass,
411                       /* grantID   */                request->grantID,
412                       /* format    */ (DVDKeyFormat) request->format );
413
414            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
415
416            DKIOC_COMPLETE_BUFFER(buffer);
417
418        } break;
419
420        case DKIOCDVDGETSPEED:                                   // (uint16_t *)
421        {
422            status = getProvider()->getSpeed((uint16_t *)data);
423
424        } break;
425
426        case DKIOCDVDSETSPEED:                                   // (uint16_t *)
427        {
428            status = getProvider()->setSpeed(*(uint16_t *)data);
429
430        } break;
431
432        case DKIOCDVDREADDISCINFO32:           // (dk_dvd_read_disc_info_32_t *)
433        {
434            dk_dvd_read_disc_info_32_t * request;
435
436            request = (dk_dvd_read_disc_info_32_t *) data;
437
438            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
439
440            if ( DKIOC_IS_RESERVED(data, 0x3FF) )  { error = EINVAL;  break; }
441
442            buffer = DKIOC_PREPARE_BUFFER(
443                       /* address   */ request->buffer,
444                       /* length    */ request->bufferLength,
445                       /* direction */ kIODirectionIn,
446                       /* proc      */ proc );
447
448            status = getProvider()->readDiscInfo(
449                       /* buffer          */ buffer,
450                       /* actualByteCount */ &request->bufferLength );
451
452            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
453
454            DKIOC_COMPLETE_BUFFER(buffer);
455
456        } break;
457
458        case DKIOCDVDREADDISCINFO64:           // (dk_dvd_read_disc_info_64_t *)
459        {
460            dk_dvd_read_disc_info_64_t * request;
461
462            request = (dk_dvd_read_disc_info_64_t *) data;
463
464            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
465
466            if ( DKIOC_IS_RESERVED(data, 0x3FFF) )  { error = EINVAL;  break; }
467
468            buffer = DKIOC_PREPARE_BUFFER(
469                       /* address   */ request->buffer,
470                       /* length    */ request->bufferLength,
471                       /* direction */ kIODirectionIn,
472                       /* proc      */ proc );
473
474            status = getProvider()->readDiscInfo(
475                       /* buffer          */ buffer,
476                       /* actualByteCount */ &request->bufferLength );
477
478            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
479
480            DKIOC_COMPLETE_BUFFER(buffer);
481
482        } break;
483
484        case DKIOCDVDREADRZONEINFO32:         // (dk_dvd_read_rzone_info_32_t *)
485        {
486            dk_dvd_read_rzone_info_32_t * request;
487
488            request = (dk_dvd_read_rzone_info_32_t *) data;
489
490            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
491
492            if ( DKIOC_IS_RESERVED(data, 0x20F) )  { error = EINVAL;  break; }
493
494            buffer = DKIOC_PREPARE_BUFFER(
495                       /* address   */ request->buffer,
496                       /* length    */ request->bufferLength,
497                       /* direction */ kIODirectionIn,
498                       /* proc      */ proc );
499
500            status = getProvider()->readRZoneInfo(
501                       /* buffer          */ buffer,
502                       /* address         */ request->address,
503                       /* addressType     */ request->addressType,
504                       /* actualByteCount */ &request->bufferLength );
505
506            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
507
508            DKIOC_COMPLETE_BUFFER(buffer);
509
510        } break;
511
512        case DKIOCDVDREADRZONEINFO64:         // (dk_dvd_read_rzone_info_64_t *)
513        {
514            dk_dvd_read_rzone_info_64_t * request;
515
516            request = (dk_dvd_read_rzone_info_64_t *) data;
517
518            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
519
520            if ( DKIOC_IS_RESERVED(data, 0x3E0F) )  { error = EINVAL;  break; }
521
522            buffer = DKIOC_PREPARE_BUFFER(
523                       /* address   */ request->buffer,
524                       /* length    */ request->bufferLength,
525                       /* direction */ kIODirectionIn,
526                       /* proc      */ proc );
527
528            status = getProvider()->readRZoneInfo(
529                       /* buffer          */ buffer,
530                       /* address         */ request->address,
531                       /* addressType     */ request->addressType,
532                       /* actualByteCount */ &request->bufferLength );
533
534            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
535
536            DKIOC_COMPLETE_BUFFER(buffer);
537
538        } break;
539
540        default:
541        {
542            //
543            // A foreign ioctl was received.  Ask our superclass' opinion.
544            //
545
546            error = super::ioctl(dev, cmd, data, flags, proc);
547
548        } break;
549    }
550
551    return error ? error : getProvider()->errnoFromReturn(status);
552}
553
554OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 0);
555OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 1);
556OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 2);
557OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 3);
558OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 4);
559OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 5);
560OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 6);
561OSMetaClassDefineReservedUnused(IODVDMediaBSDClient, 7);
562