1 /* $NetBSD: ossaudio.c,v 1.11 1997/05/19 23:05:14 augustss Exp $ */ 2 #include <sys/param.h> 3 #include <sys/proc.h> 4 #include <sys/systm.h> 5 #include <sys/file.h> 6 #include <sys/vnode.h> 7 #include <sys/filedesc.h> 8 #include <sys/ioctl.h> 9 #include <sys/mount.h> 10 #include <sys/audioio.h> 11 12 #include <sys/syscallargs.h> 13 14 #include <compat/ossaudio/ossaudio.h> 15 #include <compat/ossaudio/ossaudiovar.h> 16 17 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *)); 18 19 int 20 oss_ioctl_audio(p, uap, retval) 21 register struct proc *p; 22 register struct oss_sys_ioctl_args /* { 23 syscallarg(int) fd; 24 syscallarg(u_long) com; 25 syscallarg(caddr_t) data; 26 } */ *uap; 27 register_t *retval; 28 { 29 register struct file *fp; 30 register struct filedesc *fdp; 31 u_long com; 32 struct audio_info tmpinfo; 33 int idat; 34 int error; 35 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 36 37 fdp = p->p_fd; 38 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 39 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 40 return (EBADF); 41 42 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 43 return (EBADF); 44 45 ioctlf = fp->f_ops->fo_ioctl; 46 47 com = SCARG(uap, com); 48 retval[0] = 0; 49 50 switch (com) { 51 case OSS_SNDCTL_DSP_RESET: 52 error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p); 53 if (error) 54 return error; 55 break; 56 case OSS_SNDCTL_DSP_SYNC: 57 case OSS_SNDCTL_DSP_POST: 58 error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p); 59 if (error) 60 return error; 61 break; 62 case OSS_SNDCTL_DSP_SPEED: 63 AUDIO_INITINFO(&tmpinfo); 64 error = copyin(SCARG(uap, data), &idat, sizeof idat); 65 if (error) 66 return error; 67 tmpinfo.play.sample_rate = 68 tmpinfo.record.sample_rate = idat; 69 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 70 /* fall into ... */ 71 case OSS_SOUND_PCM_READ_RATE: 72 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 73 if (error) 74 return error; 75 idat = tmpinfo.play.sample_rate; 76 error = copyout(&idat, SCARG(uap, data), sizeof idat); 77 if (error) 78 return error; 79 break; 80 case OSS_SNDCTL_DSP_STEREO: 81 AUDIO_INITINFO(&tmpinfo); 82 error = copyin(SCARG(uap, data), &idat, sizeof idat); 83 if (error) 84 return error; 85 tmpinfo.play.channels = 86 tmpinfo.record.channels = idat ? 2 : 1; 87 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 88 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 89 if (error) 90 return error; 91 idat = tmpinfo.play.channels - 1; 92 error = copyout(&idat, SCARG(uap, data), sizeof idat); 93 if (error) 94 return error; 95 break; 96 case OSS_SNDCTL_DSP_GETBLKSIZE: 97 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 98 if (error) 99 return error; 100 idat = tmpinfo.blocksize; 101 error = copyout(&idat, SCARG(uap, data), sizeof idat); 102 if (error) 103 return error; 104 break; 105 case OSS_SNDCTL_DSP_SETFMT: 106 AUDIO_INITINFO(&tmpinfo); 107 error = copyin(SCARG(uap, data), &idat, sizeof idat); 108 if (error) 109 return error; 110 switch (idat) { 111 case OSS_AFMT_MU_LAW: 112 tmpinfo.play.precision = 113 tmpinfo.record.precision = 8; 114 tmpinfo.play.encoding = 115 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; 116 break; 117 case OSS_AFMT_A_LAW: 118 tmpinfo.play.precision = 119 tmpinfo.record.precision = 8; 120 tmpinfo.play.encoding = 121 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; 122 break; 123 case OSS_AFMT_U8: 124 tmpinfo.play.precision = 125 tmpinfo.record.precision = 8; 126 tmpinfo.play.encoding = 127 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; 128 break; 129 case OSS_AFMT_S8: 130 tmpinfo.play.precision = 131 tmpinfo.record.precision = 8; 132 tmpinfo.play.encoding = 133 tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR; 134 break; 135 case OSS_AFMT_S16_LE: 136 tmpinfo.play.precision = 137 tmpinfo.record.precision = 16; 138 tmpinfo.play.encoding = 139 tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR_LE; 140 break; 141 case OSS_AFMT_S16_BE: 142 tmpinfo.play.precision = 143 tmpinfo.record.precision = 16; 144 tmpinfo.play.encoding = 145 tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR_BE; 146 break; 147 case OSS_AFMT_U16_LE: 148 tmpinfo.play.precision = 149 tmpinfo.record.precision = 16; 150 tmpinfo.play.encoding = 151 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; 152 break; 153 case OSS_AFMT_U16_BE: 154 tmpinfo.play.precision = 155 tmpinfo.record.precision = 16; 156 tmpinfo.play.encoding = 157 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; 158 break; 159 default: 160 return EINVAL; 161 } 162 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 163 /* fall into ... */ 164 case OSS_SOUND_PCM_READ_BITS: 165 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 166 if (error) 167 return error; 168 switch (tmpinfo.play.encoding) { 169 case AUDIO_ENCODING_ULAW: 170 idat = OSS_AFMT_MU_LAW; 171 break; 172 case AUDIO_ENCODING_ALAW: 173 idat = OSS_AFMT_A_LAW; 174 break; 175 case AUDIO_ENCODING_LINEAR_LE: 176 if (tmpinfo.play.precision == 16) 177 idat = OSS_AFMT_S16_LE; 178 else 179 idat = OSS_AFMT_S8; 180 break; 181 case AUDIO_ENCODING_LINEAR_BE: 182 if (tmpinfo.play.precision == 16) 183 idat = OSS_AFMT_S16_BE; 184 else 185 idat = OSS_AFMT_S8; 186 break; 187 case AUDIO_ENCODING_ULINEAR_LE: 188 if (tmpinfo.play.precision == 16) 189 idat = OSS_AFMT_U16_LE; 190 else 191 idat = OSS_AFMT_U8; 192 break; 193 case AUDIO_ENCODING_ULINEAR_BE: 194 if (tmpinfo.play.precision == 16) 195 idat = OSS_AFMT_U16_BE; 196 else 197 idat = OSS_AFMT_U8; 198 break; 199 case AUDIO_ENCODING_ADPCM: 200 idat = OSS_AFMT_IMA_ADPCM; 201 break; 202 } 203 error = copyout(&idat, SCARG(uap, data), sizeof idat); 204 if (error) 205 return error; 206 break; 207 case OSS_SNDCTL_DSP_CHANNELS: 208 AUDIO_INITINFO(&tmpinfo); 209 error = copyin(SCARG(uap, data), &idat, sizeof idat); 210 if (error) 211 return error; 212 tmpinfo.play.channels = 213 tmpinfo.record.channels = idat; 214 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 215 /* fall into ... */ 216 case OSS_SOUND_PCM_READ_CHANNELS: 217 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 218 if (error) 219 return error; 220 idat = tmpinfo.play.channels; 221 error = copyout(&idat, SCARG(uap, data), sizeof idat); 222 if (error) 223 return error; 224 break; 225 case OSS_SOUND_PCM_WRITE_FILTER: 226 case OSS_SOUND_PCM_READ_FILTER: 227 return EINVAL; /* XXX unimplemented */ 228 case OSS_SNDCTL_DSP_SUBDIVIDE: 229 error = copyin(SCARG(uap, data), &idat, sizeof idat); 230 if (error) 231 return error; 232 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 233 if (error) 234 return error; 235 if (idat == 0) 236 idat = tmpinfo.buffersize / tmpinfo.blocksize; 237 idat = (tmpinfo.buffersize / idat) & -4; 238 AUDIO_INITINFO(&tmpinfo); 239 tmpinfo.blocksize = idat; 240 error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 241 if (error) 242 return error; 243 idat = tmpinfo.buffersize / tmpinfo.blocksize; 244 error = copyout(&idat, SCARG(uap, data), sizeof idat); 245 if (error) 246 return error; 247 break; 248 case OSS_SNDCTL_DSP_SETFRAGMENT: 249 AUDIO_INITINFO(&tmpinfo); 250 error = copyin(SCARG(uap, data), &idat, sizeof idat); 251 if (error) 252 return error; 253 if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) 254 return EINVAL; 255 tmpinfo.blocksize = 1 << (idat & 0xffff); 256 tmpinfo.hiwat = (idat >> 16) & 0xffff; 257 (void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p); 258 error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p); 259 if (error) 260 return error; 261 idat = tmpinfo.blocksize; 262 error = copyout(&idat, SCARG(uap, data), sizeof idat); 263 if (error) 264 return error; 265 break; 266 case OSS_SNDCTL_DSP_GETFMTS: 267 idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE; 268 error = copyout(&idat, SCARG(uap, data), sizeof idat); 269 if (error) 270 return error; 271 break; 272 case OSS_SNDCTL_DSP_GETOSPACE: 273 case OSS_SNDCTL_DSP_GETISPACE: 274 case OSS_SNDCTL_DSP_NONBLOCK: 275 return EINVAL; /* XXX unimplemented */ 276 case OSS_SNDCTL_DSP_GETCAPS: 277 idat = 0; /* XXX full duplex info should be set */ 278 error = copyout(&idat, SCARG(uap, data), sizeof idat); 279 if (error) 280 return error; 281 break; 282 case OSS_SNDCTL_DSP_GETTRIGGER: 283 case OSS_SNDCTL_DSP_SETTRIGGER: 284 case OSS_SNDCTL_DSP_GETIPTR: 285 case OSS_SNDCTL_DSP_GETOPTR: 286 case OSS_SNDCTL_DSP_MAPINBUF: 287 case OSS_SNDCTL_DSP_MAPOUTBUF: 288 case OSS_SNDCTL_DSP_SETSYNCRO: 289 case OSS_SNDCTL_DSP_SETDUPLEX: 290 case OSS_SNDCTL_DSP_PROFILE: 291 return EINVAL; /* XXX unimplemented */ 292 default: 293 return EINVAL; 294 } 295 296 return 0; 297 } 298 299 /* If the NetBSD mixer device should have more than 32 devices 300 * some will not be available to Linux */ 301 #define NETBSD_MAXDEVS 32 302 struct audiodevinfo { 303 int done; 304 dev_t dev; 305 int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], 306 rdevmap[NETBSD_MAXDEVS]; 307 u_long devmask, recmask, stereomask; 308 u_long caps, source; 309 }; 310 311 /* 312 * Collect the audio device information to allow faster 313 * emulation of the Linux mixer ioctls. Cache the information 314 * to eliminate the overhead of repeating all the ioctls needed 315 * to collect the information. 316 */ 317 static struct audiodevinfo * 318 getdevinfo(fp, p) 319 struct file *fp; 320 struct proc *p; 321 { 322 mixer_devinfo_t mi; 323 int i; 324 static struct { 325 char *name; 326 int code; 327 } *dp, devs[] = { 328 { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, 329 { AudioNline, OSS_SOUND_MIXER_LINE }, 330 { AudioNcd, OSS_SOUND_MIXER_CD }, 331 { AudioNdac, OSS_SOUND_MIXER_PCM }, 332 { AudioNrecord, OSS_SOUND_MIXER_IMIX }, 333 { AudioNvolume, OSS_SOUND_MIXER_VOLUME }, 334 { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, 335 { AudioNbass, OSS_SOUND_MIXER_BASS }, 336 { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, 337 /* { AudioNheadphone, ?? },*/ 338 { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, 339 { AudioNinput, OSS_SOUND_MIXER_IGAIN }, 340 { AudioNmaster, OSS_SOUND_MIXER_SPEAKER }, 341 /* { AudioNstereo, ?? },*/ 342 /* { AudioNmono, ?? },*/ 343 { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, 344 /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ 345 { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, 346 /* { AudioNmixerout, ?? },*/ 347 { 0, -1 } 348 }; 349 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) = 350 fp->f_ops->fo_ioctl; 351 struct vnode *vp; 352 struct vattr va; 353 static struct audiodevinfo devcache = { 0 }; 354 struct audiodevinfo *di = &devcache; 355 356 /* Figure out what device it is so we can check if the 357 * cached data is valid. 358 */ 359 vp = (struct vnode *)fp->f_data; 360 if (vp->v_type != VCHR) 361 return 0; 362 if (VOP_GETATTR(vp, &va, p->p_ucred, p)) 363 return 0; 364 if (di->done && di->dev == va.va_rdev) 365 return di; 366 367 di->done = 1; 368 di->dev = va.va_rdev; 369 di->devmask = 0; 370 di->recmask = 0; 371 di->stereomask = 0; 372 di->source = -1; 373 di->caps = 0; 374 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 375 di->devmap[i] = -1; 376 for(i = 0; i < NETBSD_MAXDEVS; i++) 377 di->rdevmap[i] = -1; 378 for(i = 0; i < NETBSD_MAXDEVS; i++) { 379 mi.index = i; 380 if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0) 381 break; 382 switch(mi.type) { 383 case AUDIO_MIXER_VALUE: 384 for(dp = devs; dp->name; dp++) 385 if (strcmp(dp->name, mi.label.name) == 0) 386 break; 387 if (dp->code >= 0) { 388 di->devmap[dp->code] = i; 389 di->rdevmap[i] = dp->code; 390 di->devmask |= 1 << dp->code; 391 if (mi.un.v.num_channels == 2) 392 di->stereomask |= 1 << dp->code; 393 } 394 break; 395 case AUDIO_MIXER_ENUM: 396 if (strcmp(mi.label.name, AudioNsource) == 0) { 397 int j; 398 di->source = i; 399 for(j = 0; j < mi.un.e.num_mem; j++) 400 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 401 di->caps = OSS_SOUND_CAP_EXCL_INPUT; 402 } 403 break; 404 case AUDIO_MIXER_SET: 405 if (strcmp(mi.label.name, AudioNsource) == 0) { 406 int j; 407 di->source = i; 408 for(j = 0; j < mi.un.s.num_mem; j++) { 409 int k, mask = mi.un.s.member[j].mask; 410 if (mask) { 411 for(k = 0; !(mask & 1); mask >>= 1, k++) 412 ; 413 di->recmask |= 1 << di->rdevmap[k]; 414 } 415 } 416 } 417 break; 418 } 419 } 420 return di; 421 } 422 423 int 424 oss_ioctl_mixer(p, uap, retval) 425 register struct proc *p; 426 register struct oss_sys_ioctl_args /* { 427 syscallarg(int) fd; 428 syscallarg(u_long) com; 429 syscallarg(caddr_t) data; 430 } */ *uap; 431 register_t *retval; 432 { 433 struct file *fp; 434 struct filedesc *fdp; 435 u_long com; 436 struct audiodevinfo *di; 437 mixer_ctrl_t mc; 438 int idat; 439 int i; 440 int error; 441 int l, r, n; 442 int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)); 443 444 fdp = p->p_fd; 445 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 446 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 447 return (EBADF); 448 449 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 450 return (EBADF); 451 452 com = SCARG(uap, com); 453 retval[0] = 0; 454 455 di = getdevinfo(fp, p); 456 if (di == 0) 457 return EINVAL; 458 459 ioctlf = fp->f_ops->fo_ioctl; 460 switch (com) { 461 case OSS_SOUND_MIXER_READ_RECSRC: 462 if (di->source == -1) 463 return EINVAL; 464 mc.dev = di->source; 465 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 466 mc.type = AUDIO_MIXER_ENUM; 467 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 468 if (error) 469 return error; 470 idat = 1 << di->rdevmap[mc.un.ord]; 471 } else { 472 int k; 473 unsigned int mask; 474 mc.type = AUDIO_MIXER_SET; 475 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 476 if (error) 477 return error; 478 idat = 0; 479 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 480 if (mask & 1) 481 idat |= 1 << di->rdevmap[k]; 482 } 483 break; 484 case OSS_SOUND_MIXER_READ_DEVMASK: 485 idat = di->devmask; 486 break; 487 case OSS_SOUND_MIXER_READ_RECMASK: 488 idat = di->recmask; 489 break; 490 case OSS_SOUND_MIXER_READ_STEREODEVS: 491 idat = di->stereomask; 492 break; 493 case OSS_SOUND_MIXER_READ_CAPS: 494 idat = di->caps; 495 break; 496 case OSS_SOUND_MIXER_WRITE_RECSRC: 497 if (di->source == -1) 498 return EINVAL; 499 mc.dev = di->source; 500 error = copyin(SCARG(uap, data), &idat, sizeof idat); 501 if (error) 502 return error; 503 if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { 504 mc.type = AUDIO_MIXER_ENUM; 505 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) 506 if (idat & (1 << i)) 507 break; 508 if (i >= OSS_SOUND_MIXER_NRDEVICES || 509 di->devmap[i] == -1) 510 return EINVAL; 511 mc.un.ord = di->devmap[i]; 512 } else { 513 mc.type = AUDIO_MIXER_SET; 514 mc.un.mask = 0; 515 for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { 516 if (idat & (1 << i)) { 517 if (di->devmap[i] == -1) 518 return EINVAL; 519 mc.un.mask |= 1 << di->devmap[i]; 520 } 521 } 522 } 523 return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 524 default: 525 if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && 526 com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { 527 n = OSS_GET_DEV(com); 528 if (di->devmap[n] == -1) 529 return EINVAL; 530 mc.dev = di->devmap[n]; 531 mc.type = AUDIO_MIXER_VALUE; 532 doread: 533 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 534 error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p); 535 if (error) 536 return error; 537 if (mc.type != AUDIO_MIXER_VALUE) 538 return EINVAL; 539 if (mc.un.value.num_channels != 2) { 540 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 541 } else { 542 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 543 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 544 } 545 l = l * 100 / 255; 546 r = r * 100 / 255; 547 idat = l | (r << 8); 548 break; 549 } else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && 550 com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { 551 n = OSS_GET_DEV(com); 552 if (di->devmap[n] == -1) 553 return EINVAL; 554 error = copyin(SCARG(uap, data), &idat, sizeof idat); 555 if (error) 556 return error; 557 l = (idat & 0xff) * 255 / 100; 558 r = (idat >> 8) * 255 / 100; 559 mc.dev = di->devmap[n]; 560 mc.type = AUDIO_MIXER_VALUE; 561 if (di->stereomask & (1<<n)) { 562 mc.un.value.num_channels = 2; 563 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 564 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 565 } else { 566 mc.un.value.num_channels = 1; 567 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 568 } 569 error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p); 570 if (error) 571 return error; 572 goto doread; 573 } else 574 return EINVAL; 575 } 576 return copyout(&idat, SCARG(uap, data), sizeof idat); 577 } 578 579 /* XXX hook for sequencer emulation */ 580 int 581 oss_ioctl_sequencer(p, uap, retval) 582 register struct proc *p; 583 register struct oss_sys_ioctl_args /* { 584 syscallarg(int) fd; 585 syscallarg(u_long) com; 586 syscallarg(caddr_t) data; 587 } */ *uap; 588 register_t *retval; 589 { 590 register struct file *fp; 591 register struct filedesc *fdp; 592 #if 0 593 u_long com; 594 int idat; 595 int error; 596 #endif 597 598 fdp = p->p_fd; 599 if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || 600 (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) 601 return (EBADF); 602 603 if ((fp->f_flag & (FREAD | FWRITE)) == 0) 604 return (EBADF); 605 606 #if 0 607 com = SCARG(uap, com); 608 #endif 609 retval[0] = 0; 610 611 return EINVAL; 612 } 613