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