AudioFlinger往hal层写数据时会调用到Hal层的out_write函数

在打开pcm stream设备的时候会调用到select_device和pcm_open函数

enable_snd_device和 enable_audio_route函数
打开设备通路的流程:

int select_devices(struct audio_device *adev, audio_usecase_t uc_id){    usecase = get_usecase_from_list(adev, uc_id);//根据uc_id获取当前的usecase      if ((usecase->type == VOICE_CALL) || (usecase->type == VOIP_CALL)  ||     。。。。    out_snd_device = platform_get_output_snd_device(adev->platform, usecase->stream.out->devices);    in_snd_device = platform_get_input_snd_device
if (usecase->type == PCM_PLAYBACK) {              usecase->devices = usecase->stream.out->devices;              in_snd_device = SND_DEVICE_NONE;              out_snd_device = platform_get_output_snd_device(adev->platform,                                              usecase->stream.out->devices);  ...//如果当前是音乐播放的话只选择输出设备  } else if (usecase->type == PCM_CAPTURE) {              usecase->devices = usecase->stream.in->device;              out_snd_device = SND_DEVICE_NONE;              in_snd_device = platform_get_input_snd_device(adev->platform,                                                                 AUDIO_DEVICE_NONE);               ...//如果当时是录音的话只选择输入设备  }  }     ...  if (out_snd_device != SND_DEVICE_NONE) {  ...          enable_snd_device(adev, out_snd_device, false);//打开输出设备通路  }  if (in_snd_device != SND_DEVICE_NONE) {      ....      enable_snd_device(adev, in_snd_device, false);//打开输入设备通路  }   ...  usecase->in_snd_device = in_snd_device;  usecase->out_snd_device = out_snd_device;//更新当前的输入和输出设备  enable_audio_route(adev, usecase, true);//使能audio route 

int enable_snd_device(struct audio_device *adev,
snd_device_t snd_device,
bool __unused update_mixer)
{
….
if(platform_get_snd_device_name_extn(adev->platform, snd_device, device_name) < 0 ) {
ALOGE(“%s: Invalid sound device returned”, func);
return -EINVAL;
}//根据snd_device名字在device_table上查找device_name,如[SND_DEVICE_OUT_HANDSET] = “handset”等

audio_route_apply_and_update_path(adev->audio_route, device_name);
//跟当前的device_name在 mixer_paths.xml上找到对应的设备通路并打开

}

Audio_route.c (\system\media\audio_route)int audio_route_apply_and_update_path(struct audio_route *ar, const charchar *name)  {  if (audio_route_apply_path(ar, name) < 0) {  //更新声卡中所有控件的值          return -1;      }  return audio_route_update_path(ar, name, false /*reverse*/);  //将更新后声卡控件的值写入到“/dev/snd/controlC0”中  }  
int audio_route_apply_path(struct audio_route *ar, const charchar *name)  {      struct mixer_path *path;  ...      path = path_get_by_name(ar, name);//根据当前设备名字找到该设备通路  ...      path_apply(ar, path);//根据当前使能通路控件值更新声卡中控件的状态,即mixer_state      return 0;  }  static int path_apply(struct audio_route *ar, struct mixer_path *path)  {  ...      for (i = 0; i < path->length; i++) {          ctl_index = path->setting[i].ctl_index;//通路控件的id          ctl = index_to_ctl(ar, ctl_index);          type = mixer_ctl_get_type(ctl);          if (!is_supported_ctl_type(type))              continue;  ...          memcpy(ar->mixer_state[ctl_index].new_value, path->setting[i].value,                 path->setting[i].num_values * sizeof(int));//更新声卡中该id对应的空间的值      }      return 0;  }  static int audio_route_update_path(struct audio_route *ar, const charchar *name, bool reverse)  {  ...  path = path_get_by_name(ar, name);//找出当前设备通路  ...  i = reverse ? (path->length - 1) : 0;  end = reverse ? -1 : (int32_t)path->length;  while (i != end) {  ...  ctl_index = path->setting[i].ctl_index;//找出设备通路中控件id  struct mixer_state * ms = &ar->mixer_state[ctl_index];//根据控件id找到当前控件的值   ...   /* if any value has changed, update the mixer */   for (j = 0; j < ms->num_values; j++) {      if (ms->old_value[j] != ms->new_value[j]) {          if (type == MIXER_CTL_TYPE_ENUM)              mixer_ctl_set_value(ms->ctl, 0, ms->new_value[0]);  //将值写入到底层控件中          else              mixer_ctl_set_array(ms->ctl, ms->new_value, ms->num_values);              memcpy(ms->old_value, ms->new_value, ms->num_values * sizeof(int));              break;      }    }    i = reverse ? (i - 1) : (i + 1);  }  return 0;  }  
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)  {  struct snd_ctl_elem_value ev;  ...  memset(&ev, 0, sizeof(ev));  ev.id.numid = ctl->info->id.numid;  ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);//读当前所有音频控件的值  ...  return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);//将新的控件的值写入到/dev/snd/controlC0中去  }  

audio route的流程:

int enable_audio_route(struct audio_device *adev,                                struct audio_usecase *usecase,                                bool __unused update_mixer)  {  ...  platform_add_backend_name(mixer_path, snd_device);//在通路名上添加设备名,用于定制不同的audio route  ...  audio_route_apply_and_update_path(adev->audio_route, mixer_path);//打开audio route,其流程与之前的打开设备通路的一样  ...  }  

以上设备通路和audio route打开的过程中,最终都走到了mixer_ctl_set_value函数,这个函数里面利用底层提供给用户的空间的设备节点来控制底层的音频控件
首先,我们在使用 snd_card_create函数创建声卡的时候,会调用snd_ctl_create函数创建control逻辑设备,

int snd_ctl_create(struct snd_card *card)  {      static struct snd_device_ops ops = {          .dev_free = snd_ctl_dev_free,          .dev_register = snd_ctl_dev_register,          .dev_disconnect = snd_ctl_dev_disconnect,      };//这个ops很关键      if (snd_BUG_ON(!card))          return -ENXIO;      return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//把control设备放入到card->devices中去  }  

上面创建control逻辑设备的时候,ops中的snd_ctl_dev_register接口,会调用snd_register_device传递snd_ctl_f_ops,这个osp就是实际使用到的control设备的操作函数。

static const struct file_operations snd_ctl_f_ops ={.owner =THIS_MODULE,.read =snd_ctl_read,.open =snd_ctl_open,.release =snd_ctl_release,.llseek =no_llseek,.poll =snd_ctl_poll,.unlocked_ioctl =snd_ctl_ioctl,.compat_ioctl =snd_ctl_ioctl_compat,.fasync =snd_ctl_fasync,};

在用户空间中 mixer_ctl_set_value函数读写底层control节点,其实就是通过snd_ctl_ioctl函数来实现的:

static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg){...case SNDRV_CTL_IOCTL_ELEM_READ:return snd_ctl_elem_read_user(card, argp);case SNDRV_CTL_IOCTL_ELEM_WRITE:return snd_ctl_elem_write_user(ctl, argp);...}

最终,snd_ctl_elem_read_user和snd_ctl_elem_write_user函数都分别调用了snd_kcontrol的get和put函数,实现了对dapm kcontrol控件的控制。

pcm_open

struct pcm_config pcm_config_deep_buffer = {    .channels = 2,    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,//48000    .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,//960,硬件一次消耗的数据    .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,//8    .format = PCM_FORMAT_S16_LE,//16 bits = 2bytes(即2字节)    .start_threshold = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,    .stop_threshold = INT_MAX,    .avail_min = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,};

1 frame = channels * format = 2*2字节 = 4字节 (32bits)
Period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE 即 960frames = 960*32字节

struct pcm *pcm_open(unsigned int card, unsigned int device,                     unsigned int flags, struct pcm_config *config){struct snd_pcm_hw_params params;//硬件参数struct snd_pcm_sw_params sparams;//软件参数...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');//P即为底层对应的dai_linkpcm->flags = flags;pcm->fd = open(fn, O_RDWR);//打开底层设备节点param_init(&params);param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,                   pcm_format_to_alsa(config->format));param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,                   SNDRV_PCM_SUBFORMAT_STD);param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,                  pcm_format_to_bits(config->format));param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,                  pcm_format_to_bits(config->format) * config->channels);param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,                  config->channels);param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);if (flags & PCM_MMAP)param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,                   SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);elseparam_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,                   SNDRV_PCM_ACCESS_RW_INTERLEAVED);//设置硬件参数if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {        oops(pcm, errno, "cannot set hw params");        goto fail_close;}//将硬件参数参数写到设备节点中。/* get our refined hw_params */config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);//硬件一次性消耗数据量config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);pcm->buffer_size = config->period_count * config->period_size;//硬件整体缓冲大小 if (flags & PCM_MMAP) {     pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),                                PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);  //将pcm->buffer_size映射进内存     ...  }  memset(&sparams, 0, sizeof(sparams));  sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;  sparams.period_step = 1;  sparams.start_threshold = config->start_threshold;  sparams.stop_threshold = config->stop_threshold;  sparams.avail_min = config->avail_min;  sparams.xfer_align = config->period_size / 2; /* needed for old kernels */  sparams.silence_size = 0;  sparams.silence_threshold = config->silence_threshold;  pcm->boundary = sparams.boundary = pcm->buffer_size;  if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {        oops(pcm, errno, "cannot set sw params");        goto fail;  }//将软件参数写入到底层设备节点中  rc = pcm_hw_mmap_status(pcm);  ..}

/dev/snd/pcmC%uD%u%c 节点底层相关的操作函数在:Pcm_native.c (\linux\android\kernel\sound\core):

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,}};

硬件参数写入:
snd_pcm_playback_ioctl -> snd_pcm_playback_ioctl1 -> snd_pcm_common_ioctl1-> snd_pcm_hw_params_user(substream, arg);
其中 arg 对应的是 params
static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
struct snd_pcm_hw_params *params;
int err;

params = memdup_user(_params, sizeof(*params)); //从用户向内核拷贝参数

err = snd_pcm_hw_params(substream, params);//pcm硬件设备参数设置

kfree(params);
return err;
}

软件参数写入,流程上硬件的类似,最终走到snd_pcm_sw_params函数。

pcm_write函数

int pcm_write(struct pcm *pcm, const void *data, unsigned int count){struct snd_xferi x;...x.buf = (void*)data;x.frames = count / (pcm->config.channels *                        pcm_format_to_bits(pcm->config.format) / 8);//计算出帧数。。。。for (;;) {   if (!pcm->running) {      if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))         return oops(pcm, errno, "cannot prepare channel");         if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))            return oops(pcm, errno, "cannot write initial data");         pcm->running = 1;         return 0;   }   if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {         pcm->running = 0return oops(pcm, errno, "cannot write stream data");   }   return 0;}}底层相关操作函数:snd_pcm_playback_ioctl1() -> SNDRV_PCM_IOCTL_WRITEI_FRAMES -> snd_pcm_lib_write() ->snd_pcm_lib_write1() -> snd_pcm_lib_write_transfer()

具体流程如下:
在snd_pcm_playback_ioctl1函数中,根据SNDRV_PCM_IOCTL_WRITEI_FRAMES参数,将用户空间传下来的数据传递给snd_pcm_lib_write()函数:

if (copy_from_user(&xferi, _xferi, sizeof(xferi)))return -EFAULT;result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);snd_pcm_lib_write()函数由通过snd_pcm_lib_write1()函数调用snd_pcm_lib_write_transfer()将size大小的数据buf传送给DSP:return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_write_transfer);static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,    unsigned long data,   snd_pcm_uframes_t size,   int nonblock,   transfer_f transfer){...while (size > 0) {...err = transfer(substream, appl_ofs, data, offset, frames);//这个transfer即为snd_pcm_lib_write_transfer,在其中会调用substream->ops->copy(substream, -1, hwoff, buf, frames) 拷贝数据...if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {err = snd_pcm_start(substream);...}}...}

下面看一下snd_pcm_lib_write_transfer和snd_pcm_start两个函数。

static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,     unsigned int hwoff,     unsigned long data, unsigned int off,     snd_pcm_uframes_t frames){struct snd_pcm_runtime *runtime = substream->runtime;...char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);if (substream->ops->copy) {if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)//调用substeam的copy函数拷贝数据到dai上去return err;} else {char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))    //直接将数据拷贝到硬件去return -EFAULT;}return 0;}int snd_pcm_start(struct snd_pcm_substream *substream){return snd_pcm_action(&snd_pcm_action_start, substream,     SNDRV_PCM_STATE_RUNNING);}其中,static struct action_ops snd_pcm_action_start = {.pre_action = snd_pcm_pre_start,.do_action = snd_pcm_do_start,.undo_action = snd_pcm_undo_start,.post_action = snd_pcm_post_start};

在snd_pcm_start函数中有以下流程:
snd_pcm_start-> snd_pcm_action -> snd_pcm_action_single

在snd_pcm_action_single中会分别执行:
ops->pre_action(substream, state);
ops->do_action(substream, state);
即snd_pcm_pre_start() 和snd_pcm_do_start() 函数,在snd_pcm_do_start函数中会执行:
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); 即触发数据拷贝。

Substream的ops都是在platform中实现的,如在snd_soc_dai_link中,我们设置了platform_name = “msm-pcm-dsp.0”
则可以找到其对应的ops函数在:

Msm-pcm-q6-v2.c (\kernel\sound\soc\msm\qdsp6v2)static struct snd_pcm_ops msm_pcm_ops = {.open           = msm_pcm_open,.copy= msm_pcm_copy,.hw_params= msm_pcm_hw_params,.close          = msm_pcm_close,.ioctl          = snd_pcm_lib_ioctl,.prepare        = msm_pcm_prepare,.trigger        = msm_pcm_trigger,.pointer        = msm_pcm_pointer,.mmap= msm_pcm_mmap,};

用户空间pcm_write函数分别调用了底层的msm_pcm_copy和msm_pcm_trigger函数,在msm_pcm_copy函数中使用了q6asm_write函数向DSP中写入数据,q6asm_write和msm_pcm_trigger中最终都是通过apr_send_pkt来发送数据的。

更多相关文章

  1. android日期选择控件
  2. Android之项目推荐使用的第三方库
  3. Android(安卓)Activity 二
  4. 分享Android中ExpandableListView控件使用教程
  5. Android(安卓)TTS学习——继续爱的表白
  6. Kotlin入门(三)使用协程
  7. android 循环播放图片实现
  8. Android(安卓)Retrofit 源码系列(二)~ 自定义 CallAdapter
  9. android 控件翻转切换布局

随机推荐

  1. Android——全局获取Context
  2. Android(安卓)Automotive 车载应用对驾驶
  3. Android(安卓)MPAndroidChart--使用柱状
  4. GreenDao 3.0 简介、使用及踩坑
  5. Android(安卓)进阶解密读书笔记1
  6. Android(安卓)Studio 3.4 注解处理器不能
  7. Android(安卓)实现ListView 3D效果 - 1
  8. Unknown host ‘XXXX: nodename nor serv
  9. 一文详解Android(安卓)轻量级存储方案的
  10. Android(安卓)Secret Code