OSS 44B0X mp3by 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条评论)
登录后参与讨论