原创 OSS 44B0X mp3

2010-4-30 15:30 2994 9 9 分类: MCU/ 嵌入式
OSS 44B0X mp3
by panasonic.lin@163.com
在前一篇中,借助libmad,已经可以在44B0X上顺利解码MP3,但是采样率只有16k,常见的44k等采样并没支持。
这一篇中,主要添加oss接口,并且支持常见的几个采样。

1添加oss接口的驱动,主要是一下两个结构体:解码器dsp和混音器mixer的函数实现。
static struct file_operations s3c44b0_audio_fops =
{
.open = s3c44b0_audio_open,
.write = s3c44b0_audio_write,
.release = s3c44b0_audio_release,

.llseek=s3c44b0_audio_llseek,
.ioctl=s3c44b0_audio_ioctl
};


static struct file_operations s3c44b0_mixer_fops =
{
    ioctl: s3c44b0_mixer_ioctl,
    open: s3c44b0_mixer_open,
    release: s3c44b0_mixer_release
};
另外还有注册和卸载设备的函数4个:
audio_dev_dsp = register_sound_dsp(&s3c44b0_audio_fops, -1);
audio_dev_mixer = register_sound_mixer(&s3c44b0_mixer_fops, -1);

unregister_sound_dsp(audio_dev_dsp);
unregister_sound_mixer(audio_dev_mixer);

支持的采样率如下,另外几种如8k采样率没有支持
case 16000:
    rIISPSR = 0x77; //使能预分频器a/b enable division factor=16,so,MCLK/16=256fs->fs=65.536M/256*16=16khz   
    break;   

case 22050:
    rIISPSR = 0x55; //使能预分频器a/b enable division factor=12,so,MCLK/12=256fs->fs=65.536M/256*12=21.484khz   
    break;

case 32000:
    rIISPSR = 0x33; //使能预分频器a/b enable division factor=8,so,MCLK/8=256fs->fs=65.536M/256*8=32.226khz           
    break;

case 48000:
    rIISPSR = 0xcc; //使能预分频器a/b enable division factor=5,so,MCLK/5=256fs->fs=65.536M/256*5=51.5625khz           
    break;

case 64000:
    rIISPSR = 0x11; //使能预分频器a/b enable division factor=4,so,MCLK/4=256fs->fs=65.536M/256*4=64.4531khz           
    break;
   
    //defualt is 44100       
default:
    val = 44100;
    rIISPSR = 0x22; //使能预分频器a/b enable division factor=6,so,MCLK/6=256fs->fs=65.536M/256*6=42.928khz
}

具体见附件
2    Uclinux很奇怪,linux2.4的menuconfig中竟然找不到任何oss的菜单,我以为已经默认编译进去内核了。结果
编译好的uda1314.o插入内核后,出现一下警告:

点击看大图
看来内核并没有把oss的驱动编译到内核中,到linux2.4/driver/sound目录查看soundcore.c确实没有编译。
打开Makefile看看需要配置什么变量才可以编译soundcore:
# Each configuration option enables a list of files.

obj-$(CONFIG_SOUND)        += soundcore.o
obj-$(CONFIG_SOUND_OSS)        += sound.o
obj-$(CONFIG_SOUND_CS4232)    += cs4232.o ad1848.o

打开/uClinux-dist/linux-2.4.x/arch/armnomm/config.in,搜索CONFIG_SOUND,没有
找个空点的地方,插入如下菜单:
mainmenu_option next_comment
   comment 'Sound'

   tristate 'Sound support' CONFIG_SOUND
   if [ "$CONFIG_SOUND" != "n" ]; then
      source drivers/sound/Config.in
   fi
endmenu

