1 /* $NetBSD: linux_cdrom.c,v 1.20 2006/06/12 00:42:18 christos 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.20 2006/06/12 00:42:18 christos 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/sa.h> 51 #include <sys/syscallargs.h> 52 53 #include <compat/linux/common/linux_types.h> 54 #include <compat/linux/common/linux_ioctl.h> 55 #include <compat/linux/common/linux_signal.h> 56 #include <compat/linux/common/linux_util.h> 57 #include <compat/linux/common/linux_cdrom.h> 58 59 #include <compat/linux/linux_syscallargs.h> 60 61 static int bsd_to_linux_msf_lba(unsigned address_format, union msf_lba *bml, 62 union linux_cdrom_addr *llml); 63 64 #if DEBUG_LINUX 65 #define DPRINTF(x) uprintf x 66 #else 67 #define DPRINTF(x) 68 #endif 69 70 /* 71 * XXX from dev/scsipi/cd.c 72 */ 73 #define MAXTRACK 99 74 75 static int 76 bsd_to_linux_msf_lba(unsigned address_format, union msf_lba *bml, 77 union linux_cdrom_addr *llml) 78 { 79 switch (address_format) { 80 case CD_LBA_FORMAT: 81 llml->lba = bml->lba; 82 break; 83 case CD_MSF_FORMAT: 84 llml->msf.minute = bml->msf.minute; 85 llml->msf.second = bml->msf.second; 86 llml->msf.frame = bml->msf.frame; 87 break; 88 default: 89 return -1; 90 } 91 return 0; 92 } 93 94 int 95 linux_ioctl_cdrom(l, uap, retval) 96 struct lwp *l; 97 struct linux_sys_ioctl_args /* { 98 syscallarg(int) fd; 99 syscallarg(u_long) com; 100 syscallarg(caddr_t) data; 101 } */ *uap; 102 register_t *retval; 103 { 104 int error, idata; 105 u_long com, ncom; 106 caddr_t sg; 107 struct file *fp; 108 struct filedesc *fdp; 109 int (*ioctlf)(struct file *, u_long, void *, struct lwp *); 110 111 union { 112 struct linux_cdrom_blk ll_blk; 113 struct linux_cdrom_msf ll_msf; 114 struct linux_cdrom_ti ll_ti; 115 struct linux_cdrom_tochdr ll_tochdr; 116 struct linux_cdrom_tocentry ll_tocentry; 117 struct linux_cdrom_subchnl ll_subchnl; 118 struct linux_cdrom_volctrl ll_volctrl; 119 struct linux_cdrom_multisession ll_session; 120 dvd_struct ll_ds; 121 dvd_authinfo ll_dai; 122 } *u1; 123 124 #define l_blk u1->ll_blk 125 #define l_msf u1->ll_msf 126 #define l_ti u1->ll_ti 127 #define l_tochdr u1->ll_tochdr 128 #define l_tocentry u1->ll_tocentry 129 #define l_subchnl u1->ll_subchnl 130 #define l_volctrl u1->ll_volctrl 131 #define l_session u1->ll_session 132 #define ds u1->ll_ds 133 #define dai u1->ll_dai 134 135 union { 136 struct ioc_play_blocks tt_blocks; 137 struct ioc_play_msf tt_msf; 138 struct ioc_play_track tt_track; 139 struct ioc_toc_header tt_header; 140 struct cd_toc_entry tt_entry; 141 struct ioc_read_toc_entry tt_toc_entry; 142 struct cd_sub_channel_info tt_info; 143 struct ioc_read_subchannel tt_subchannel; 144 struct ioc_vol tt_vol; 145 } *u2; 146 147 #define t_blocks u2->tt_blocks 148 #define t_msf u2->tt_msf 149 #define t_track u2->tt_track 150 #define t_header u2->tt_header 151 #define t_entry u2->tt_entry 152 #define t_toc_entry u2->tt_toc_entry 153 #define t_info u2->tt_info 154 #define t_subchannel u2->tt_subchannel 155 #define t_vol u2->tt_vol 156 157 struct cd_toc_entry *entry; 158 struct cd_sub_channel_info *info; 159 struct proc *p = l->l_proc; 160 161 fdp = p->p_fd; 162 if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) 163 return (EBADF); 164 165 FILE_USE(fp); 166 167 com = SCARG(uap, com); 168 ioctlf = fp->f_ops->fo_ioctl; 169 retval[0] = error = 0; 170 171 u1 = malloc(sizeof(*u1), M_TEMP, M_WAITOK); 172 u2 = malloc(sizeof(*u2), M_TEMP, M_WAITOK); 173 174 switch(com) { 175 case LINUX_CDROMPLAYMSF: 176 error = copyin(SCARG(uap, data), &l_msf, sizeof l_msf); 177 if (error) 178 break; 179 180 t_msf.start_m = l_msf.cdmsf_min0; 181 t_msf.start_s = l_msf.cdmsf_sec0; 182 t_msf.start_f = l_msf.cdmsf_frame0; 183 t_msf.end_m = l_msf.cdmsf_min1; 184 t_msf.end_s = l_msf.cdmsf_sec1; 185 t_msf.end_f = l_msf.cdmsf_frame1; 186 187 error = ioctlf(fp, CDIOCPLAYMSF, (caddr_t)&t_msf, l); 188 break; 189 190 case LINUX_CDROMPLAYTRKIND: 191 error = copyin(SCARG(uap, data), &l_ti, sizeof l_ti); 192 if (error) 193 break; 194 195 t_track.start_track = l_ti.cdti_trk0; 196 t_track.start_index = l_ti.cdti_ind0; 197 t_track.end_track = l_ti.cdti_trk1; 198 t_track.end_index = l_ti.cdti_ind1; 199 200 error = ioctlf(fp, CDIOCPLAYTRACKS, (caddr_t)&t_track, l); 201 break; 202 203 case LINUX_CDROMREADTOCHDR: 204 error = ioctlf(fp, CDIOREADTOCHEADER, (caddr_t)&t_header, l); 205 if (error) 206 break; 207 208 l_tochdr.cdth_trk0 = t_header.starting_track; 209 l_tochdr.cdth_trk1 = t_header.ending_track; 210 211 error = copyout(&l_tochdr, SCARG(uap, data), sizeof l_tochdr); 212 break; 213 214 case LINUX_CDROMREADTOCENTRY: 215 error = copyin(SCARG(uap, data), &l_tocentry, 216 sizeof l_tocentry); 217 if (error) 218 break; 219 220 sg = stackgap_init(p, 0); 221 entry = stackgap_alloc(p, &sg, sizeof *entry); 222 t_toc_entry.address_format = l_tocentry.cdte_format; 223 t_toc_entry.starting_track = l_tocentry.cdte_track; 224 t_toc_entry.data_len = sizeof *entry; 225 t_toc_entry.data = entry; 226 227 error = ioctlf(fp, CDIOREADTOCENTRIES, (caddr_t)&t_toc_entry, 228 l); 229 if (error) 230 break; 231 232 error = copyin(entry, &t_entry, sizeof t_entry); 233 if (error) 234 break; 235 236 l_tocentry.cdte_adr = t_entry.addr_type; 237 l_tocentry.cdte_ctrl = t_entry.control; 238 if (bsd_to_linux_msf_lba(t_entry.addr_type, &t_entry.addr, 239 &l_tocentry.cdte_addr) < 0) { 240 DPRINTF(("linux_ioctl: unknown format msf/lba\n")); 241 error = EINVAL; 242 break; 243 } 244 245 error = copyout(&l_tocentry, SCARG(uap, data), 246 sizeof l_tocentry); 247 break; 248 249 case LINUX_CDROMVOLCTRL: 250 error = copyin(SCARG(uap, data), &l_volctrl, sizeof l_volctrl); 251 if (error) 252 break; 253 254 t_vol.vol[0] = l_volctrl.channel0; 255 t_vol.vol[1] = l_volctrl.channel1; 256 t_vol.vol[2] = l_volctrl.channel2; 257 t_vol.vol[3] = l_volctrl.channel3; 258 259 error = ioctlf(fp, CDIOCSETVOL, (caddr_t)&t_vol, l); 260 break; 261 262 case LINUX_CDROMVOLREAD: 263 error = ioctlf(fp, CDIOCGETVOL, (caddr_t)&t_vol, l); 264 if (error) 265 break; 266 267 l_volctrl.channel0 = t_vol.vol[0]; 268 l_volctrl.channel1 = t_vol.vol[1]; 269 l_volctrl.channel2 = t_vol.vol[2]; 270 l_volctrl.channel3 = t_vol.vol[3]; 271 272 error = copyout(&l_volctrl, SCARG(uap, data), sizeof l_volctrl); 273 break; 274 275 case LINUX_CDROMSUBCHNL: 276 error = copyin(SCARG(uap, data), &l_subchnl, sizeof l_subchnl); 277 if (error) 278 break; 279 280 sg = stackgap_init(p, 0); 281 info = stackgap_alloc(p, &sg, sizeof *info); 282 t_subchannel.address_format = CD_MSF_FORMAT; 283 t_subchannel.track = 0; 284 t_subchannel.data_format = l_subchnl.cdsc_format; 285 t_subchannel.data_len = sizeof *info; 286 t_subchannel.data = info; 287 DPRINTF(("linux_ioctl: CDROMSUBCHNL %d %d\n", 288 l_subchnl.cdsc_format, l_subchnl.cdsc_trk)); 289 290 error = ioctlf(fp, CDIOCREADSUBCHANNEL, (caddr_t)&t_subchannel, 291 l); 292 if (error) 293 break; 294 295 error = copyin(info, &t_info, sizeof t_info); 296 if (error) 297 break; 298 299 l_subchnl.cdsc_audiostatus = t_info.header.audio_status; 300 l_subchnl.cdsc_adr = t_info.what.position.addr_type; 301 l_subchnl.cdsc_ctrl = t_info.what.position.control; 302 l_subchnl.cdsc_ind = t_info.what.position.index_number; 303 304 DPRINTF(("linux_ioctl: CDIOCREADSUBCHANNEL %d %d %d\n", 305 t_info.header.audio_status, 306 t_info.header.data_len[0], 307 t_info.header.data_len[1])); 308 DPRINTF(("(more) %d %d %d %d %d\n", 309 t_info.what.position.data_format, 310 t_info.what.position.control, 311 t_info.what.position.addr_type, 312 t_info.what.position.track_number, 313 t_info.what.position.index_number)); 314 315 if (bsd_to_linux_msf_lba(t_subchannel.address_format, 316 &t_info.what.position.absaddr, 317 &l_subchnl.cdsc_absaddr) < 0 || 318 bsd_to_linux_msf_lba(t_subchannel.address_format, 319 &t_info.what.position.reladdr, 320 &l_subchnl.cdsc_reladdr) < 0) { 321 DPRINTF(("linux_ioctl: unknown format msf/lba\n")); 322 error = EINVAL; 323 break; 324 } 325 326 error = copyout(&l_subchnl, SCARG(uap, data), sizeof l_subchnl); 327 break; 328 329 case LINUX_CDROMPLAYBLK: 330 error = copyin(SCARG(uap, data), &l_blk, sizeof l_blk); 331 if (error) 332 break; 333 334 t_blocks.blk = l_blk.from; 335 t_blocks.len = l_blk.len; 336 337 error = ioctlf(fp, CDIOCPLAYBLOCKS, (caddr_t)&t_blocks, l); 338 break; 339 340 case LINUX_CDROMEJECT_SW: 341 error = copyin(SCARG(uap, data), &idata, sizeof idata); 342 if (error) 343 break; 344 345 if (idata == 1) 346 ncom = CDIOCALLOW; 347 else 348 ncom = CDIOCPREVENT; 349 error = ioctlf(fp, ncom, NULL, l); 350 break; 351 352 case LINUX_CDROMPAUSE: 353 error = ioctlf(fp, CDIOCPAUSE, NULL, l); 354 break; 355 356 case LINUX_CDROMRESUME: 357 error = ioctlf(fp, CDIOCRESUME, NULL, l); 358 break; 359 360 case LINUX_CDROMSTOP: 361 error = ioctlf(fp, CDIOCSTOP, NULL, l); 362 break; 363 364 case LINUX_CDROMSTART: 365 error = ioctlf(fp, CDIOCSTART, NULL, l); 366 break; 367 368 case LINUX_CDROMEJECT: 369 error = ioctlf(fp, CDIOCEJECT, NULL, l); 370 break; 371 372 case LINUX_CDROMRESET: 373 error = ioctlf(fp, CDIOCRESET, NULL, l); 374 break; 375 376 case LINUX_CDROMMULTISESSION: 377 error = copyin(SCARG(uap, data), &l_session, sizeof l_session); 378 if (error) 379 break; 380 381 error = ioctlf(fp, CDIOREADTOCHEADER, (caddr_t)&t_header, l); 382 if (error) 383 break; 384 385 sg = stackgap_init(p, 0); 386 entry = stackgap_alloc(p, &sg, sizeof *entry); 387 t_toc_entry.address_format = l_session.addr_format; 388 t_toc_entry.starting_track = 0; 389 t_toc_entry.data_len = sizeof *entry; 390 t_toc_entry.data = entry; 391 392 error = ioctlf(fp, CDIOREADTOCENTRIES, 393 (caddr_t)&t_toc_entry, l); 394 if (error) 395 break; 396 397 error = copyin(entry, &t_entry, sizeof t_entry); 398 if (error) 399 break; 400 401 if (bsd_to_linux_msf_lba(l_session.addr_format, 402 &t_entry.addr, &l_session.addr) < 0) { 403 error = EINVAL; 404 break; 405 } 406 407 l_session.xa_flag = 408 t_header.starting_track != t_header.ending_track; 409 410 error = copyout(&l_session, SCARG(uap, data), sizeof l_session); 411 break; 412 413 case LINUX_CDROMCLOSETRAY: 414 error = ioctlf(fp, CDIOCCLOSE, NULL, l); 415 break; 416 417 case LINUX_CDROM_LOCKDOOR: 418 ncom = SCARG(uap, data) != 0 ? CDIOCPREVENT : CDIOCALLOW; 419 error = ioctlf(fp, ncom, NULL, l); 420 break; 421 422 case LINUX_CDROM_SET_OPTIONS: 423 case LINUX_CDROM_CLEAR_OPTIONS: 424 /* whatever you say */ 425 break; 426 427 case LINUX_CDROM_DEBUG: 428 ncom = SCARG(uap, data) != 0 ? CDIOCSETDEBUG : CDIOCCLRDEBUG; 429 error = ioctlf(fp, ncom, NULL, l); 430 break; 431 432 case LINUX_CDROM_SELECT_SPEED: 433 case LINUX_CDROM_SELECT_DISC: 434 case LINUX_CDROM_MEDIA_CHANGED: 435 case LINUX_CDROM_DRIVE_STATUS: 436 case LINUX_CDROM_DISC_STATUS: 437 case LINUX_CDROM_CHANGER_NSLOTS: 438 case LINUX_CDROM_GET_CAPABILITY: 439 error = ENOSYS; 440 break; 441 442 case LINUX_DVD_READ_STRUCT: 443 error = copyin(SCARG(uap, data), &ds, sizeof ds); 444 if (error) 445 break; 446 error = ioctlf(fp, DVD_READ_STRUCT, (caddr_t)&ds, l); 447 if (error) 448 break; 449 error = copyout(&ds, SCARG(uap, data), sizeof ds); 450 break; 451 452 case LINUX_DVD_WRITE_STRUCT: 453 error = copyin(SCARG(uap, data), &ds, sizeof ds); 454 if (error) 455 break; 456 error = ioctlf(fp, DVD_WRITE_STRUCT, (caddr_t)&ds, l); 457 if (error) 458 break; 459 error = copyout(&ds, SCARG(uap, data), sizeof ds); 460 break; 461 462 case LINUX_DVD_AUTH: 463 error = copyin(SCARG(uap, data), &dai, sizeof dai); 464 if (error) 465 break; 466 error = ioctlf(fp, DVD_AUTH, (caddr_t)&dai, l); 467 if (error) 468 break; 469 error = copyout(&dai, SCARG(uap, data), sizeof dai); 470 break; 471 472 473 default: 474 DPRINTF(("linux_ioctl: unimplemented ioctl %08lx\n", com)); 475 error = EINVAL; 476 } 477 478 FILE_UNUSE(fp, l); 479 free(u1, M_TEMP); 480 free(u2, M_TEMP); 481 return error; 482 } 483