status_t AudioALSAPlaybackHandlerBase::openPcmDriver(const unsigned int device)
{
mPcm = pcm_open(AudioALSADeviceParser::getInstance()->GetCardIndex(),
device, PCM_OUT | PCM_MONOTONIC, &mConfig);
return NO_ERROR;
}
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config)
{
struct pcm *pcm;
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
char fn[256];
int rc;
pcm = calloc(1, sizeof(struct pcm));
pcm->config = *config;
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
flags & PCM_IN ? 'c' : 'p');
pcm->flags = flags;
pcm->fd = open(fn, O_RDWR);
......
}
//在创建alsa主设备时设置了文件操作对象,PCM设备都是alsa的从设备,共享一个驱动,打开次设备时会调用主设备的open函数
/*static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};*/
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *new_fops;
int err = 0;
mptr = snd_minors[minor];
new_fops = fops_get(mptr->f_ops);
replace_fops(file, new_fops);
if (file->f_op->open)
err = file->f_op->open(inode, file);
return err;
}
//注册PCM设备时设置了PCM设备的文件操作对象
/*const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};*/
static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
int err = nonseekable_open(inode, file);
pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
return err;
}
void *snd_lookup_minor_data(unsigned int minor, int type)
{
struct snd_minor *mreg;
void *private_data;
if (minor >= ARRAY_SIZE(snd_minors))
return NULL;
mreg = snd_minors[minor];
if (mreg && mreg->type == type) {
private_data = mreg->private_data;
if (private_data && mreg->card_ptr)
get_device(&mreg->card_ptr->card_dev);
} else
private_data = NULL;
return private_data;
}
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
err = snd_card_file_add(pcm->card, file);//将file加入到card的files链表进行管理
while (1) {
err = snd_pcm_open_file(file, pcm, stream);
}
return err;
}
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
int err;
err = snd_pcm_open_substream(pcm, stream, file, &substream);
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
pcm_file->substream = substream;//将打开的substream保存在pcm_file中
if (substream->ref_count == 1) {
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file;//file获得打开的substream
return 0;
}
//打开pcm所属的substream,需要使用substream->ops操作函数
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_substream *substream;
int err;
err = snd_pcm_attach_substream(pcm, stream, file, &substream);//获取pcm的substream
err = snd_pcm_hw_constraints_init(substream);//初始化pcm硬件约束
if ((err = substream->ops->open(substream)) < 0)//调用substream的回调函数open
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream);//设置pcm硬件约束
*rsubstream = substream;
return 0;
}
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_str * pstr;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_ctl_file *kctl;
struct snd_card *card;
int prefer_subdevice = -1;
size_t size;
*rsubstream = NULL;
pstr = &pcm->streams[stream];
card = pcm->card;
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == task_pid(current)) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
if (prefer_subdevice != -1)
break;
}
}
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream;
substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
}
break;
case SNDRV_PCM_STREAM_CAPTURE:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream;
substream = substream->next) {
if (SUBSTREAM_BUSY(substream))
return -EAGAIN;
}
}
break;
default:
return -EINVAL;
}
if (file->f_flags & O_APPEND) {
if (prefer_subdevice < 0) {
if (pstr->substream_count > 1)
return -EINVAL; /* must be unique */
substream = pstr->substream;
} else {
for (substream = pstr->substream; substream;
substream = substream->next)
if (substream->number == prefer_subdevice)
break;
}
substream->ref_count++;
*rsubstream = substream;
return 0;
}
if (prefer_subdevice >= 0) {
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
goto __ok;
}
for (substream = pstr->substream; substream; substream = substream->next)
if (!SUBSTREAM_BUSY(substream))
break;
__ok:
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
runtime->status = snd_malloc_pages(size, GFP_KERNEL);
memset((void*)runtime->status, 0, size);
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
runtime->control = snd_malloc_pages(size, GFP_KERNEL);
memset((void*)runtime->control, 0, size);
runtime->status->state = SNDRV_PCM_STATE_OPEN;
substream->runtime = runtime;
substream->private_data = pcm->private_data;
substream->ref_count = 1;
substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
return 0;
}
在创建pcm设备时已经调用snd_pcm_set_ops函数设置了substream->ops,在打开pcm的时候需要调用substream的ops->open函数
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
const char *codec_dai_name = "multicodec";
int i, ret = 0;
/* startup the audio subsystem */
if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);//调用cpu dai driver的startup
}
if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);//调用platform driver的open
}
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream, codec_dai);//调用codec dai driver的startup
}
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream);//调用dai link的startup,没有实现
}
/* Check that the codec and cpu DAIs are compatible */
soc_pcm_init_runtime_hw(substream);//检测codec和cup dai的兼容性
return ret;
}
cpu dai driver、platform driver、codec dai driver的具体实现
static int multimedia_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_sample_rates);
return 0;
}
static int mtk_pcm_I2S0dl1_open(struct snd_pcm_substream *substream)
{
int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
AfeControlSramLock();
if (GetSramState() == SRAM_STATE_FREE) {
mtk_I2S0dl1_hardware.buffer_bytes_max = GetPLaybackSramFullSize();
mPlaybackSramState = SRAM_STATE_PLAYBACKFULL;
SetSramState(mPlaybackSramState);
} else {
mtk_I2S0dl1_hardware.buffer_bytes_max = GetPLaybackDramSize();
mPlaybackSramState = SRAM_STATE_PLAYBACKDRAM;
}
AfeControlSramUnLock();
if (mPlaybackSramState == SRAM_STATE_PLAYBACKDRAM)
AudDrv_Emi_Clk_On();
runtime->hw = mtk_I2S0dl1_hardware;
AudDrv_Clk_On();
memcpy((void *)(&(runtime->hw)), (void *)&mtk_I2S0dl1_hardware ,
sizeof(struct snd_pcm_hardware));
pI2S0dl1MemControl = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_DL1);
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_sample_rates);
return 0;
}
static int mt63xx_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *Daiport)
{
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && substream->runtime->rate) {
mBlockSampleRate[AUDIO_ANALOG_DEVICE_IN_ADC] = substream->runtime->rate;
} else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && substream->runtime->rate) {
mBlockSampleRate[AUDIO_ANALOG_DEVICE_OUT_DAC] = substream->runtime->rate;
}
return 0;
}