linux_cdrom.c revision 1.25 1 /* $NetBSD: linux_cdrom.c,v 1.25 2007/12/20 23:02:53 dsl Exp $ */
2
3 /*
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the NetBSD
18 * Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: linux_cdrom.c,v 1.25 2007/12/20 23:02:53 dsl Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/ioctl.h>
42 #include <sys/file.h>
43 #include <sys/filedesc.h>
44 #include <sys/mount.h>
45 #include <sys/proc.h>
46 #include <sys/cdio.h>
47 #include <sys/dvdio.h>
48 #include <sys/malloc.h>
49
50 #include <sys/syscallargs.h>
51
52 #include <compat/linux/common/linux_types.h>
53 #include <compat/linux/common/linux_ioctl.h>
54 #include <compat/linux/common/linux_signal.h>
55 #include <compat/linux/common/linux_util.h>
56 #include <compat/linux/common/linux_ipc.h>
57 #include <compat/linux/common/linux_sem.h>
58 #include <compat/linux/common/linux_cdrom.h>
59
60 #include <compat/linux/linux_syscallargs.h>
61
62 static int bsd_to_linux_msf_lba(unsigned address_format, union msf_lba *bml,
63 union linux_cdrom_addr *llml);
64
65 #if DEBUG_LINUX
66 #define DPRINTF(x) uprintf x
67 #else
68 #define DPRINTF(x)
69 #endif
70
71 /*
72 * XXX from dev/scsipi/cd.c
73 */
74 #define MAXTRACK 99
75
76 static int
77 bsd_to_linux_msf_lba(unsigned address_format, union msf_lba *bml,
78 union linux_cdrom_addr *llml)
79 {
80 switch (address_format) {
81 case CD_LBA_FORMAT:
82 llml->lba = bml->lba;
83 break;
84 case CD_MSF_FORMAT:
85 llml->msf.minute = bml->msf.minute;
86 llml->msf.second = bml->msf.second;
87 llml->msf.frame = bml->msf.frame;
88 break;
89 default:
90 return -1;
91 }
92 return 0;
93 }
94
95 int
96 linux_ioctl_cdrom(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval)
97 {
98 /* {
99 syscallarg(int) fd;
100 syscallarg(u_long) com;
101 syscallarg(void *) data;
102 } */
103 int error, idata;
104 u_long com, ncom;
105 struct file *fp;
106 struct filedesc *fdp;
107 int (*ioctlf)(struct file *, u_long, void *, struct lwp *);
108
109 union {
110 struct linux_cdrom_blk ll_blk;
111 struct linux_cdrom_msf ll_msf;
112 struct linux_cdrom_ti ll_ti;
113 struct linux_cdrom_tochdr ll_tochdr;
114 struct linux_cdrom_tocentry ll_tocentry;
115 struct linux_cdrom_subchnl ll_subchnl;
116 struct linux_cdrom_volctrl ll_volctrl;
117 struct linux_cdrom_multisession ll_session;
118 dvd_struct ll_ds;
119 dvd_authinfo ll_dai;
120 } *u1;
121
122 #define l_blk u1->ll_blk
123 #define l_msf u1->ll_msf
124 #define l_ti u1->ll_ti
125 #define l_tochdr u1->ll_tochdr
126 #define l_tocentry u1->ll_tocentry
127 #define l_subchnl u1->ll_subchnl
128 #define l_volctrl u1->ll_volctrl
129 #define l_session u1->ll_session
130 #define ds u1->ll_ds
131 #define dai u1->ll_dai
132
133 union {
134 struct ioc_play_blocks tt_blocks;
135 struct ioc_play_msf tt_msf;
136 struct ioc_play_track tt_track;
137 struct ioc_toc_header tt_header;
138 struct ioc_read_toc_entry_buf tt_toc_entry;
139 struct ioc_read_subchannel_buf tt_subchannel;
140 struct ioc_vol tt_vol;
141 } *u2;
142
143 #define t_blocks u2->tt_blocks
144 #define t_msf u2->tt_msf
145 #define t_track u2->tt_track
146 #define t_header u2->tt_header
147 #define t_toc_entry u2->tt_toc_entry
148 #define t_subchannel u2->tt_subchannel
149 #define t_vol u2->tt_vol
150
151 struct cd_toc_entry *entry;
152 struct proc *p = l->l_proc;
153
154 fdp = p->p_fd;
155 if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
156 return (EBADF);
157
158 FILE_USE(fp);
159
160 com = SCARG(uap, com);
161 ioctlf = fp->f_ops->fo_ioctl;
162 retval[0] = error = 0;
163
164 u1 = malloc(sizeof(*u1), M_TEMP, M_WAITOK);
165 u2 = malloc(sizeof(*u2), M_TEMP, M_WAITOK);
166
167 switch(com) {
168 case LINUX_CDROMPLAYMSF:
169 error = copyin(SCARG(uap, data), &l_msf, sizeof l_msf);
170 if (error)
171 break;
172
173 t_msf.start_m = l_msf.cdmsf_min0;
174 t_msf.start_s = l_msf.cdmsf_sec0;
175 t_msf.start_f = l_msf.cdmsf_frame0;
176 t_msf.end_m = l_msf.cdmsf_min1;
177 t_msf.end_s = l_msf.cdmsf_sec1;
178 t_msf.end_f = l_msf.cdmsf_frame1;
179
180 error = ioctlf(fp, CDIOCPLAYMSF, (void *)&t_msf, l);
181 break;
182
183 case LINUX_CDROMPLAYTRKIND:
184 error = copyin(SCARG(uap, data), &l_ti, sizeof l_ti);
185 if (error)
186 break;
187
188 t_track.start_track = l_ti.cdti_trk0;
189 t_track.start_index = l_ti.cdti_ind0;
190 t_track.end_track = l_ti.cdti_trk1;
191 t_track.end_index = l_ti.cdti_ind1;
192
193 error = ioctlf(fp, CDIOCPLAYTRACKS, (void *)&t_track, l);
194 break;
195
196 case LINUX_CDROMREADTOCHDR:
197 error = ioctlf(fp, CDIOREADTOCHEADER, (void *)&t_header, l);
198 if (error)
199 break;
200
201 l_tochdr.cdth_trk0 = t_header.starting_track;
202 l_tochdr.cdth_trk1 = t_header.ending_track;
203
204 error = copyout(&l_tochdr, SCARG(uap, data), sizeof l_tochdr);
205 break;
206
207 case LINUX_CDROMREADTOCENTRY:
208 error = copyin(SCARG(uap, data), &l_tocentry,
209 sizeof l_tocentry);
210 if (error)
211 break;
212
213 t_toc_entry.req.address_format = l_tocentry.cdte_format;
214 t_toc_entry.req.starting_track = l_tocentry.cdte_track;
215 t_toc_entry.req.data_len = sizeof *entry;
216 t_toc_entry.req.data = NULL;
217
218 error = ioctlf(fp, CDIOREADTOCENTRIES_BUF, &t_toc_entry, l);
219 if (error)
220 break;
221
222 l_tocentry.cdte_adr = t_toc_entry.entry[0].addr_type;
223 l_tocentry.cdte_ctrl = t_toc_entry.entry[0].control;
224 if (bsd_to_linux_msf_lba(t_toc_entry.entry[0].addr_type,
225 &t_toc_entry.entry[0].addr, &l_tocentry.cdte_addr) < 0) {
226 DPRINTF(("linux_ioctl: unknown format msf/lba\n"));
227 error = EINVAL;
228 break;
229 }
230
231 error = copyout(&l_tocentry, SCARG(uap, data),
232 sizeof l_tocentry);
233 break;
234
235 case LINUX_CDROMVOLCTRL:
236 error = copyin(SCARG(uap, data), &l_volctrl, sizeof l_volctrl);
237 if (error)
238 break;
239
240 t_vol.vol[0] = l_volctrl.channel0;
241 t_vol.vol[1] = l_volctrl.channel1;
242 t_vol.vol[2] = l_volctrl.channel2;
243 t_vol.vol[3] = l_volctrl.channel3;
244
245 error = ioctlf(fp, CDIOCSETVOL, (void *)&t_vol, l);
246 break;
247
248 case LINUX_CDROMVOLREAD:
249 error = ioctlf(fp, CDIOCGETVOL, (void *)&t_vol, l);
250 if (error)
251 break;
252
253 l_volctrl.channel0 = t_vol.vol[0];
254 l_volctrl.channel1 = t_vol.vol[1];
255 l_volctrl.channel2 = t_vol.vol[2];
256 l_volctrl.channel3 = t_vol.vol[3];
257
258 error = copyout(&l_volctrl, SCARG(uap, data), sizeof l_volctrl);
259 break;
260
261 case LINUX_CDROMSUBCHNL:
262 error = copyin(SCARG(uap, data), &l_subchnl, sizeof l_subchnl);
263 if (error)
264 break;
265
266 t_subchannel.req.address_format = CD_MSF_FORMAT;
267 t_subchannel.req.track = 0;
268 t_subchannel.req.data_format = l_subchnl.cdsc_format;
269 t_subchannel.req.data_len = sizeof t_subchannel.info;
270 t_subchannel.req.data = NULL;
271 DPRINTF(("linux_ioctl: CDROMSUBCHNL %d %d\n",
272 l_subchnl.cdsc_format, l_subchnl.cdsc_trk));
273
274 error = ioctlf(fp, CDIOCREADSUBCHANNEL_BUF, &t_subchannel, l);
275 if (error)
276 break;
277
278 l_subchnl.cdsc_audiostatus = t_subchannel.info.header.audio_status;
279 l_subchnl.cdsc_adr = t_subchannel.info.what.position.addr_type;
280 l_subchnl.cdsc_ctrl = t_subchannel.info.what.position.control;
281 l_subchnl.cdsc_ind = t_subchannel.info.what.position.index_number;
282
283 DPRINTF(("linux_ioctl: CDIOCREADSUBCHANNEL %d %d %d\n",
284 t_subchannel.info.header.audio_status,
285 t_subchannel.info.header.data_len[0],
286 t_subchannel.info.header.data_len[1]));
287 DPRINTF(("(more) %d %d %d %d %d\n",
288 t_subchannel.info.what.position.data_format,
289 t_subchannel.info.what.position.control,
290 t_subchannel.info.what.position.addr_type,
291 t_subchannel.info.what.position.track_number,
292 t_subchannel.info.what.position.index_number));
293
294 if (bsd_to_linux_msf_lba(t_subchannel.req.address_format,
295 &t_subchannel.info.what.position.absaddr,
296 &l_subchnl.cdsc_absaddr) < 0 ||
297 bsd_to_linux_msf_lba(t_subchannel.req.address_format,
298 &t_subchannel.info.what.position.reladdr,
299 &l_subchnl.cdsc_reladdr) < 0) {
300 DPRINTF(("linux_ioctl: unknown format msf/lba\n"));
301 error = EINVAL;
302 break;
303 }
304
305 error = copyout(&l_subchnl, SCARG(uap, data), sizeof l_subchnl);
306 break;
307
308 case LINUX_CDROMPLAYBLK:
309 error = copyin(SCARG(uap, data), &l_blk, sizeof l_blk);
310 if (error)
311 break;
312
313 t_blocks.blk = l_blk.from;
314 t_blocks.len = l_blk.len;
315
316 error = ioctlf(fp, CDIOCPLAYBLOCKS, (void *)&t_blocks, l);
317 break;
318
319 case LINUX_CDROMEJECT_SW:
320 error = copyin(SCARG(uap, data), &idata, sizeof idata);
321 if (error)
322 break;
323
324 if (idata == 1)
325 ncom = CDIOCALLOW;
326 else
327 ncom = CDIOCPREVENT;
328 error = ioctlf(fp, ncom, NULL, l);
329 break;
330
331 case LINUX_CDROMPAUSE:
332 error = ioctlf(fp, CDIOCPAUSE, NULL, l);
333 break;
334
335 case LINUX_CDROMRESUME:
336 error = ioctlf(fp, CDIOCRESUME, NULL, l);
337 break;
338
339 case LINUX_CDROMSTOP:
340 error = ioctlf(fp, CDIOCSTOP, NULL, l);
341 break;
342
343 case LINUX_CDROMSTART:
344 error = ioctlf(fp, CDIOCSTART, NULL, l);
345 break;
346
347 case LINUX_CDROMEJECT:
348 error = ioctlf(fp, CDIOCEJECT, NULL, l);
349 break;
350
351 case LINUX_CDROMRESET:
352 error = ioctlf(fp, CDIOCRESET, NULL, l);
353 break;
354
355 case LINUX_CDROMMULTISESSION:
356 error = copyin(SCARG(uap, data), &l_session, sizeof l_session);
357 if (error)
358 break;
359
360 error = ioctlf(fp, CDIOREADTOCHEADER, (void *)&t_header, l);
361 if (error)
362 break;
363
364 t_toc_entry.req.address_format = l_session.addr_format;
365 t_toc_entry.req.starting_track = 0;
366 t_toc_entry.req.data_len = sizeof t_toc_entry.entry;
367 t_toc_entry.req.data = NULL;
368
369 error = ioctlf(fp, CDIOREADTOCENTRIES_BUF, &t_toc_entry, l);
370 if (error)
371 break;
372
373 if (bsd_to_linux_msf_lba(l_session.addr_format,
374 &t_toc_entry.entry[0].addr, &l_session.addr) < 0) {
375 error = EINVAL;
376 break;
377 }
378
379 l_session.xa_flag =
380 t_header.starting_track != t_header.ending_track;
381
382 error = copyout(&l_session, SCARG(uap, data), sizeof l_session);
383 break;
384
385 case LINUX_CDROMCLOSETRAY:
386 error = ioctlf(fp, CDIOCCLOSE, NULL, l);
387 break;
388
389 case LINUX_CDROM_LOCKDOOR:
390 ncom = SCARG(uap, data) != 0 ? CDIOCPREVENT : CDIOCALLOW;
391 error = ioctlf(fp, ncom, NULL, l);
392 break;
393
394 case LINUX_CDROM_SET_OPTIONS:
395 case LINUX_CDROM_CLEAR_OPTIONS:
396 /* whatever you say */
397 break;
398
399 case LINUX_CDROM_DEBUG:
400 ncom = SCARG(uap, data) != 0 ? CDIOCSETDEBUG : CDIOCCLRDEBUG;
401 error = ioctlf(fp, ncom, NULL, l);
402 break;
403
404 case LINUX_CDROM_SELECT_SPEED:
405 case LINUX_CDROM_SELECT_DISC:
406 case LINUX_CDROM_MEDIA_CHANGED:
407 case LINUX_CDROM_DRIVE_STATUS:
408 case LINUX_CDROM_DISC_STATUS:
409 case LINUX_CDROM_CHANGER_NSLOTS:
410 case LINUX_CDROM_GET_CAPABILITY:
411 error = ENOSYS;
412 break;
413
414 case LINUX_DVD_READ_STRUCT:
415 error = copyin(SCARG(uap, data), &ds, sizeof ds);
416 if (error)
417 break;
418 error = ioctlf(fp, DVD_READ_STRUCT, (void *)&ds, l);
419 if (error)
420 break;
421 error = copyout(&ds, SCARG(uap, data), sizeof ds);
422 break;
423
424 case LINUX_DVD_WRITE_STRUCT:
425 error = copyin(SCARG(uap, data), &ds, sizeof ds);
426 if (error)
427 break;
428 error = ioctlf(fp, DVD_WRITE_STRUCT, (void *)&ds, l);
429 if (error)
430 break;
431 error = copyout(&ds, SCARG(uap, data), sizeof ds);
432 break;
433
434 case LINUX_DVD_AUTH:
435 error = copyin(SCARG(uap, data), &dai, sizeof dai);
436 if (error)
437 break;
438 error = ioctlf(fp, DVD_AUTH, (void *)&dai, l);
439 if (error)
440 break;
441 error = copyout(&dai, SCARG(uap, data), sizeof dai);
442 break;
443
444
445 default:
446 DPRINTF(("linux_ioctl: unimplemented ioctl %08lx\n", com));
447 error = EINVAL;
448 }
449
450 FILE_UNUSE(fp, l);
451 free(u1, M_TEMP);
452 free(u2, M_TEMP);
453 return error;
454 }
455