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