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/IOCDMediaBSDClient.h>
27
28#define super IOMediaBSDClient
29OSDefineMetaClassAndStructors(IOCDMediaBSDClient, IOMediaBSDClient)
30
31typedef struct
32{
33    uint64_t      offset;
34
35    uint8_t       sectorArea;
36    uint8_t       sectorType;
37
38    uint8_t       reserved0080[6];
39
40    uint32_t      bufferLength;
41    user32_addr_t buffer;
42} dk_cd_read_32_t;
43
44typedef struct
45{
46    uint64_t      offset;
47
48    uint8_t       sectorArea;
49    uint8_t       sectorType;
50
51    uint8_t       reserved0080[10];
52
53    uint32_t      bufferLength;
54    user64_addr_t buffer;
55} dk_cd_read_64_t;
56
57typedef struct
58{
59    uint8_t       format;
60    uint8_t       formatAsTime;
61
62    uint8_t       reserved0016[5];
63
64    union
65    {
66        uint8_t   session;
67        uint8_t   track;
68    } address;
69
70    uint8_t       reserved0064[2];
71
72    uint16_t      bufferLength;
73    user32_addr_t buffer;
74} dk_cd_read_toc_32_t;
75
76typedef struct
77{
78    uint8_t       format;
79    uint8_t       formatAsTime;
80
81    uint8_t       reserved0016[5];
82
83    union
84    {
85        uint8_t   session;
86        uint8_t   track;
87    } address;
88
89    uint8_t       reserved0064[6];
90
91    uint16_t      bufferLength;
92    user64_addr_t buffer;
93} dk_cd_read_toc_64_t;
94
95typedef struct
96{
97    uint8_t       reserved0000[10];
98
99    uint16_t      bufferLength;
100    user32_addr_t buffer;
101} dk_cd_read_disc_info_32_t;
102
103typedef struct
104{
105    uint8_t       reserved0000[14];
106
107    uint16_t      bufferLength;
108    user64_addr_t buffer;
109} dk_cd_read_disc_info_64_t;
110
111typedef struct
112{
113    uint8_t       reserved0000[4];
114
115    uint32_t      address;
116    uint8_t       addressType;
117
118    uint8_t       reserved0072[1];
119
120    uint16_t      bufferLength;
121    user32_addr_t buffer;
122} dk_cd_read_track_info_32_t;
123
124typedef struct
125{
126    uint8_t       reserved0000[4];
127
128    uint32_t      address;
129    uint8_t       addressType;
130
131    uint8_t       reserved0072[5];
132
133    uint16_t      bufferLength;
134    user64_addr_t buffer;
135} dk_cd_read_track_info_64_t;
136
137#define DKIOCCDREAD32          _IOWR('d', 96, dk_cd_read_32_t)
138#define DKIOCCDREAD64          _IOWR('d', 96, dk_cd_read_64_t)
139
140#define DKIOCCDREADTOC32       _IOWR('d', 100, dk_cd_read_toc_32_t)
141#define DKIOCCDREADTOC64       _IOWR('d', 100, dk_cd_read_toc_64_t)
142
143#define DKIOCCDREADDISCINFO32  _IOWR('d', 101, dk_cd_read_disc_info_32_t)
144#define DKIOCCDREADDISCINFO64  _IOWR('d', 101, dk_cd_read_disc_info_64_t)
145#define DKIOCCDREADTRACKINFO32 _IOWR('d', 102, dk_cd_read_track_info_32_t)
146#define DKIOCCDREADTRACKINFO64 _IOWR('d', 102, dk_cd_read_track_info_64_t)
147
148static bool DKIOC_IS_RESERVED(caddr_t data, uint32_t reserved)
149{
150    UInt32 index;
151
152    for ( index = 0; index < sizeof(reserved) * 8; index++, reserved >>= 1 )
153    {
154        if ( (reserved & 1) )
155        {
156            if ( data[index] )  return true;
157        }
158    }
159
160    return false;
161}
162
163static IOMemoryDescriptor * DKIOC_PREPARE_BUFFER( user_addr_t address,
164                                                  UInt32      length,
165                                                  IODirection direction,
166                                                  proc_t      proc )
167{
168    IOMemoryDescriptor * buffer = 0;
169
170    if ( address && length )
171    {
172        buffer = IOMemoryDescriptor::withAddressRange(    // (create the buffer)
173            /* address */ address,
174            /* length  */ length,
175            /* options */ direction,
176            /* task    */ (proc == kernproc) ? kernel_task : current_task() );
177    }
178
179    if ( buffer )
180    {
181        if ( buffer->prepare() != kIOReturnSuccess )     // (prepare the buffer)
182        {
183            buffer->release();
184            buffer = 0;
185        }
186    }
187
188    return buffer;
189}
190
191static void DKIOC_COMPLETE_BUFFER(IOMemoryDescriptor * buffer)
192{
193    if ( buffer )
194    {
195        buffer->complete();                             // (complete the buffer)
196        buffer->release();                               // (release the buffer)
197    }
198}
199
200IOCDMedia * IOCDMediaBSDClient::getProvider() const
201{
202    //
203    // Obtain this object's provider.  We override the superclass's method
204    // to return a more specific subclass of IOService -- IOCDMedia.  This
205    // method serves simply as a convenience to subclass developers.
206    //
207
208    return (IOCDMedia *) IOService::getProvider();
209}
210
211int IOCDMediaBSDClient::ioctl( dev_t   dev,
212                               u_long  cmd,
213                               caddr_t data,
214                               int     flags,
215                               proc_t  proc )
216{
217    //
218    // Process a CD-specific ioctl.
219    //
220
221    IOMemoryDescriptor * buffer = 0;
222    int                  error  = 0;
223    IOReturn             status = kIOReturnSuccess;
224
225    switch ( cmd )
226    {
227        case DKIOCCDREAD32:                               // (dk_cd_read_32_t *)
228        {
229            UInt64            actualByteCount = 0;
230            dk_cd_read_32_t * request         = (dk_cd_read_32_t *) data;
231
232            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
233
234            if ( DKIOC_IS_RESERVED(data, 0xFC00) )  { error = EINVAL;  break; }
235
236            buffer = DKIOC_PREPARE_BUFFER(
237                       /* address   */ request->buffer,
238                       /* length    */ request->bufferLength,
239                       /* direction */ kIODirectionIn,
240                       /* proc      */ proc );
241
242            status = getProvider()->readCD(
243                       /* client          */                this,
244                       /* byteStart       */                request->offset,
245                       /* buffer          */                buffer,
246                       /* sectorArea      */ (CDSectorArea) request->sectorArea,
247                       /* sectorType      */ (CDSectorType) request->sectorType,
248#ifdef __LP64__
249                       /* attributes      */                NULL,
250#endif /* __LP64__ */
251                       /* actualByteCount */                &actualByteCount );
252
253            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
254
255            request->bufferLength = (UInt32) actualByteCount;
256
257            DKIOC_COMPLETE_BUFFER(buffer);
258
259        } break;
260
261        case DKIOCCDREAD64:                               // (dk_cd_read_64_t *)
262        {
263            UInt64            actualByteCount = 0;
264            dk_cd_read_64_t * request         = (dk_cd_read_64_t *) data;
265
266            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
267
268            if ( DKIOC_IS_RESERVED(data, 0xFFC00) )  { error = EINVAL;  break; }
269
270            buffer = DKIOC_PREPARE_BUFFER(
271                       /* address   */ request->buffer,
272                       /* length    */ request->bufferLength,
273                       /* direction */ kIODirectionIn,
274                       /* proc      */ proc );
275
276            status = getProvider()->readCD(
277                       /* client          */                this,
278                       /* byteStart       */                request->offset,
279                       /* buffer          */                buffer,
280                       /* sectorArea      */ (CDSectorArea) request->sectorArea,
281                       /* sectorType      */ (CDSectorType) request->sectorType,
282#ifdef __LP64__
283                       /* attributes      */                NULL,
284#endif /* __LP64__ */
285                       /* actualByteCount */                &actualByteCount );
286
287            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
288
289            request->bufferLength = (UInt32) actualByteCount;
290
291            DKIOC_COMPLETE_BUFFER(buffer);
292
293        } break;
294
295        case DKIOCCDREADISRC:                           // (dk_cd_read_isrc_t *)
296        {
297            dk_cd_read_isrc_t * request = (dk_cd_read_isrc_t *) data;
298
299            if ( DKIOC_IS_RESERVED(data, 0xC000) )  { error = EINVAL;  break; }
300
301            status = getProvider()->readISRC(request->track, request->isrc);
302
303        } break;
304
305        case DKIOCCDREADMCN:                             // (dk_cd_read_mcn_t *)
306        {
307            dk_cd_read_mcn_t * request = (dk_cd_read_mcn_t *) data;
308
309            if ( DKIOC_IS_RESERVED(data, 0xC000) )  { error = EINVAL;  break; }
310
311            status = getProvider()->readMCN(request->mcn);
312
313        } break;
314
315        case DKIOCCDGETSPEED:                                    // (uint16_t *)
316        {
317            status = getProvider()->getSpeed((uint16_t *)data);
318
319        } break;
320
321        case DKIOCCDSETSPEED:                                    // (uint16_t *)
322        {
323            status = getProvider()->setSpeed(*(uint16_t *)data);
324
325        } break;
326
327        case DKIOCCDREADTOC32:                        // (dk_cd_read_toc_32_t *)
328        {
329            dk_cd_read_toc_32_t * request = (dk_cd_read_toc_32_t *) data;
330
331            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
332
333            if ( DKIOC_IS_RESERVED(data, 0x37C) )  { error = EINVAL;  break; }
334
335            buffer = DKIOC_PREPARE_BUFFER(
336                       /* address   */ request->buffer,
337                       /* length    */ request->bufferLength,
338                       /* direction */ kIODirectionIn,
339                       /* proc      */ proc );
340
341            status = getProvider()->readTOC(
342                       /* buffer          */ buffer,
343                       /* format          */ request->format,
344                       /* formatAsTime    */ request->formatAsTime,
345                       /* address         */ request->address.session,
346                       /* actualByteCount */ &request->bufferLength );
347
348            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
349
350            DKIOC_COMPLETE_BUFFER(buffer);
351
352        } break;
353
354        case DKIOCCDREADTOC64:                        // (dk_cd_read_toc_64_t *)
355        {
356            dk_cd_read_toc_64_t * request = (dk_cd_read_toc_64_t *) data;
357
358            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
359
360            if ( DKIOC_IS_RESERVED(data, 0x3F7C) )  { error = EINVAL;  break; }
361
362            buffer = DKIOC_PREPARE_BUFFER(
363                       /* address   */ request->buffer,
364                       /* length    */ request->bufferLength,
365                       /* direction */ kIODirectionIn,
366                       /* proc      */ proc );
367
368            status = getProvider()->readTOC(
369                       /* buffer          */ buffer,
370                       /* format          */ request->format,
371                       /* formatAsTime    */ request->formatAsTime,
372                       /* address         */ request->address.session,
373                       /* actualByteCount */ &request->bufferLength );
374
375            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
376
377            DKIOC_COMPLETE_BUFFER(buffer);
378
379        } break;
380
381        case DKIOCCDREADDISCINFO32:             // (dk_cd_read_disc_info_32_t *)
382        {
383            dk_cd_read_disc_info_32_t * request;
384
385            request = (dk_cd_read_disc_info_32_t *) data;
386
387            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
388
389            if ( DKIOC_IS_RESERVED(data, 0x3FF) )  { error = EINVAL;  break; }
390
391            buffer = DKIOC_PREPARE_BUFFER(
392                       /* address   */ request->buffer,
393                       /* length    */ request->bufferLength,
394                       /* direction */ kIODirectionIn,
395                       /* proc      */ proc );
396
397            status = getProvider()->readDiscInfo(
398                       /* buffer          */ buffer,
399                       /* actualByteCount */ &request->bufferLength );
400
401            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
402
403            DKIOC_COMPLETE_BUFFER(buffer);
404
405        } break;
406
407        case DKIOCCDREADDISCINFO64:             // (dk_cd_read_disc_info_64_t *)
408        {
409            dk_cd_read_disc_info_64_t * request;
410
411            request = (dk_cd_read_disc_info_64_t *) data;
412
413            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
414
415            if ( DKIOC_IS_RESERVED(data, 0x3FFF) )  { error = EINVAL;  break; }
416
417            buffer = DKIOC_PREPARE_BUFFER(
418                       /* address   */ request->buffer,
419                       /* length    */ request->bufferLength,
420                       /* direction */ kIODirectionIn,
421                       /* proc      */ proc );
422
423            status = getProvider()->readDiscInfo(
424                       /* buffer          */ buffer,
425                       /* actualByteCount */ &request->bufferLength );
426
427            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
428
429            DKIOC_COMPLETE_BUFFER(buffer);
430
431        } break;
432
433        case DKIOCCDREADTRACKINFO32:           // (dk_cd_read_track_info_32_t *)
434        {
435            dk_cd_read_track_info_32_t * request;
436
437            request = (dk_cd_read_track_info_32_t *) data;
438
439            if ( proc_is64bit(proc) )  { error = ENOTTY;  break; }
440
441            if ( DKIOC_IS_RESERVED(data, 0x20F) )  { error = EINVAL;  break; }
442
443            buffer = DKIOC_PREPARE_BUFFER(
444                       /* address   */ request->buffer,
445                       /* length    */ request->bufferLength,
446                       /* direction */ kIODirectionIn,
447                       /* proc      */ proc );
448
449            status = getProvider()->readTrackInfo(
450                       /* buffer          */ buffer,
451                       /* address         */ request->address,
452                       /* addressType     */ request->addressType,
453                       /* actualByteCount */ &request->bufferLength );
454
455            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
456
457            DKIOC_COMPLETE_BUFFER(buffer);
458
459        } break;
460
461        case DKIOCCDREADTRACKINFO64:           // (dk_cd_read_track_info_64_t *)
462        {
463            dk_cd_read_track_info_64_t * request;
464
465            request = (dk_cd_read_track_info_64_t *) data;
466
467            if ( proc_is64bit(proc) == 0 )  { error = ENOTTY;  break; }
468
469            if ( DKIOC_IS_RESERVED(data, 0x3E0F) )  { error = EINVAL;  break; }
470
471            buffer = DKIOC_PREPARE_BUFFER(
472                       /* address   */ request->buffer,
473                       /* length    */ request->bufferLength,
474                       /* direction */ kIODirectionIn,
475                       /* proc      */ proc );
476
477            status = getProvider()->readTrackInfo(
478                       /* buffer          */ buffer,
479                       /* address         */ request->address,
480                       /* addressType     */ request->addressType,
481                       /* actualByteCount */ &request->bufferLength );
482
483            status = (status == kIOReturnUnderrun) ? kIOReturnSuccess : status;
484
485            DKIOC_COMPLETE_BUFFER(buffer);
486
487        } break;
488
489        default:
490        {
491            //
492            // A foreign ioctl was received.  Ask our superclass' opinion.
493            //
494
495            error = super::ioctl(dev, cmd, data, flags, proc);
496
497        } break;
498    }
499
500    return error ? error : getProvider()->errnoFromReturn(status);
501}
502
503OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 0);
504OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 1);
505OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 2);
506OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 3);
507OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 4);
508OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 5);
509OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 6);
510OSMetaClassDefineReservedUnused(IOCDMediaBSDClient, 7);
511