📄 uda1341_driver.c
字号:
//接着进行判断,如果dmasize 为0,则继续执行。这里一开始就定义了dmasize 为0////。一开始,先将dmasize 赋值为所需要的最大的缓冲区空间,即(8-0)*8192。 #endif dmasize = (s->nbfrags - frag) * s->fragsize; do {#if 0// 下面又进入一个do while 循环,调用consistent_alloc 函数来进行内存分配,//该函数在《LCD驱动程序分析》一文中有过详细分析。//通过调用该函数来分配先前dmasize 大小的内存空间(所需要的最大的缓冲区空间)。//返回两个值,一个是dmabuf,为所分配内存空间的起始地址,为虚拟地址;//另一个是dmaphys,也为所分配内存空间的起始地址,为物理地址。//如果返回的dmabuf 值为0,则表示内存没有申请成功////那么要分配的内存空间dmasize 就需要进行减少,减去一个缓冲区片大小,//再调用consistent_alloc 函数进行内存分配,知道分配成功或dmasize 为0 才退出循环?#endif dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA, dmasize, &dmaphys); if (!dmabuf) dmasize -= s->fragsize; } while (!dmabuf && dmasize); if (!dmabuf) goto err; b->master = dmasize;//接着把所分配的内存大小赋值给b->master 表示内存大小的结构参数。 }#if 0//将所分配的内存空间起始地址的虚拟地址赋值给b->start 这个虚拟地址指针//物理地址赋值给b->dma_addr 这个DMA 缓冲区地址。#endif b->start = dmabuf; b->dma_addr = dmaphys;#if 0//在for 大循环的最后,将所分配内存起始地址的虚拟地址和物理地址都加上音频缓冲区片的大小,//而总的缓冲区空间大小是减去音频缓冲区片的大小。前面两个参数都将作为下一个缓冲区地址audio_buf_t//结构中的虚拟地址指针和DMA 缓冲区地址的参数。//如果dmasize 不为0 的话,在进入下一次循环时,就不会进入do while 循环进行内存空间的分配了。//但是如果第一次没有分配到8 个音频缓冲区片大小的内存空间,比如只分配到4 个音频缓冲区片大小的内存空间,//则进入第5 次循环时,dmasize 为0 了,那么就会再次进入do while 循环进行内存空间的分配,//不过分配的为剩下的4 个音频缓冲区片大小的内存空间。//这个函数巧妙的解决了万一一次分配不到连续的8 个音频缓冲区片大小的内存空间,就会按几次来分配较小的连续的内存空间了。// 其中b->master 参数只有第0个缓冲区地址有值,为总的缓冲区空间大小,其余缓冲区地址的b->master 都为0。#endif sema_init(&b->sem, 1); DPRINTK("buf %d: start %p dma %d\n", frag, b->start, b->dma_addr); dmabuf += s->fragsize; dmaphys += s->fragsize; dmasize -= s->fragsize; } s->buf_idx = 0; s->buf = &s->buffers[0]; return 0; err: printk(AUDIO_NAME ": unable to allocate audio memory\n "); audio_clear_buf(s); return -ENOMEM;}/**********************************************************************************************************************************************************/static void audio_dmaout_done_callback(void *buf_id, int size){ audio_buf_t *b = (audio_buf_t *) buf_id; up(&b->sem); wake_up(&b->sem.wait);}/**********************************************************************************************************************************************************/static void audio_dmain_done_callback(void *buf_id, int size){ audio_buf_t *b = (audio_buf_t *) buf_id; b->size = size; up(&b->sem); wake_up(&b->sem.wait); } /*************************************************************************** ** ** ***************************************************************************/ /* using when write */static int audio_sync(struct file *file){ audio_stream_t *s = &output_stream; audio_buf_t *b = s->buf; DPRINTK("audio_sync\n"); if (!s->buffers) return 0; if (b->size != 0) { down(&b->sem); s3c2410_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, b->size, DMA_BUF_WR); b->size = 0; NEXT_BUF(s, buf); } b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags); if (down_interruptible(&b->sem)) return -EINTR; up(&b->sem); return 0;} /*************************************************************************** ** ** ***************************************************************************/static inline int copy_from_user_mono_stereo(char *to, const char *from, int count){ u_int *dst = (u_int *)to; const char *end = from + count; if (verify_area(VERIFY_READ, from, count)) return -EFAULT; if ((int)from & 0x2) { u_int v; __get_user(v, (const u_short *)from); from += 2; *dst++ = v | (v << 16); } while (from < end-2) { u_int v, x, y; __get_user(v, (const u_int *)from); from += 4; x = v << 16; x |= x >> 16; y = v >> 16; y |= y << 16; *dst++ = x; *dst++ = y; } if (from < end) { u_int v; __get_user(v, (const u_short *)from); *dst = v | (v << 16); } return 0;}/**********************************************************************************************************************************************************/static ssize_t smdk2410_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos){ const char *buffer0 = buffer;// 该函数首先又定义了一个audio_stream_t 结构的指针变量指向输出音频缓冲区。 audio_stream_t *s = &output_stream; int chunksize, ret = 0; DPRINTK("audio_write : start count=%d\n", count);// 然后根据file->f_flags 这个表示设备文件的打开方式是读取,写入//还是可读写的标志进行判断,//若为写入或可读写则继续执行,否则就会返回退出。 switch (file->f_flags & O_ACCMODE) { case O_WRONLY: case O_RDWR: break; default: return -EPERM; }//这里通过s->buffers 指针是否为空来判断有没有创建过DMA 缓冲区。若s->buffers 指针不为空,//则表示已经创建过DMA 缓冲区,那么就不会执行audio_setup_buf 函数了;若s->buffers 指针为空,//则就会执行audio_setup_buf 函数来创建DMA 缓冲区,创建成功的话就会返回0,//这样就会继续执行下面的代码。 if (!s->buffers && audio_setup_buf(s)) return -ENOMEM;//由于DMA 数据必须4字节对齐传输,即每次传输4个字节?//因此驱动程序需要保证每次写入的数据都是4的倍数。//这样屏蔽掉所要写入字节数的最后2位就是4的倍数了。 count &= ~0x03; while (count > 0) // 若要写入的字节数大于0,则进入一个while 大循环。 { audio_buf_t *b = s->buf;//在大循环一开始就定义了一个audio_buf_t 结构的指针变量指向前面定义的 //输出音频缓冲区里的当前缓冲区指针。 if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } if (audio_channels == 2) {// 对于“chunksize = s->fragsize - b->size”这一句一开始一直不太理解,//不知道为什么要将音频缓冲区片大小减去DMA 缓冲区大小作为写入的数据长度?//这两个量的大小是一样的,//这样一减不是变为0 了吗?现在觉得其实b->size 只是一个缓冲区地址的偏移量?//一开始这个偏移量应该为0,//这样就不难理解用s->fragsize 作为写入的数据长度。//接下去判断,如果所要写入的数据长度count 小于chunksize 值?//那就以count 为准备写入数据的长度。//在count 大于chunksize 的情况下,写入的数据长度以一个s->fragsize 大小为单位。//然后调用了copy_from_user 函数将用户空间buffer 里的数据复制到内核空间//起始地址为b->start + b->size 的内存中,//复制数据长度为chunksize。这里b->start 为指向环形缓冲区中第0个缓冲区地址的内存起始地址(虚拟地址),//用这个起始地址加上缓冲区地址的偏移量(0)还是指向第0个缓冲区地址(共8个)的起始地址(虚拟地址)。// 若copy_from_user 函数执行成功,则返回0,继续执行将缓冲区地址的偏移量b->size 加上已写入的数据长度chunksize。//若copy_from_user 函数执行失败,就调用up 函数释放信号量,并退出写设备文件函数。 chunksize = s->fragsize - b->size; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize, s->buf_idx); if (copy_from_user(b->start + b->size, buffer, chunksize)) { up(&b->sem); return -EFAULT; } b->size += chunksize; } else { chunksize = (s->fragsize - b->size) >> 1; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize*2, s->buf_idx); if (copy_from_user_mono_stereo(b->start + b->size, buffer, chunksize)) { up(&b->sem); return -EFAULT; } b->size += chunksize*2; } buffer += chunksize; count -= chunksize; if (b->size < s->fragsize) { up(&b->sem); break; }//该函数完成了管理DMA 缓冲区的相关数据结构s3c2410_dma_t 和dma_buf_t 进行了设置,//并对S3C2410 芯片的DMA 控制器部分的相关寄存器进行了相应配置。//传入的参数为DMA 通道号,一个空指针,DMA 缓冲区的物理起始地址,DMA 缓冲区大小,//DMA 缓冲区工作模式, s3c2410_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, b->size, DMA_BUF_WR); b->size = 0;//最后调用NEXT_BUF 宏函数来将当前缓冲区的指针指向环形缓冲区中下一个缓冲区地址处。 NEXT_BUF(s, buf); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write : end count=%d\n\n", ret); return ret;}/**********************************************************************************************************************************************************/static ssize_t smdk2410_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos){ const char *buffer0 = buffer; audio_stream_t *s = &input_stream; int chunksize, ret = 0; DPRINTK("audio_read: count=%d\n", count); if (ppos != &file->f_pos) return -ESPIPE; if (!s->buffers) { int i; if (audio_setup_buf(s)) return -ENOMEM; for (i = 0; i < s->nbfrags; i++) { audio_buf_t *b = s->buf; down(&b->sem); s3c2410_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, s->fragsize, DMA_BUF_RD); NEXT_BUF(s, buf); } } //mdelay(1); if((IISFIFOC & IISFCON_RX_EN)==0){//start RX FIFO //IISFIFOC&=~(IISFCON_RX_DMA|IISFCON_RX_EN); //udelay(1); //IISFIFOC|=(IISFCON_RX_DMA|IISFCON_RX_EN); IISFIFOC |=IISFCON_RX_EN; DPRINTK("audio_read: start RX\n"); } DPRINTK("audio_read: DISRCC1=%x, DIDSTC1=%x, DCON1=%x,DSTAT1=%x,DMSKTRIG1=%x, DISRC1=%x, DIDST1=%x, DCDST1=%x, DCSRC1=%x\n", DISRCC1,DIDSTC1, DCON1, DSTAT1, DMTRIG1, DISRC1, DIDST1, DCDST1, DCSRC1); while (count > 0) { audio_buf_t *b = s->buf; /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } chunksize = b->size; if (chunksize > count) chunksize = count; DPRINTK("read %d from %d\n", chunksize, s->buf_idx); if (copy_to_user(buffer, b->start + s->fragsize - b->size, chunksize)) { up(&b->sem); return -EFAULT; } b->size -= chunksize; buffer += chunksize; count -= chunksize; if (b->size > 0) { up(&b->sem); break; } /* Make current buffer available for DMA again */ s3c2410_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, s->fragsize, DMA_BUF_RD); NEXT_BUF(s, buf);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -