?? sb.c
字號:
break;
case 10:
sb_write_mix(base,0x80,8);
break;
default:
printf("invalid irq %d\n",irq);
return -1;
}
maskon(irq);
if(dma8 < 0 || dma8 > 3 || dma8 == 2){
printf("dma8 invalid\n");
return -1;
}
if(dma16 < 5 || dma16 > 7){
printf("dma16 invalid\n");
return -1;
}
dis_dmaxl(dma16);
dis_dmaxl(dma8);
dma = (1 << dma16) | (1<<dma8);
sb_write_mix(base,0x81,dma);
return 0;
}
/* Enter A/D mode */
int
sb_adc(rate,stereo,bufsiz)
long rate; /* Sampling rate, Hz */
int stereo; /* 1 = stereo, 0 = mono */
int bufsiz; /* Size of mbufs to be generated (1/2 DMA buffer) */
{
if(Sb.base == 0){
printf("No SB16 card attached\n");
return -1;
}
if(Sb.state != IDLE){
printf("SB16 not idle\n");
return -1;
}
/* Allocate DMA buffer */
if((Sb.ioptr = Sb.dmabuf = dma_malloc(&Sb.physaddr,2*bufsiz)) == NULL){
printf("Can't alloc dma buffer\n");
return -1;
}
Sb.state = ADC;
Sb.dmasize = 2*bufsiz;
setup_dma(Sb.dma16,Sb.physaddr,Sb.dmasize,0x54);
/* Set sampling rate */
sb_write_byte(Sb.base,0x42);
sb_write_word_be(Sb.base,rate);
sb_write_byte(Sb.base,0xbe); /* 16-bit input, auto-init, fifo on */
if(stereo)
sb_write_byte(Sb.base,0x30); /* stereo, signed */
else
sb_write_byte(Sb.base,0x10); /* mono, signed */
/* Write number of 16-bit words in half buffer, minus 1 */
sb_write_word_le(Sb.base,Sb.dmasize/4-1);
return 0;
}
/* Enter DAC mode */
int
sb_dac(rate,stereo,bufsiz)
long rate; /* Sampling rate, Hz */
int stereo; /* 1 = stereo, 0 = mono */
int bufsiz; /* Size of buffer unit (1/2 DMA buffer) */
{
if(Sb.base == 0){
printf("No SB16 card attached\n");
return -1;
}
if(Sb.state != IDLE){
printf("SB16 not idle\n");
return -1;
}
/* Allocate DMA buffer */
if((Sb.ioptr = Sb.dmabuf = dma_malloc(&Sb.physaddr,2*bufsiz)) == NULL){
printf("Can't alloc dma buffer\n");
return -1;
}
Sb.state = DAC;
Sb.dmasize = 2*bufsiz;
Sb.stuffsamples = 0;
Sb.bufcnt = 0;
memset(Sb.dmabuf,0,Sb.dmasize);
sb_write_byte(Sb.base,0xd1); /* spkr on */
setup_dma(Sb.dma16,Sb.physaddr,Sb.dmasize,0x58);
/* Set up sampling rate */
sb_write_byte(Sb.base,0x41);
sb_write_word_be(Sb.base,rate);
sb_write_byte(Sb.base,0xb6); /* 16-bit output, auto-init, fifo on */
if(stereo)
sb_write_byte(Sb.base,0x30); /* stereo, signed */
else
sb_write_byte(Sb.base,0x10); /* mono, signed */
/* Number of 16-bit words in a half-buffer, minus 1 */
sb_write_word_le(Sb.base,Sb.dmasize/4-1);
Sb.pause = 1;
sb_write_byte(Sb.base,0xd5); /* Pause until we get data */
return 0;
}
/* Return soundblaster to idle condition */
int
sb_idle()
{
switch(Sb.state){
case IDLE:
return 0; /* Already idle */
case DAC: /* Wait for output queue to drain */
case ADC:
/* stop conversion */
sb_write_byte(Sb.base,0xd9);
break;
}
Sb.state = IDLE;
kwait(NULL);
/* Ought to wait for last interrupt here */
free_q(&Sb.sndq);
free_q(&Sb.rcvq);
dma_disable(Sb.dma16);
dmaunlock(Sb.physaddr,Sb.dmasize);
free(Sb.dmabuf);
Sb.dmabuf = NULL;
Sb.physaddr = 0;
return 0;
}
/* Reset Soundblaster card */
static int
sb_reset(base)
int base;
{
unsigned int i;
outportb(base+SB_RESET,1);
for(i=100;i !=0;i--)
;
outportb(base+SB_RESET,0);
for(i=65535;i != 0;i--){
if(sb_read_data(base) == 0xaa)
break;
}
if(i == 0)
return -1;
return 0;
}
/* Wait for read data to become available, then read it. Return -1 on timeout */
static int
sb_read_data(base)
int base;
{
unsigned int i;
for(i=65535;i!=0;i--){
if(inportb(base+SB_RB_STAT) & 0x80)
break;
}
if(i == 0)
return -1; /* Timeout */
return inportb(base+SB_READ_DATA);
}
/* Write data byte to soundblaster when it's ready. return 0 normally, -1 on err */
static int
sb_write_byte(base,data)
int base,data;
{
int i;
for(i=65535;i!=0;i--){
if((inportb(base+SB_WB_STAT) & 0x80) == 0)
break;
}
if(i == 0)
return -1;
outportb(base+SB_WB,data);
return 0;
}
/* Write 16-bit word in big-endian order */
static int
sb_write_word_be(base,data)
int base;
short data;
{
sb_write_byte(base,data >> 8);
sb_write_byte(base,data);
return 0;
}
/* Write 16-bit word in little-endian order */
static int
sb_write_word_le(base,data)
int base;
short data;
{
sb_write_byte(base,data);
sb_write_byte(base,data >> 8);
return 0;
}
/* Read the mixer */
static int
sb_read_mix(base,reg)
int base,reg;
{
outportb(base+SB_MIX_INDEX,reg);
return inportb(base+SB_MIX_DATA);
}
/* Write data to soundblaster when it's ready. return 0 normally, -1 on err */
static int
sb_write_mix(base,reg,data)
int base,reg,data;
{
outportb(base+SB_MIX_INDEX,reg);
outportb(base+SB_MIX_DATA,data);
return 0;
}
dosbcal(argc,argv,p)
int argc;
char *argv[];
void *p;
{
long rate;
rate = atol(argv[1]);
sb_calibrate(rate);
return 0;
}
/* Find DC offsets of each channel */
sb_calibrate(rate)
long rate;
{
long leftavg,rightavg;
uint8 omixl,omixr;
long samples;
/* Save previous mixer state,
* then turn everything off
*/
omixl = sb_read_mix(Sb.base,SB_INMIXL);
omixr = sb_read_mix(Sb.base,SB_INMIXR);
sb_write_mix(Sb.base,SB_INMIXL,0); /* All inputs off */
sb_write_mix(Sb.base,SB_INMIXR,0);
/* Collect a second of samples */
sb_adc(rate,1,TXBUF);
ppause(1000L);
sb_idle();
/* Restore previous mixer switches */
sb_write_mix(Sb.base,SB_INMIXL,omixl);
sb_write_mix(Sb.base,SB_INMIXR,omixr);
/* Analyze the data */
leftavg = rightavg = 0;
samples = 0;
while(Sb.rcvq != NULL){
uint16 left,right;
left = pull16le(&Sb.rcvq);
right = pull16le(&Sb.rcvq);
leftavg += left;
rightavg += right;
samples++;
}
leftavg /= samples;
rightavg /= samples;
printf("Left channel avg: %ld Right channel avg: %ld\n",
leftavg,rightavg);
return 0;
}
unsigned short
pull16le(bpp)
struct mbuf **bpp;
{
uint16 x;
x = pull16(bpp);
return (x >> 8) | (x << 8);
}
/* Copy as much of the buffer as will fit into the DMA buffer and start
* the DAC if it was paused
*/
void
sb_outstart(bpp)
struct mbuf **bpp;
{
int cnt;
int istate;
istate = dirps();
/* Copy as much send queue data as we can into the DMA buffer */
while(Sb.bufcnt < Sb.dmasize){
cnt = min(Sb.dmasize-Sb.bufcnt,Sb.dmabuf+Sb.dmasize-Sb.ioptr);
cnt = pullup(bpp,Sb.ioptr,cnt);
if(cnt == 0)
break;
Sb.ioptr += cnt;
if(Sb.ioptr >= Sb.dmabuf + Sb.dmasize)
Sb.ioptr = Sb.dmabuf; /* Write pointer wraparound */
Sb.bufcnt += cnt; /* Add to bytes in buffer */
}
if(!Sb.pause && Sb.bufcnt != 0 && Sb.bufcnt < Sb.dmasize/2){
/* While running, pad out a partial half-buffer with silence */
cnt = Sb.dmasize/2 - Sb.bufcnt;
memset(Sb.ioptr,0,cnt);
Sb.stuffsamples += cnt;
Sb.ioptr += cnt;
if(Sb.ioptr >= Sb.dmabuf + Sb.dmasize)
Sb.ioptr = Sb.dmabuf; /* Write pointer wraparound */
Sb.bufcnt += cnt;
} else if(Sb.pause && Sb.bufcnt >= Sb.dmasize/2){
/* If paused and there's now at least one full buffer, resume */
Sb.pause = 0;
sb_write_byte(Sb.base,0xd6); /* Resume output */
}
restore(istate);
}
/* Send a buffer to the DAC */
int
sb_send(bp)
struct mbuf *bp;
{
if(Sb.state == DAC)
sb_outstart(&bp);
if(bp != NULL){
/* Queue remainder */
disable();
append(&Sb.sndq,&bp);
enable();
}
return 0;
}
/* Soundblaster interrupt handler */
INTERRUPT (far *(sbint)(dev))()
int dev;
{
int i;
struct mbuf *bp;
i = sb_read_mix(Sb.base,0x82);
if(i & 1) /* 8-bit transfers not used, reset anyway */
(void)inportb(Sb.base+0xe);
if(i & 2){
(void)inportb(Sb.base+0xf);
switch(Sb.state){
case IDLE:
break;
case DAC:
Sb.bufcnt -= Sb.dmasize/2; /* Amount sent */
sb_outstart(&Sb.sndq); /* Try to send more */
if(Sb.bufcnt == 0){
/* buffer empty, pause */
Sb.pause = 1;
sb_write_byte(Sb.base,0xd5);
}
break;
case ADC:
bp = alloc_mbuf(Sb.dmasize/2);
memcpy(bp->data,Sb.ioptr,Sb.dmasize/2);
bp->cnt = Sb.dmasize/2;
Sb.ioptr += bp->cnt;
if(Sb.ioptr >= Sb.dmabuf + Sb.dmasize)
Sb.ioptr = Sb.dmabuf;
enqueue(&Sb.rcvq,&bp);
break;
}
ksignal(&Sb,0);
}
return NULL;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -