📄 audio.c
字号:
a9200_rxend = bufcnt;
}
else
{
slen = (bufcnt - LBUFSIZE) > LBUFSIZE ? LBUFSIZE : (bufcnt - LBUFSIZE);
a9200_rxend = slen;
}
// SSC_CR
// DPRINTK(" pdc_ptsr is: %d!\n", pdc_regs->PDC_PTSR);
// DPRINTK(" ssc_sr is: %d!\n", ssc_regs->SSC_SR);
ssc_regs->SSC_CR |= AT91C_SSC_RXEN;
// pdc_regs->PDC_PTCR |= AT91C_PDC_RXTEN;
// DPRINTK(" pdc_ptsr is: %d!\n", pdc_regs->PDC_PTSR);
// DPRINTK(" ssc_sr is: %d!\n", ssc_regs->SSC_SR);
//DPRINTK("pdc_ptsr = %p \n",pdc_regs->PDC_PTSR);
// rx with irq
if(a9200_rxbusy == 0)
{
a9200_rxbusy = 1;
ssc_regs->SSC_IER = AT91C_SSC_RXRDY;// enable RXRDY interrupt
tmp = ssc_regs->SSC_RHR;
}
DPRINTK("waiting the recording to finish ... \n");
while(a9200_rxbusy == 1)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
}
DPRINTK("Copy from buflp : %p to appbuflp : %p ,length : %p \n",appbuflp,buflp,4 * slen);
for(i = 0;i < slen;i++)
{
*appbuflp++ = buflp[i];
}
// rx with dma
// a9200_rx_dmabuf();
bufcnt -= slen;
if(bufcnt > 0)
goto tryagain;
return (count);
}
/**********************************************************
放音函数
***********************************************************/
static ssize_t at91_audio_write(struct file * file, const char * buf, size_t count,loff_t *ppos)
{
unsigned long *dp,*buflp;
unsigned short *bufwp;
unsigned char *bufbp;
unsigned int slen,bufcnt,i,s,e;
if(count <= 0)
return (0);
//待输入的数据缓存的指针
buflp = (unsigned long *) buf;
bufwp = (unsigned short *) buf;
bufbp = (unsigned char *) buf;
bufcnt = count & ~0x3; //以4 为单位
//非立体声或8bits,缓存字节数乘以2
if(a9200_stereo == 0)
bufcnt <<= 1;
if(a9200_bits == 8)
bufcnt <<= 1;
tryagain:
s = a9200_dmastart;
e = a9200_append;
dp = (unsigned long *) & a9200_buf[e];//dp指向缓存的末尾
//计算剩余DMA缓存的大小,即每次写操作能处理的数据长度
slen = ((s > e) ? (s - e) : (BUFSIZE - (e - s))) - 4;
//如果缓存大于需写入的数量,全部写入
if(slen > bufcnt)
slen = bufcnt;
//如果剩余的缓存跨越头尾,本次操作先写到尾部,下次再重头开始
if((BUFSIZE - e) < slen)
slen = BUFSIZE - e;
//如果没有空闲的DMA缓存,则睡眠等待
if(slen == 0)
{ //有未决信号
if(signal_pending(current))
return (-ERESTARTSYS);
//设置为可中断方式
set_current_state(TASK_INTERRUPTIBLE);
//切换到其他进程,并进入睡眠状态
schedule_timeout(1);
//定时轮询,直到有空闲的DMA缓存
goto tryagain;
}
if(a9200_stereo)
{
if(a9200_bits == 16)
{
for(i = 0;i < slen;i += 4)
*dp++ = *buflp++;
}
else
{
for(i = 0;i < slen;i += 4)
{ //将第一字节复制高16位,将第二个字节复制到低16位
*dp++ = (((unsigned long) *bufbp++) << 24) | (((unsigned long) *bufbp++) << 8);
}
}
}
else
{
if(a9200_bits == 16)
{
for(i = 0;i < slen;i += 4)
{
*dp++ = (((unsigned long) *bufwp) << 16) | *bufwp;
bufwp++;
}
}
else
{
for(i = 0;i < slen;i += 4)
{
*dp++ = (((unsigned long) *bufbp) << 24) | (((unsigned long) *bufbp) << 8);
bufbp++;
}
}
}
//更新尾部指针
e += slen;
if(e >= BUFSIZE)
//回绕
e = 0;
a9200_append = e;
#if 0
// SSC Control Register
pdc_regs->PDC_PTCR = AT91C_PDC_TXTEN;
ssc_regs->SSC_CR = AT91C_SSC_TXEN;
// tx with irq
if(a9200_txbusy == 0)
{
a9200_txbusy++;
ssc_regs->SSC_IER = AT91C_SSC_TXRDY ;// enable TXRDY interrupt
ssc_regs->SSC_THR = 0;
}
#endif
#if 1
// tx with dma
a9200_tx_dmabuf();
#endif
//更新剩余字节数,每次写出slen字节
bufcnt -= slen;
if(bufcnt > 0)
goto tryagain;
return (count);
}
/*
static ssize_t at91_audio_llseek(struct file * file, const char * buf, size_t cmd,loff_t *ppos)
{
int val, ret = 0;
U32 tmpdata;
U8 lsb, msb;
switch (cmd)
{
case REC:
codec_init(uda1380_rx_init);
break;
case SOUND:
codec_init(uda1380_tx_init);
break;
default :
ret = -EINVAL;
break;
}
return ret;
}
*/
/******************************************************
本函数用于启动DMA传输,将数据从用户缓
冲区拷贝到内核缓冲区
*******************************************************/
void __inline__ a9200_tx_dmarun(void)
{
unsigned long *bp;
U32 dma_tx_addr;
a9200_dmaing = 1;
a9200_txbusy = 1;
bp = (unsigned long *) & a9200_buf[a9200_dmastart];
dma_tx_addr = virt_to_phys((void *)bp);
AT91F_PDC_SetTx(pdc_regs,(char *)dma_tx_addr,a9200_dmacount/2);
// AT91F_PDC_SetNextTx(pdc_regs,(char *)dma_tx_addr,a9200_dmacount/2);
//pdc_regs->PDC_PTCR = AT91C_PDC_TXTDIS;
pdc_regs->PDC_PTCR = AT91C_PDC_TXTEN;
ssc_regs->SSC_CR = AT91C_SSC_TXEN;
ssc_regs->SSC_IER = AT91C_SSC_ENDTX; // endtx interrupt
}
/***************************************************
计算本次DMA传输长度,调整当前DMA缓
存首地址,最后调用DMARUN启动DMA传输
****************************************************/
void a9200_tx_dmabuf(void)
{
// 如果已经启动DMA,则直接返回
if(a9200_dmaing)
return;
// 计算DMA传输数据长度
a9200_dmacount = (a9200_append >= a9200_appstart) ? (a9200_append - a9200_appstart) : (BUFSIZE - a9200_appstart);
if(a9200_dmacount > DMASIZE)
a9200_dmacount = DMASIZE;
// 调整首地址指针和计数
a9200_appstart += a9200_dmacount;
if(a9200_appstart >= BUFSIZE)
a9200_appstart = 0;
if(a9200_dmacount > 0)
a9200_tx_dmarun();
else
a9200_txbusy = 0;
}
void a9200_tx_dmaisr(int irq,void *dev_id,struct pt_regs *regs)
{
//DPRINTK("I'm in transfor!...\n");
// clear dma interrupt
// aic_regs->AIC_ICCR = 0x1 << AT91C_ID_SSC1; // clear interrupt
// ssc_regs->SSC_IDR = AT91C_SSC_ENDTX; // disbale endtx interrupt
// pdc_regs->PDC_PTCR = AT91C_PDC_TXTDIS;
a9200_dmaing = 0;
// update data pointers and counts
a9200_dmastart += a9200_dmacount;
if(a9200_dmastart >= BUFSIZE)
a9200_dmastart = 0;
a9200_dmacount = 0;
// start new dma buffer if we can
a9200_tx_dmabuf();
}
void a9200_txdrain(void)
{
a9200_txbusy = 0;
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
return;
while(!signal_pending(current)) {
if(a9200_txbusy == 0)
break;
}
}
void unload_tx_ssi()
{
unregister_sound_dsp(audio_dev_dsp);
}
/*
void unload_rx_ssi()
{
unregister_sound_mixer(audio_dev_mixer);
}
*/
struct file_operations at91_audio_fops = {
open:at91_audio_open, /* open */
release:at91_audio_close,/* release */
read:at91_audio_read, /*read*/
write:at91_audio_write, /* write */
ioctl:at91_audio_ioctl, /* ioctl */
// llseek:at91_audio_llseek,
};
//struct file_operations at91_mixer_fops = {
// open:at91_mixer_open, /* open */
// release:at91_mixer_close,/* release */
// ioctl:at91_mixer_ioctl, /* ioctl */
//};
static int __init init_ssi(void)
{
int m;
SINT32 ok = 0;
printk("AT91RM9200 audio driver version 0.1, build time:" __TIME__ "\n");
audio_dev_dsp = register_sound_dsp(&at91_audio_fops, -1);
// audio_dev_mixer = register_sound_mixer(&at91_mixer_fops, -1);
//为设备分配内存
a9200_buf = (unsigned char *) __get_free_pages(GFP_KERNEL,7);
if(a9200_buf == NULL)
{
printk("a9200:failed to allocate DMA buffer\n");
}
//申请中断
if( request_irq(AT91C_ID_SSC1,at91_audio_interrupt, SA_INTERRUPT, "SSC", NULL) < 0 )
// if( request_irq(AT91C_ID_SYS, ssc_interrupt_handler, SA_SHIRQ, "SSC", NULL) < 0 )
printk("SSC interrupt handler request failed!\n");
else
DPRINTK("request irq succeed...\n");
AT91_PIO_SSC_Set();
SSC_init();
port_init_I2C();
/*Configure the CODEC to Default status */
// codec_init(uda1380_tx_init);
printk("I'm ready now,9200!... \n");
return ok;
}
static void __exit cleanup_ssi(void)
{
codec_init(uda1380_init);
if (a9200_buf)
free_pages((void *) a9200_buf, 7); // 512K
free_irq(AT91C_ID_SSC1,NULL);
// free_irq(AT91C_ID_SYS,NULL);
unload_tx_ssi();
// unload_rx_ssi();
codec_init(uda1380_init);
printk("Good bye,9200,I'll be back!...\n");
}
MODULE_AUTHOR("WELL");
MODULE_DESCRIPTION("SSI-Low Level Audio Device Driver");
module_init(init_ssi);
module_exit(cleanup_ssi);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -