⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound.c

📁 freebsd v4.4内核源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	{	    int bytes, count;	    bytes = *(int *)arg & 0xffff ;	    count = ( *(int *)arg >> 16) & 0xffff ;	    if (bytes > 15)		bytes = 15 ;	    bytes = 1 << bytes ;	    if (bytes <= 1) { /* means no blocks */		d->flags &= ~SND_F_HAS_SIZE ;	    } else {		RANGE (bytes, 40, d->dbuf_out.bufsize /4);		d->play_blocksize =		d->rec_blocksize = bytes  & ~3 ; /* align to multiple of 4 */		d->flags |= SND_F_HAS_SIZE ;	    }	    splx(s);	    ask_init(d);#if 0	    /* XXX todo: set the buffer size to the # of fragments */	    count = d->dbuf_in.bufsize / d->play_blocksize ;	    bytes = ffs(d->play_blocksize) - 1;	    /*	     * don't change arg, since it's fake anyways and some	     * programs might fail if we do.	     */	    *(int *)arg = (count << 16) | bytes ;#endif	}	break ;    case SNDCTL_DSP_GETISPACE:	/* return space available in the input queue */	{	    audio_buf_info *a = (audio_buf_info *)arg;	    snd_dbuf *b = &(d->dbuf_in);	    if (b->dl)		dsp_rd_dmaupdate( b );	    a->bytes = d->dbuf_in.fl ;	    a->fragments = 1 ;	    a->fragstotal = b->bufsize / d->rec_blocksize ;	    a->fragsize = d->rec_blocksize ;	}	break ;    case SNDCTL_DSP_GETOSPACE:	/* return space available in the output queue */	{	    audio_buf_info *a = (audio_buf_info *)arg;	    snd_dbuf *b = &(d->dbuf_out);	    if (b->dl)		dsp_wr_dmaupdate( b );	    a->bytes = d->dbuf_out.fl ;	    a->fragments = 1 ;	    a->fragstotal = b->bufsize / d->play_blocksize ;	    a->fragsize = d->play_blocksize ;	}	break ;    case SNDCTL_DSP_GETIPTR:	{	    count_info *a = (count_info *)arg;	    snd_dbuf *b = &(d->dbuf_in);	    if (b->dl)		dsp_rd_dmaupdate( b );	    a->bytes = b->total;	    a->blocks = (b->total - b->prev_total +		    d->rec_blocksize -1 ) / d->rec_blocksize ;	    a->ptr = b->fp ; /* XXX not sure... */	    b->prev_total = b->total ;	}	break;    case SNDCTL_DSP_GETOPTR:	{	    count_info *a = (count_info *)arg;	    snd_dbuf *b = &(d->dbuf_out);	    if (b->dl)		dsp_wr_dmaupdate( b );	    a->bytes = b->total;	    a->blocks = (b->total - b->prev_total		    /* +d->play_blocksize -1*/ ) / d->play_blocksize ;	    a->ptr = b->rp ; /* XXX not sure... */	    b->prev_total = b->total ;	}	break;    case SNDCTL_DSP_GETCAPS :	*(int *) arg = 0x0 ; /* revision */	if (FULL_DUPLEX(d))		*(int *) arg |= DSP_CAP_DUPLEX ;	*(int *) arg |= DSP_CAP_REALTIME ;	break ;    case SOUND_PCM_READ_BITS:	if (d->play_fmt == AFMT_S16_LE)	    *(int *) arg = 16 ;	else	    *(int *) arg = 8 ;	break ;    /*     * mixer calls     */    case SOUND_MIXER_READ_DEVMASK :    case SOUND_MIXER_READ_CAPS :    case SOUND_MIXER_READ_STEREODEVS :	*(int *)arg = d->mix_devs;	break ;    case SOUND_MIXER_READ_RECMASK :	*(int *)arg = d->mix_rec_devs;	break ;    case SOUND_MIXER_READ_RECSRC :	*(int *)arg = d->mix_recsrc ;	break;    default:	DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n",	    unit, dev & 0xf, cmd));	ret = EINVAL;	break ;    }    splx(s);    return ret ;}/* * we use the name 'select', but the new "poll" interface this is * really sndpoll. Second arg for poll is not "rw" but "events" */intsndselect(dev_t i_dev, int rw, struct proc * p){    int dev, unit, c = 1 /* default: success */ ;    snddev_info *d ;    u_long flags;    dev = minor(i_dev);    d = get_snddev_info(dev, &unit);    DEB(printf("sndselect dev 0x%04x rw 0x%08x\n",i_dev, rw));    if (d == NULL ) /* should not happen! */	return (ENXIO) ;    if (d->select == NULL)#if __FreeBSD__ >= 3        return ( (rw & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP);#else	return 1 ; /* always success ? */#endif    else if (d->select != sndselect )	return d->select(i_dev, rw, p);    else {	/* handle it here with the generic code */	/*	 * if the user selected a block size, then we want to use the	 * device as a block device, and select will return ready when	 * we have a full block.	 * In all other cases, select will return when 1 byte is ready.	 */	int lim = 1;#if __FreeBSD__ >= 3	int revents = 0 ;	if (rw & (POLLOUT | POLLWRNORM) ) {#else	if (rw == FWRITE) {#endif	    if ( d->flags & SND_F_HAS_SIZE )		lim = d->play_blocksize ;	    /* XXX fix the test here for half duplex devices */	    if (1 /* write is compatible with current mode */) {		flags = spltty();		if (d->dbuf_out.dl)		    dsp_wr_dmaupdate(&(d->dbuf_out));		c = d->dbuf_out.fl ;		if (c < lim) /* no space available */		    selrecord(p, & (d->wsel));#if __FreeBSD__ >= 3		else		    revents |= rw & (POLLOUT | POLLWRNORM);#endif		splx(flags);	    }#if __FreeBSD__ >= 3        }        if (rw & (POLLIN | POLLRDNORM)) {#else	    return c < lim ? 0 : 1 ;        } else if (rw == FREAD) {#endif	    if ( d->flags & SND_F_HAS_SIZE )		lim = d->rec_blocksize ;	    /* XXX fix the test here */	    if (1 /* read is compatible with current mode */) {		flags = spltty();		if ( d->dbuf_in.dl == 0 ) /* dma idle, restart it */		    dsp_rdintr(d);		else		    dsp_rd_dmaupdate(&(d->dbuf_in));		c = d->dbuf_in.rl ;		if (c < lim) /* no data available */		    selrecord(p, & (d->rsel));#if __FreeBSD__ >= 3		else		    revents |= rw & (POLLIN | POLLRDNORM);#endif		splx(flags);	    }	    DEB(printf("sndselect on read: %d >= %d flags 0x%08x\n",		c, lim, d->flags));	    return c < lim ? 0 : 1 ;#if __FreeBSD__ >= 3	}	return revents;#else	} else {	    DDB(printf("select on exceptions, unimplemented\n"));	    return 1;	}#endif    }    return ENXIO ; /* notreached */}/* * The mmap interface allows access to the play and read buffer, * plus the device descriptor. * The various blocks are accessible at the following offsets: * * 0x00000000 ( 0   ) : write buffer ; * 0x01000000 (16 MB) : read buffer ; * 0x02000000 (32 MB) : device descriptor (dangerous!) * * WARNING: the mmap routines assume memory areas are aligned. This * is true (probably) for the dma buffers, but likely false for the * device descriptor. As a consequence, we do not know where it is * located in the requested area. */#include <sys/mman.h>#include <vm/vm.h>      #include <vm/vm_kern.h> #include <vm/vm_param.h>#include <vm/pmap.h>    #include <vm/vm_extern.h>static intsndmmap(dev_t dev, vm_offset_t offset, int nprot){    snddev_info *d = get_snddev_info(dev, NULL);    DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",	d, dev, offset, nprot));        if (d == NULL || nprot & PROT_EXEC || offset < 0)	return -1 ; /* forbidden */    if (offset >= d->dbuf_out.bufsize && (nprot & PROT_WRITE) )	return -1 ; /* can only write to the first block */    if (offset < d->dbuf_out.bufsize)	return i386_btop(vtophys(d->dbuf_out.buf + offset));    offset -= 1 << 24;    if ( (offset >= 0) && (offset < d->dbuf_in.bufsize))	return i386_btop(vtophys(d->dbuf_in.buf + offset));    offset -= 1 << 24;    if ( (offset >= 0) && (offset < 0x2000)) {	return i386_btop(vtophys( ((int)d & ~0xfff) + offset));    }    return -1 ;}/* * ask_init sets the init flag in the device descriptor, and * possibly calls the appropriate callback routine, returning 1 * if the callback was successful. This enables ioctls handler for * rw parameters to read back the updated value. * Since the init callback can be slow, ask_init() should be called * with interrupts enabled. */intask_init(snddev_info *d){    u_long s;    if ( d->callback == NULL )	return 0 ;    s = spltty();    if ( d->flags & SND_F_PENDING_IO ||	 d->dbuf_out.dl || d->dbuf_in.dl ) {	/* cannot do it now, record the request and return */	d->flags |= SND_F_INIT ;	splx(s);	return 0 ;    } else {	splx(s);	d->callback(d, SND_CB_INIT );	return 1;    }}/* * these are the functions for the soundstat device. We copy parameters * from the device info structure to static variables, and from there * back to the structure when done. */static voidinit_status(snddev_info *d){    /*     * Write the status information to the status_buf and update     * status_len. There is a limit of SNDSTAT_BUF_SIZE bytes for the data.     */    int             i;    if (status_len != 0) /* only do init once */	return ;    sprintf(status_buf,	"FreeBSD Audio Driver (981022) "  __DATE__ " " __TIME__ "\n"	"Installed devices:\n");    for (i = 0; i < NPCM_MAX; i++) {        if (pcm_info[i].open)            sprintf(status_buf + strlen(status_buf),		"pcm%d: <%s> at 0x%x irq %d dma %d:%d\n",		i, pcm_info[i].name, pcm_info[i].io_base,		pcm_info[i].irq,		pcm_info[i].dbuf_out.chan, pcm_info[i].dbuf_in.chan);        if (midi_info[i].open)            sprintf(status_buf + strlen(status_buf),		"midi%d: <%s> at 0x%x irq %d dma %d:%d\n",		i, midi_info[i].name, midi_info[i].io_base,		midi_info[i].irq,		midi_info[i].dbuf_out.chan, midi_info[i].dbuf_in.chan);        if (pcm_info[i].synth_base) {	    char *s = "???";	    switch (pcm_info[i].synth_type) {	    case 2 : s = "OPL2"; break;	    case 3 : s = "OPL3"; break;	    case 4 : s = "OPL4"; break;	    }            sprintf(status_buf + strlen(status_buf),		"sequencer%d: <%s> at 0x%x (not functional)\n",		i, s, pcm_info[i].synth_base);	}    }    status_len = strlen(status_buf) ;}/* * finally, some "libraries" *//* * isa_dmastatus1() is a wrapper for isa_dmastatus(), which * might return -1 or -2 in some cases (errors). Since for the * user code it is more comfortable not to check for these cases, * negative values are mapped back to 0 (which is reasonable). */intisa_dmastatus1(int channel){    int r = isa_dmastatus(channel);    if (r<0) r = 0;    return r;}/* * snd_conflict scans already-attached boards to see if * the current address is conflicting with one of the already * assigned ones. Returns 1 if a conflict is detected. */intsnd_conflict(int io_base){    int i;    for (i=0; i< NPCM_MAX ; i++) {        if ( (io_base == pcm_info[i].io_base  ) ||             (io_base == pcm_info[i].alt_base ) ||             (io_base == pcm_info[i].conf_base) ||             (io_base == pcm_info[i].mix_base ) ||             (io_base == pcm_info[i].midi_base) ||             (io_base == pcm_info[i].synth_base) ) {            BVDDB(printf("device at 0x%x already attached as unit %d\n",                io_base, i);)            return 1 ;        }    }    return 0;}voidsnd_set_blocksize(snddev_info *d){    int tmp ;    /*     * compute the sample size, and possibly     * set the blocksize so as to guarantee approx 1/4s     * between callbacks.     */    tmp = 1 ;    if (d->flags & SND_F_STEREO) tmp += tmp;    if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;    d->dbuf_out.sample_size = tmp ;    tmp = tmp * d->play_speed;    if ( (d->flags & SND_F_HAS_SIZE) == 0) {	d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */	RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3);    }    tmp = 1 ;    if (d->flags & SND_F_STEREO) tmp += tmp;    if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp;    tmp = tmp * d->rec_speed;    d->dbuf_in.sample_size = tmp ;    if ( (d->flags & SND_F_HAS_SIZE) == 0) {	d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */	RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3);    }}/* * The various mixers use a variety of bitmasks etc. The Voxware * driver had a very nice technique to describe a mixer and interface * to it. A table defines, for each channel, which register, bits, * offset, polarity to use. This procedure creates the new value * using the table and the old value. */voidchange_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval){    u_char mask;    int shift;    DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "	"r %d p %d bit %d off %d\n",	dev, chn, newval, *regval,	(*t)[dev][chn].regno, (*t)[dev][chn].polarity,	(*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );    if ( (*t)[dev][chn].polarity == 1)	/* reverse */	newval = 100 - newval ;    mask = (1 << (*t)[dev][chn].nbits) - 1;    newval = (int) ((newval * mask) + 50) / 100; /* Scale it */    shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;    *regval &= ~(mask << shift);        /* Filter out the previous value */    *regval |= (newval & mask) << shift;        /* Set the new value */}/* * code for translating between U8 and ULAW. Needed to support * /dev/audio on the SoundBlaster. Actually, we would also need * ulaw -> 16 bits (for the soundblaster as well, when used in * full-duplex) */#if 1voidtranslate_bytes (u_char *table, u_char *buff, int n){    u_long   i;    if (n <= 0)	return;      for (i = 0; i < n; ++i)	buff[i] = table[buff[i]];}#else/* inline */voidtranslate_bytes (const void *table, void *buff, int n){         if (n > 0) { 	__asm__ (  "   cld\n"		   "1: lodsb\n"		   "   xlatb\n"		   "   stosb\n"		   "   loop 1b\n":	    : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)	    : "bx", "cx", "di", "si", "ax");    }}   #endif#endif	/* NPCM > 0 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -