1 /* $NetBSD: linux_cdrom.c,v 1.24 2007/10/19 18:52:10 njoly 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.24 2007/10/19 18:52:10 njoly 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(l, uap, retval) 97 struct lwp *l; 98 struct linux_sys_ioctl_args /* { 99 syscallarg(int) fd; 100 syscallarg(u_long) com; 101 syscallarg(void *) data; 102 } */ *uap; 103 register_t *retval; 104 { 105 int error, idata; 106 u_long com, ncom; 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 ioc_read_toc_entry_buf tt_toc_entry; 141 struct ioc_read_subchannel_buf tt_subchannel; 142 struct ioc_vol tt_vol; 143 } *u2; 144 145 #define t_blocks u2->tt_blocks 146 #define t_msf u2->tt_msf 147 #define t_track u2->tt_track 148 #define t_header u2->tt_header 149 #define t_toc_entry u2->tt_toc_entry 150 #define t_subchannel u2->tt_subchannel 151 #define t_vol u2->tt_vol 152 153 struct cd_toc_entry *entry; 154 struct proc *p = l->l_proc; 155 156 fdp = p->p_fd; 157 if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) 158 return (EBADF); 159 160 FILE_USE(fp); 161 162 com = SCARG(uap, com); 163 ioctlf = fp->f_ops->fo_ioctl; 164 retval[0] = error = 0; 165 166 u1 = malloc(sizeof(*u1), M_TEMP, M_WAITOK); 167 u2 = malloc(sizeof(*u2), M_TEMP, M_WAITOK); 168 169 switch(com) { 170 case LINUX_CDROMPLAYMSF: 171 error = copyin(SCARG(uap, data), &l_msf, sizeof l_msf); 172 if (error) 173 break; 174 175 t_msf.start_m = l_msf.cdmsf_min0; 176 t_msf.start_s = l_msf.cdmsf_sec0; 177 t_msf.start_f = l_msf.cdmsf_frame0; 178 t_msf.end_m = l_msf.cdmsf_min1; 179 t_msf.end_s = l_msf.cdmsf_sec1; 180 t_msf.end_f = l_msf.cdmsf_frame1; 181 182 error = ioctlf(fp, CDIOCPLAYMSF, (void *)&t_msf, l); 183 break; 184 185 case LINUX_CDROMPLAYTRKIND: 186 error = copyin(SCARG(uap, data), &l_ti, sizeof l_ti); 187 if (error) 188 break; 189 190 t_track.start_track = l_ti.cdti_trk0; 191 t_track.start_index = l_ti.cdti_ind0; 192 t_track.end_track = l_ti.cdti_trk1; 193 t_track.end_index = l_ti.cdti_ind1; 194 195 error = ioctlf(fp, CDIOCPLAYTRACKS, (void *)&t_track, l); 196 break; 197 198 case LINUX_CDROMREADTOCHDR: 199 error = ioctlf(fp, CDIOREADTOCHEADER, (void *)&t_header, l); 200 if (error) 201 break; 202 203 l_tochdr.cdth_trk0 = t_header.starting_track; 204 l_tochdr.cdth_trk1 = t_header.ending_track; 205 206 error = copyout(&l_tochdr, SCARG(uap, data), sizeof l_tochdr); 207 break; 208 209 case LINUX_CDROMREADTOCENTRY: 210 error = copyin(SCARG(uap, data), &l_tocentry, 211 sizeof l_tocentry); 212 if (error) 213 break; 214 215 t_toc_entry.req.address_format = l_tocentry.cdte_format; 216 t_toc_entry.req.starting_track = l_tocentry.cdte_track; 217 t_toc_entry.req.data_len = sizeof *entry; 218 t_toc_entry.req.data = NULL; 219 220 error = ioctlf(fp, CDIOREADTOCENTRIES_BUF, &t_toc_entry, l); 221 if (error) 222 break; 223 224 l_tocentry.cdte_adr = t_toc_entry.entry[0].addr_type; 225 l_tocentry.cdte_ctrl = t_toc_entry.entry[0].control; 226 if (bsd_to_linux_msf_lba(t_toc_entry.entry[0].addr_type, 227 &t_toc_entry.entry[0].addr, &l_tocentry.cdte_addr) < 0) { 228 DPRINTF(("linux_ioctl: unknown format msf/lba\n")); 229 error = EINVAL; 230 break; 231 } 232 233 error = copyout(&l_tocentry, SCARG(uap, data), 234 sizeof l_tocentry); 235 break; 236 237 case LINUX_CDROMVOLCTRL: 238 error = copyin(SCARG(uap, data), &l_volctrl, sizeof l_volctrl); 239 if (error) 240 break; 241 242 t_vol.vol[0] = l_volctrl.channel0; 243 t_vol.vol[1] = l_volctrl.channel1; 244 t_vol.vol[2] = l_volctrl.channel2; 245 t_vol.vol[3] = l_volctrl.channel3; 246 247 error = ioctlf(fp, CDIOCSETVOL, (void *)&t_vol, l); 248 break; 249 250 case LINUX_CDROMVOLREAD: 251 error = ioctlf(fp, CDIOCGETVOL, (void *)&t_vol, l); 252 if (error) 253 break; 254 255 l_volctrl.channel0 = t_vol.vol[0]; 256 l_volctrl.channel1 = t_vol.vol[1]; 257 l_volctrl.channel2 = t_vol.vol[2]; 258 l_volctrl.channel3 = t_vol.vol[3]; 259 260 error = copyout(&l_volctrl, SCARG(uap, data), sizeof l_volctrl); 261 break; 262 263 case LINUX_CDROMSUBCHNL: 264 error = copyin(SCARG(uap, data), &l_subchnl, sizeof l_subchnl); 265 if (error) 266 break; 267 268 t_subchannel.req.address_format = CD_MSF_FORMAT; 269 t_subchannel.req.track = 0; 270 t_subchannel.req.data_format = l_subchnl.cdsc_format; 271 t_subchannel.req.data_len = sizeof t_subchannel.info; 272 t_subchannel.req.data = NULL; 273 DPRINTF(("linux_ioctl: CDROMSUBCHNL %d %d\n", 274 l_subchnl.cdsc_format, l_subchnl.cdsc_trk)); 275 276 error = ioctlf(fp, CDIOCREADSUBCHANNEL_BUF, &t_subchannel, l); 277 if (error) 278 break; 279 280 l_subchnl.cdsc_audiostatus = t_subchannel.info.header.audio_status; 281 l_subchnl.cdsc_adr = t_subchannel.info.what.position.addr_type; 282 l_subchnl.cdsc_ctrl = t_subchannel.info.what.position.control; 283 l_subchnl.cdsc_ind = t_subchannel.info.what.position.index_number; 284 285 DPRINTF(("linux_ioctl: CDIOCREADSUBCHANNEL %d %d %d\n", 286 t_subchannel.info.header.audio_status, 287 t_subchannel.info.header.data_len[0], 288 t_subchannel.info.header.data_len[1])); 289 DPRINTF(("(more) %d %d %d %d %d\n", 290 t_subchannel.info.what.position.data_format, 291 t_subchannel.info.what.position.control, 292 t_subchannel.info.what.position.addr_type, 293 t_subchannel.info.what.position.track_number, 294 t_subchannel.info.what.position.index_number)); 295 296 if (bsd_to_linux_msf_lba(t_subchannel.req.address_format, 297 &t_subchannel.info.what.position.absaddr, 298 &l_subchnl.cdsc_absaddr) < 0 || 299 bsd_to_linux_msf_lba(t_subchannel.req.address_format, 300 &t_subchannel.info.what.position.reladdr, 301 &l_subchnl.cdsc_reladdr) < 0) { 302 DPRINTF(("linux_ioctl: unknown format msf/lba\n")); 303 error = EINVAL; 304 break; 305 } 306 307 error = copyout(&l_subchnl, SCARG(uap, data), sizeof l_subchnl); 308 break; 309 310 case LINUX_CDROMPLAYBLK: 311 error = copyin(SCARG(uap, data), &l_blk, sizeof l_blk); 312 if (error) 313 break; 314 315 t_blocks.blk = l_blk.from; 316 t_blocks.len = l_blk.len; 317 318 error = ioctlf(fp, CDIOCPLAYBLOCKS, (void *)&t_blocks, l); 319 break; 320 321 case LINUX_CDROMEJECT_SW: 322 error = copyin(SCARG(uap, data), &idata, sizeof idata); 323 if (error) 324 break; 325 326 if (idata == 1) 327 ncom = CDIOCALLOW; 328 else 329 ncom = CDIOCPREVENT; 330 error = ioctlf(fp, ncom, NULL, l); 331 break; 332 333 case LINUX_CDROMPAUSE: 334 error = ioctlf(fp, CDIOCPAUSE, NULL, l); 335 break; 336 337 case LINUX_CDROMRESUME: 338 error = ioctlf(fp, CDIOCRESUME, NULL, l); 339 break; 340 341 case LINUX_CDROMSTOP: 342 error = ioctlf(fp, CDIOCSTOP, NULL, l); 343 break; 344 345 case LINUX_CDROMSTART: 346 error = ioctlf(fp, CDIOCSTART, NULL, l); 347 break; 348 349 case LINUX_CDROMEJECT: 350 error = ioctlf(fp, CDIOCEJECT, NULL, l); 351 break; 352 353 case LINUX_CDROMRESET: 354 error = ioctlf(fp, CDIOCRESET, NULL, l); 355 break; 356 357 case LINUX_CDROMMULTISESSION: 358 error = copyin(SCARG(uap, data), &l_session, sizeof l_session); 359 if (error) 360 break; 361 362 error = ioctlf(fp, CDIOREADTOCHEADER, (void *)&t_header, l); 363 if (error) 364 break; 365 366 t_toc_entry.req.address_format = l_session.addr_format; 367 t_toc_entry.req.starting_track = 0; 368 t_toc_entry.req.data_len = sizeof t_toc_entry.entry; 369 t_toc_entry.req.data = NULL; 370 371 error = ioctlf(fp, CDIOREADTOCENTRIES_BUF, &t_toc_entry, l); 372 if (error) 373 break; 374 375 if (bsd_to_linux_msf_lba(l_session.addr_format, 376 &t_toc_entry.entry[0].addr, &l_session.addr) < 0) { 377 error = EINVAL; 378 break; 379 } 380 381 l_session.xa_flag = 382 t_header.starting_track != t_header.ending_track; 383 384 error = copyout(&l_session, SCARG(uap, data), sizeof l_session); 385 break; 386 387 case LINUX_CDROMCLOSETRAY: 388 error = ioctlf(fp, CDIOCCLOSE, NULL, l); 389 break; 390 391 case LINUX_CDROM_LOCKDOOR: 392 ncom = SCARG(uap, data) != 0 ? CDIOCPREVENT : CDIOCALLOW; 393 error = ioctlf(fp, ncom, NULL, l); 394 break; 395 396 case LINUX_CDROM_SET_OPTIONS: 397 case LINUX_CDROM_CLEAR_OPTIONS: 398 /* whatever you say */ 399 break; 400 401 case LINUX_CDROM_DEBUG: 402 ncom = SCARG(uap, data) != 0 ? CDIOCSETDEBUG : CDIOCCLRDEBUG; 403 error = ioctlf(fp, ncom, NULL, l); 404 break; 405 406 case LINUX_CDROM_SELECT_SPEED: 407 case LINUX_CDROM_SELECT_DISC: 408 case LINUX_CDROM_MEDIA_CHANGED: 409 case LINUX_CDROM_DRIVE_STATUS: 410 case LINUX_CDROM_DISC_STATUS: 411 case LINUX_CDROM_CHANGER_NSLOTS: 412 case LINUX_CDROM_GET_CAPABILITY: 413 error = ENOSYS; 414 break; 415 416 case LINUX_DVD_READ_STRUCT: 417 error = copyin(SCARG(uap, data), &ds, sizeof ds); 418 if (error) 419 break; 420 error = ioctlf(fp, DVD_READ_STRUCT, (void *)&ds, l); 421 if (error) 422 break; 423 error = copyout(&ds, SCARG(uap, data), sizeof ds); 424 break; 425 426 case LINUX_DVD_WRITE_STRUCT: 427 error = copyin(SCARG(uap, data), &ds, sizeof ds); 428 if (error) 429 break; 430 error = ioctlf(fp, DVD_WRITE_STRUCT, (void *)&ds, l); 431 if (error) 432 break; 433 error = copyout(&ds, SCARG(uap, data), sizeof ds); 434 break; 435 436 case LINUX_DVD_AUTH: 437 error = copyin(SCARG(uap, data), &dai, sizeof dai); 438 if (error) 439 break; 440 error = ioctlf(fp, DVD_AUTH, (void *)&dai, l); 441 if (error) 442 break; 443 error = copyout(&dai, SCARG(uap, data), sizeof dai); 444 break; 445 446 447 default: 448 DPRINTF(("linux_ioctl: unimplemented ioctl %08lx\n", com)); 449 error = EINVAL; 450 } 451 452 FILE_UNUSE(fp, l); 453 free(u1, M_TEMP); 454 free(u2, M_TEMP); 455 return error; 456 } 457