make menuconfig配置uclinux,然后编译make!
一会出现编译错误:
soundcard.c: In function `sound_mmap':
soundcard.c:467: structure has no member named `vm_pgoff'
soundcard.c:479: structure has no member named `vm_page_prot'
意思是结构体vm_area_struct没有上述的两个成员。
搜索下这个结构体的定义:
#ifndef NO_MM

/*
 * This struct defines a memory VMM memory area. There is one of these
 * per VM-area/task.  A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
    struct mm_struct * vm_mm;    /* The address space we belong to. */
    unsigned long vm_start;        /* Our start address within vm_mm. */
    unsigned long vm_end;        /* The first byte after our end address
                       within vm_mm. */

    /* linked list of VM areas per task, sorted by address */
    struct vm_area_struct *vm_next;

    pgprot_t vm_page_prot;        /* Access permissions of this VMA. */
    unsigned long vm_flags;        /* Flags, listed below. */

    rb_node_t vm_rb;

    /*
     * For areas with an address space and backing store,
     * one of the address_space->i_mmap{,shared} lists,
     * for shm areas, the list of attaches, otherwise unused.
     */
    struct vm_area_struct *vm_next_share;
    struct vm_area_struct **vm_pprev_share;

    /* Function pointers to deal with this struct. */
    struct vm_operations_struct * vm_ops;

    /* Information about our backing store: */
    unsigned long vm_pgoff;        /* Offset (within vm_file) in PAGE_SIZE
                       units, *not* PAGE_CACHE_SIZE */
    struct file * vm_file;        /* File we map to (can be NULL). */
    unsigned long vm_raend;        /* XXX: put full readahead info here. */
    void * vm_private_data;        /* was vm_pte (shared mem) */
};

#else /* NO_MM */

/* This dummy vm_area_struct does not define a VM area, it is only
   used to convey data between do_mmap and a f_op's mmap function. */
 
struct vm_area_struct {
    unsigned long vm_start;
    unsigned long vm_end;
    unsigned short vm_flags;
    unsigned long vm_offset;
};

#endif /* NO_MM */
uclinux是没有MMU的,所以少了几个成员。看看出错的函数的代码,修改和屏蔽相关代码

static int sound_mmap(struct file *file, struct vm_area_struct *vma)
{
    int dev_class;
    unsigned long size;
    struct dma_buffparms *dmap = NULL;
    int dev = MINOR(file->f_dentry->d_inode->i_rdev);

    dev_class = dev & 0x0f;
    dev >>= 4;

    if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
        printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
        return -EINVAL;
    }
    lock_kernel();
    if (vma->vm_flags & VM_WRITE)    /* Map write and read/write to the output buf */
        dmap = audio_devs[dev]->dmap_out;
    else if (vma->vm_flags & VM_READ)
        dmap = audio_devs[dev]->dmap_in;
    else {
        printk(KERN_ERR "Sound: Undefined mmap() access\n");
        unlock_kernel();
        return -EINVAL;
    }

    if (dmap == NULL) {
        printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
        unlock_kernel();
        return -EIO;
    }
    if (dmap->raw_buf == NULL) {
        printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
        unlock_kernel();
        return -EIO;
    }
    if (dmap->mapping_flags) {
        printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
        unlock_kernel();
        return -EIO;
    }
    if (vma->vm_pgoff != 0) {
        printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
        unlock_kernel();
        return -EINVAL;
    }
    size = vma->vm_end - vma->vm_start;

    if (size != dmap->bytes_in_use) {
        printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
    }
    if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf),
        vma->vm_end - vma->vm_start,
        vma->vm_page_prot)) {
        unlock_kernel();
        return -EAGAIN;
    }

    dmap->mapping_flags |= DMA_MAP_MAPPED;

    if( audio_devs[dev]->d->mmap)
        audio_devs[dev]->d->mmap(dev);

    memset(dmap->raw_buf,
           dmap->neutral_byte,
           dmap->bytes_in_use);
    unlock_kernel();
    return 0;
}
修改的:vma->vm_pgoff != 0--》vma->vm_offset != 0
屏蔽的是因为下面这个函数没什么作用:
/*  Note: this is only safe if the mm semaphore is held when called. */
int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long size, pgprot_t prot)
{
    return -EPERM;
}
所以屏蔽掉这个函数调用
#ifndef NO_MM   
if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf),
        vma->vm_end - vma->vm_start,
        pgprot_t prot)) {
        unlock_kernel();
        return -EAGAIN;
    }
#endif

修改后编译通过。
好,插入模块,哦对了,在插入模块之前,应该先在/dev目录下建立dsp和mixer的节点,主次编号参考host的。
# insmod ./uda1314.o                                                                                                                    
Using ./uda1314.o                                                                                                                       
enter dev Init..                                                                                                                        
irq_dma0 register:register    success                                                                                                      
exit dev init..                                                                                                                         
# lsmod                                                                                                                                 
Module                  Size  Used by    Not tainted                                                                                    
uda1314                54904   0 (unused)

先用test_music这个简单应用程序测试下wav的播放,采样率44.1k
# ./test_music dream-441k.wav                                                                                                           
This is a wav&mp3 play program.                                                                                                         
Please add a file name and the audio rate!                                                                                              
such as "./test_music test.mp3"                                                                                                         
                                                                                                                                        
                                                                                                                                        
File name : dream-441k.wav                                                                                                              
open succeed!                                                                                                                           
#<1>#<1>#<1>#<1>#<1>#audio_set_dsp_speed:44100

OK,没问题!下面修改malld,测试mp3播放。

madlld的主要修改是设备名称:
#define DEVICE_NAME "/dev/dsp"
还有,打开设备之后,设置soundcard的参数。
       if ((sfd = open("/dev/iis", O_WRONLY)) < 0)
    {
        printf("can not open device!!!\n");
        return 5;
        }

      /*设置采样格式*/
        format = AFMT_S16_LE;
      
        if (ioctl(sfd, SNDCTL_DSP_SETFMT, &format) == -1)
        {
                /* fatal error */
                printf("SNDCTL_DSP_SETFMT error\n");
                return -1;              
        }

        if (format != AFMT_S16_LE)
        {
                /* 本设备不支持选择的采样格式. */
                printf("s3c44b0x oss driver does not support AFMT_S16_LE\n");
        }

        /*设置通道数*/
        if (ioctl(sfd, SNDCTL_DSP_CHANNELS, &channels) == -1)
        {
                /* Fatal error */
                printf("SNDCTL_DSP_CHANNELS error");
                return -1;
        }

        if (channels != 2)
        {
                /* 本设备不支持立体声模式 ... */
                printf("s3c44b0x oss driver does not support 2 channels\n");
        }
      
        /*设置采样速率*/

        if (ioctl(sfd, SNDCTL_DSP_SPEED, &speed)==-1)
        {
                /* Fatal error */
                printf("SNDCTL_DSP_SPEED error\n");
                return -1;
        }
 
修改好代码后,编译连接libmad,生成madlld。
arm-elf-gcc -Wl,-elf2flt -o madlld madlld.c bstdfile.c ./libmad.a

还是测试一下wav,没问题。
# ./madlld dream-441k.wav 44100                                                                                                         
This is a wav&mp3 play program.                                                                                                         
Please add a file name and the audio rate!                                                                                              
such as "./madlld test.mp3 44100"                                                                                                       
open succeed!                                                                                                                           
#<1>#<1>#<1>#<1>#<1>#audio_set_dsp_speed:44100                                                                                          
                                                                                                                                                                                                                                                                             
File name : dream-441k.wav                                                                                                              
speed : 44100 

测试下22k的mp3,有点卡!44k的,不能听!预料之中。
# ./madlld dream-22050.mp3 22050                                                                                                        
This is a wav&mp3 play program.                                                                                                         
Please add a file name and the audio rate!                                                                                              
such as "./madlld test.mp3 44100"                                                                                                       
                                                                                                                                        
                                                                                                                                        
File name : dream-22050.mp3                                                                                                             
speed : 22050                                                                                                                           
open succeed!                                                                                                                           
#<1>#<1>#<1>#<1>#<1>#audio_set_dsp_speed:22050                                                                                          
Play mp3!                                                                                                                               
./madlld: recoverable frame level error (lost synchronization)                                                                          
./madlld: 96000 kb/s audio mpeg layer III stream with crc, joint (MS/intensity) stereo with no emphasis at 22050 Hz sample rate         
@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>@<1>)
./madlld: end of input stream                                                                                                           
./madlld: 9245 frames de<4>enter release function..                                                                                     
coded (4:01.502).                                                                                                                       
@<4>exit IISClose..
在22k和44k的采样音乐播放过程中,打印了很多的@字符,表示sound_fifo空,驱动程序处于饥饿状态。

    if (__kfifo_data_len(&sound_fifo)==0) { /* empty */
        sound_output_active = 0;
    printk(KERN_ALERT "@");
    wake_up_interruptible(&sound_empty_queue);
    }

为什么会这样阿?问题在于madlld的程序结构,程序是先读入文件到输入缓冲区,然后解码到输出缓冲区,输出缓冲区满后再把它写入驱动程序空间。在采样
低如16k的时候,数据压缩比较小,解码比较快,驱动程序一直忙着搬运!但是如果采样率高到一定时候,解码上耗费了很长时间,驱动程序处于半饥半饱状态,
所以。。。。。。驱动程序没有问题,madlld的结构有问题,需要加入多线程的支持,开一个线程解码,另外一个线程写解码好的数据到驱动空间。
先把下面的madlld的缓冲区调整一下大小优化下,加入多线程的支持,看下回分解。
/****************************************************************************
 * Main decoding loop. This is where mad is used.                            *
 ****************************************************************************/
#define INPUT_BUFFER_SIZE    (5*8192)
#define OUTPUT_BUFFER_SIZE    (4*8192) /* Must be an integer multiple of 4. */    

https://static.assets-stash.eet-china.com/album/old-resources/2010/4/30/018ee172-7efb-4118-97f2-8b20d66b9b97.zip 

文章评论0条评论)

登录后参与讨论
我要评论
0
9
关闭 站长推荐上一条 /2 下一条