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