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