📄 sound.c
字号:
case SND_DEV_CTL : /* mixer ... */ return 0 ; /* always succeed */ case SND_DEV_STATUS : /* implemented right here */ init_status(&pcm_info[unit]); d->status_ptr = 0 ; return 0 ; default: if (d->open == NULL) { printf("open: unit %d not configured, perhaps you want unit %d ?\n", unit, unit+1 ); return (ENXIO) ; } else return d->open(i_dev, flags, mode, p); } return ENXIO ;}static intsndclose(dev_t i_dev, int flags, int mode, struct proc * p){ int dev, unit ; snddev_info *d; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("close snd%d subdev %d\n", unit, dev & 0xf)); if (d == NULL) return (ENXIO) ; switch(dev & 0xf) { /* only those for which close makes sense */ case SND_DEV_SEQ:#if 0 /* XXX hook for opl3 support */ if (d->synth_base) return opl3_close(i_dev, flags, mode, p); else#endif return ENXIO ; case SND_DEV_AUDIO : case SND_DEV_DSP : case SND_DEV_DSP16 : if (d->close) return d->close(i_dev, flags, mode, p); } return 0 ;}static intsndread(dev_t i_dev, struct uio * buf, int flag){ int ret, dev, unit; snddev_info *d ; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); if (d == NULL) return ENXIO ; if ( (dev & 0x0f) == SND_DEV_STATUS ) { int l, c; u_char *p; l = buf->uio_resid; s=spltty(); c = status_len - d->status_ptr ; if (c < 0) /* should not happen! */ c = 0 ; if (c < l) l = c ; p = status_buf + d->status_ptr ; d->status_ptr += l ; splx(s); return uiomove(p, l, buf) ; } if (d->read) /* device-specific read */ return d->read(i_dev, buf, flag); /* * the generic read routine. device-specific stuff should only * be in the dma-handling procedures. */ s = spltty(); if ( d->flags & SND_F_READING ) { /* another reader is in, deny request */ splx(s); DDB(printf("read denied, another reader is in\n")); /* * sleep for a while to avoid killing the machine. */ tsleep( (void *)s, PZERO, "sndar", hz ) ; return EBUSY ; } if ( ! FULL_DUPLEX(d) ) { /* half duplex */ if ( d->flags & SND_F_WRITING ) { /* another writer is in, deny request */ splx(s); DDB(printf("read denied, half duplex and a writer is in\n")); tsleep( (void *)s, PZERO, "sndaw", hz ) ; return EBUSY ; } while ( d->dbuf_out.dl ) { /* * we have a pending dma operation, post a read request * and wait for the write to complete. */ d->flags |= SND_F_READING ; DEB(printf("sndread: sleeping waiting for write to end\n")); ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO | PCATCH , "sndrdw", hz ) ; if (ret == ERESTART || ret == EINTR) { d->flags &= ~SND_F_READING ; splx(s); return EINTR ; } } } d->flags |= SND_F_READING ; splx(s); return dsp_read_body(d, buf);}static intsndwrite(dev_t i_dev, struct uio * buf, int flag){ int ret, dev, unit; snddev_info *d; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); if (d == NULL) return (ENXIO) ; switch( dev & 0x0f) { /* only writeable devices */ case SND_DEV_MIDIN: /* XXX is this writable ? */ case SND_DEV_SEQ : case SND_DEV_SEQ2 : case SND_DEV_DSP : case SND_DEV_DSP16 : case SND_DEV_AUDIO : break ; default: return EPERM ; /* for non-writeable devices ; */ } if (d->write) return d->write(i_dev, buf, flag); /* * Otherwise, use the generic write routine. device-specific * stuff should only be in the dma-handling procedures. */ s = spltty(); if ( d->flags & SND_F_WRITING ) { /* another writer is in, deny request */ splx(s); DDB(printf("write denied, another writer is in\n")); tsleep( (void *)s, PZERO , "sndaw", hz ) ; return EBUSY ; } if ( ! FULL_DUPLEX(d) ) { /* half duplex */ if ( d->flags & SND_F_READING ) { /* another reader is in, deny request */ splx(s); DDB(printf("write denied, half duplex and a reader is in\n")); tsleep( (void *)s, PZERO, "sndar", hz ) ; return EBUSY ; } while ( d->dbuf_in.dl ) { /* * we have a pending read dma. Post a write request * and wait for the read to complete (in fact I could * abort the read dma... */ d->flags |= SND_F_WRITING ; DEB(printf("sndwrite: sleeping waiting for read to end\n")); ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO | PCATCH , "sndwr", hz ) ; if (ret == ERESTART || ret == EINTR) { d->flags &= ~SND_F_WRITING ; splx(s); return EINTR ; } } } d->flags |= SND_F_WRITING ; splx(s); return dsp_write_body(d, buf);}/* * generic sound ioctl. Functions of the default driver can be * overridden by the device-specific ioctl call. * If a device-specific call returns ENOSYS (Function not implemented), * the default driver is called. Otherwise, the returned value * is passed up. * * The default handler, for many parameters, sets the value in the * descriptor, sets SND_F_INIT, and calls the callback function with * reason INIT. If successful, the callback returns 1 and the caller * can update the parameter. */static intsndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p){ int ret = ENOSYS, dev, unit ; snddev_info *d; u_long s; dev = minor(i_dev); d = get_snddev_info(dev, &unit); if (d == NULL) return (ENXIO) ; if ( (dev & 0x0f) == SND_DEV_SEQ ) { /* sequencer. Hack... */#if 0 if (d->synth_base) return opl3_ioctl(i_dev, cmd, arg, mode, p) ; else#endif return ENXIO ; } if (d->ioctl) ret = d->ioctl(dev, cmd, arg, mode, p); if (ret != ENOSYS) return ret ; /* * pass control to the default ioctl handler. Set ret to 0 now. */ ret = 0 ; /* * The linux ioctl interface for the sound driver has a thousand * different calls, and it is unpractical to put the names in * the switch(). So we have some tests before for common routines, * such as the ones related to the mixer. But we really ought * to redesign the interface! * * Reading from the mixer just requires to look at the cached * copy in d->mix_levels[dev], so this routine should cover * practically all needs for mixer reading. */ if ( (cmd & MIXER_READ(0)) == MIXER_READ(0) && (cmd & 0xff) < 32 ) { int dev = cmd & 0x1f ; if ( d->mix_devs & (1<<dev) ) { /* supported */ *(int *)arg = d->mix_levels[dev]; return 0 ; } else return EINVAL ; } /* * all routines are called with int. blocked. Make sure that * ints are re-enabled when calling slow or blocking functions! */ s = spltty(); switch(cmd) { /* * we start with the new ioctl interface. */ case AIONWRITE : /* how many bytes can write ? */ if (d->dbuf_out.dl) dsp_wr_dmaupdate(&(d->dbuf_out)); *(int *)arg = d->dbuf_out.fl; break; case AIOSSIZE : /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { RANGE (p->play_size, 40, d->dbuf_out.bufsize /4); d->play_blocksize = p->play_size & ~3 ; RANGE (p->rec_size, 40, d->dbuf_in.bufsize /4); d->rec_blocksize = p->rec_size & ~3 ; d->flags |= SND_F_HAS_SIZE ; } } splx(s); ask_init(d); /* FALLTHROUGH */ case AIOGSIZE : /* get the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; p->play_size = d->play_blocksize ; p->rec_size = d->rec_blocksize ; } break ; case AIOSFMT : { snd_chan_param *p = (snd_chan_param *)arg; d->play_speed = p->play_rate; d->rec_speed = p->play_rate; /* XXX one speed allowed */ if (p->play_format & AFMT_STEREO) d->flags |= SND_F_STEREO ; else d->flags &= ~SND_F_STEREO ; d->play_fmt = p->play_format & ~AFMT_STEREO ; d->rec_fmt = p->rec_format & ~AFMT_STEREO ; } splx(s); if (!ask_init(d)) break ; /* could not reinit */ /* FALLTHROUGH */ case AIOGFMT : { snd_chan_param *p = (snd_chan_param *)arg; p->play_rate = d->play_speed; p->rec_rate = d->rec_speed; p->play_format = d->play_fmt; p->rec_format = d->rec_fmt; if (d->flags & SND_F_STEREO) { p->play_format |= AFMT_STEREO ; p->rec_format |= AFMT_STEREO ; } } break; case AIOGCAP : /* get capabilities */ /* this should really be implemented by the driver */ { snd_capabilities *p = (snd_capabilities *)arg; p->rate_min = 5000; p->rate_max = 48000; /* default */ p->bufsize = d->bufsize; p->formats = d->audio_fmt; /* default */ p->mixers = 1 ; /* default: one mixer */ p->inputs = d->mix_devs ; p->left = p->right = 255 ; } break ; case AIOSTOP: if (*(int *)arg == AIOSYNC_PLAY) /* play */ *(int *)arg = dsp_wrabort(d, 1 /* restart */); else if (*(int *)arg == AIOSYNC_CAPTURE) *(int *)arg = dsp_rdabort(d, 1 /* restart */); else { splx(s); printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg); *(int *)arg = 0 ; } break ; case AIOSYNC: printf("AIOSYNC chan 0x%03lx pos %d unimplemented\n", ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); break; /* * here follow the standard ioctls (filio.h etc.) */ case FIONREAD : /* get # bytes to read */ if ( d->dbuf_in.dl ) dsp_rd_dmaupdate(&(d->dbuf_in)); *(int *)arg = d->dbuf_in.rl; break; case FIOASYNC: /*set/clear async i/o */ printf("FIOASYNC\n"); break; case SNDCTL_DSP_NONBLOCK : case FIONBIO : /* set/clear non-blocking i/o */ if ( *(int *)arg == 0 ) d->flags &= ~SND_F_NBIO ; else d->flags |= SND_F_NBIO ; break ; /* * Finally, here is the linux-compatible ioctl interface */#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: *(int *) arg = d->play_blocksize ; break ; case SNDCTL_DSP_SETBLKSIZE : { int t = *(int *)arg; if (t <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { RANGE (t, 40, d->dbuf_out.bufsize /4); d->play_blocksize = d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */ d->flags |= SND_F_HAS_SIZE ; } } splx(s); ask_init(d); break ; case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); dsp_wrabort(d, 1 /* restart */); dsp_rdabort(d, 1 /* restart */); break ; case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); splx(s); snd_sync(d, 1, d->dbuf_out.bufsize - 4); /* DMA does not start with <4 bytes */ break ; case SNDCTL_DSP_SPEED: d->play_speed = d->rec_speed = *(int *)arg ; splx(s); if (ask_init(d)) *(int *)arg = d->play_speed ; break ; case SNDCTL_DSP_STEREO: if ( *(int *)arg == 0 ) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 1 ) d->flags |= SND_F_STEREO ; /* stereo */ else { printf("dsp stereo: %d is invalid, assuming 1\n", *(int *)arg ); d->flags |= SND_F_STEREO ; /* stereo */ } splx(s); if (ask_init(d)) *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ; break ; case SOUND_PCM_WRITE_CHANNELS: if ( *(int *)arg == 1) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 2) d->flags |= SND_F_STEREO ; /* stereo */ else { ret = EINVAL ; break ; } splx(s); if (ask_init(d)) *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1 ; break ; case SOUND_PCM_READ_RATE: *(int *)arg = d->play_speed; break ; case SOUND_PCM_READ_CHANNELS: *(int *)arg = (d->flags & SND_F_STEREO) ? 2 : 1; break ; case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ *(int *)arg = (int)d->audio_fmt ; break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ /* * when some card (SB16) is opened RDONLY or WRONLY, * only one of the fields is set, the other becomes 0. * This makes it possible to select DMA channels at runtime. */ if (d->play_fmt) d->play_fmt = *(int *)arg ; if (d->rec_fmt) d->rec_fmt = *(int *)arg ; splx(s); if (ask_init(d)) *(int *)arg = d->play_fmt ; break ; case SNDCTL_DSP_SUBDIVIDE: /* XXX watch out, this is RW! */ DEB(printf("SNDCTL_DSP_SUBDIVIDE yet unimplemented\n");) break; case SNDCTL_DSP_SETFRAGMENT: /* XXX watch out, this is RW! */ DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -