Linux i2c->sound

平台:i.MX6ul


// i2c总线
struct bus_type i2c_bus_type = {
 .name  = "i2c",
 .match  = i2c_device_match,
 .probe  = i2c_device_probe,
 .remove  = i2c_device_remove,
 .shutdown = i2c_device_shutdown,
};

/* 当device或driver被加载时,总线会调用该match方法对device和driver进行匹配,如果有匹配device和driver将调用driver的probe函数 */
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
 struct i2c_client *client = i2c_verify_client(dev);
 struct i2c_driver *driver;

 if (!client)
  return 0;

 /* 3中方式对device和driver进行匹配,匹配成功返回1,然后i2c_imx_probe将会被调用 */
 if (of_driver_match_device(dev, drv))// 1、从设备树节点进行匹配
  return 1;

 if (acpi_driver_match_device(dev, drv))// 2、没使用过ACPI,这个应该是X86
  return 1;

 driver = to_i2c_driver(drv);
 if (driver->id_table)// 3、id_table进行匹配
  return i2c_match_id(driver->id_table, client) != NULL;

 return 0;


i2c_adap_imx_init(void)// drivers\i2c\busses\i2c-imx.c
 platform_driver_register(&i2c_imx_driver);------------------------>


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

              设备层

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
static struct platform_driver i2c_imx_driver = {
 .probe = i2c_imx_probe,
 .remove = i2c_imx_remove,
 .driver = {
  .name = DRIVER_NAME,
  .owner = THIS_MODULE,
  .of_match_table = i2c_imx_dt_ids,
  .pm = IMX_I2C_PM,
 },
 .id_table = imx_i2c_devtype,
};
 
static int i2c_imx_probe(struct platform_device *pdev)
 struct imx_i2c_struct *i2c_imx;
 
 
 phy_addr = (dma_addr_t)res->start;// DMA使用
 i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);// 分配i2c_imx(同时也分配了i2c_adapter),i2c_imx结构体包含了struct i2c_adapter adapter;
 i2c_imx->adapter.owner  = THIS_MODULE;
 i2c_imx->adapter.algo  = &i2c_imx_algo;// 算法,关注(硬件传输数据)
 i2c_imx->adapter.dev.parent = &pdev->dev;
 i2c_imx->adapter.nr  = pdev->id;
 i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
 i2c_imx->base   = base;
 
 ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_NO_SUSPEND, pdev->name, i2c_imx);// 设置中断处理函数,devm_打头的不需要free
 init_waitqueue_head(&i2c_imx->queue);// 初始化等待队列
 ret = i2c_add_numbered_adapter(&i2c_imx->adapter);//
    if (adap->nr == -1) /* -1 means dynamically assign bus id */
     return i2c_add_adapter(adap);
        return i2c_register_adapter(adapter);-------------------------------------> i2c_register_adapter
    return __i2c_add_numbered_adapter(adap);
       i2c_register_adapter(adap);// 同样走上面这套-------------------------------------> i2c_register_adapter
 /* Init DMA config if supported */
 i2c_imx_dma_request(i2c_imx, phy_addr);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
static int i2c_register_adapter(struct i2c_adapter *adap)// drivers\i2c\i2c-core.c
 adap->dev.bus = &i2c_bus_type;// 挂到i2c总线上,device和driver之前如何匹配查看i2c_bus_type的match方法(i2c_device_match)
 adap->dev.type = &i2c_adapter_type;// 类型为adapter,主意和下面的client区别
 res = device_register(&adap->dev);// 注册adapter的device
   of_i2c_register_devices(adap);
     for_each_available_child_of_node(adap->dev.of_node, node)
      of_i2c_register_device(adap, node);
       i2c_new_device(adap, &info);// 创建i2c_client
        struct i2c_client *client;
        client = kzalloc(sizeof *client, GFP_KERNEL);// 同时也分配了一个device结构体
        client->adapter = adap;// 设置i2c_client的adapter
        client->flags = info->flags;
        client->addr = info->addr;// 设备地址
        client->irq = info->irq;
        i2c_check_client_addr_validity(client);// 检查设备地址是否有效(分10位和7位地址2中情况)
        i2c_check_addr_busy(adap, client->addr);// 检查设备地址是否被占用
        
        client->dev.parent = &client->adapter->dev;
        client->dev.bus = &i2c_bus_type;// 也挂到i2c总线上
        client->dev.type = &i2c_client_type;// 类型为client,主意和上面adapter的区别
        client->dev.of_node = info->of_node;
        client->dev.fwnode = info->fwnode;
        device_register(&client->dev)// 注册client的device
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

              驱动层

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static struct snd_soc_dai_driver wm8960_dai = {// sound\soc\codecs\wm8960.c
 .name = "wm8960-hifi",
 .playback = {
  .stream_name = "Playback",
  .channels_min = 1,
  .channels_max = 2,
  .rates = WM8960_RATES,
  .formats = WM8960_FORMATS,},
 .capture = {
  .stream_name = "Capture",
  .channels_min = 1,
  .channels_max = 2,
  .rates = WM8960_RATES,
  .formats = WM8960_FORMATS,},
 .ops = &wm8960_dai_ops,
 .symmetric_rates = 1,
};

codec: wm8960@1a {// 设备树里面定义的
 compatible = "wlf,wm8960";
 reg = <0x1a>;
 clocks = <&clks IMX6UL_CLK_SAI2>;
 clock-names = "mclk";
 wlf,shared-lrclk;
};

static const struct of_device_id wm8960_of_match[] = {// sound\soc\codecs\wm8960.c
       { .compatible = "wlf,wm8960", },
       { }
};
 
static struct i2c_driver wm8960_i2c_driver = {// sound\soc\codecs\wm8960.c
 .driver = {
  .name = "wm8960",
  .owner = THIS_MODULE,
  .of_match_table = wm8960_of_match,
 },
 .probe =    wm8960_i2c_probe,
 .remove =   wm8960_i2c_remove,
 .id_table = wm8960_i2c_id,
};

module_i2c_driver(wm8960_i2c_driver);// i2c_driver注册、反注册都实现了

#define module_i2c_driver(__i2c_driver) \// include\linux\i2c.h
 module_driver(__i2c_driver, i2c_add_driver, \
   i2c_del_driver)
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
 driver->driver.owner = owner;
 driver->driver.bus = &i2c_bus_type;
 driver_register(&driver->driver);// 注册i2c的driver
  i2c_for_each_dev(driver, __process_new_driver);
  
static int __process_new_driver(struct device *dev, void *data)
{
 if (dev->type != &i2c_adapter_type)
  return 0;
 return i2c_do_add_adapter(data, to_i2c_adapter(dev));
    i2c_detect(adap, driver);// 看下有没有驱动注册的那个设备
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int wm8960_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)// sound\soc\codecs\wm8960.c
 i2c_set_clientdata(i2c, wm8960);
 ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8960, &wm8960_dai, 1);// 注册一个snd_soc_codec



static int32_t ReOpenPcmAndSetParams(struct AlsaCapture *captureIns, const struct AudioHwCaptureParam *handleData) { AUDIO_FUNC_LOGE("capture ReOpenPcmAndSetParams enter."); int32_t ret; struct AlsaSoundCard *cardIns = (struct AlsaSoundCard *)captureIns; CHECK_NULL_PTR_RETURN_DEFAULT(captureIns); CHECK_NULL_PTR_RETURN_DEFAULT(cardIns); CHECK_NULL_PTR_RETURN_DEFAULT(handleData); enum AudioCategory scene = handleData->frameCaptureMode.attrs.type; AUDIO_FUNC_LOGE("capture ReOpenPcmAndSetParams scene: %{public}d.", scene); AUDIO_FUNC_LOGI("capture snd_pcm_opendevName: %{public}s begin.", cardIns->devName); ret = snd_pcm_open(&cardIns->pcmHandle, cardIns->devName, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); AUDIO_FUNC_LOGI("capture snd_pcm_open devName: %{public}s end.", cardIns->devName); if (ret < 0) { AUDIO_FUNC_LOGE("snd_pcm_open fail: %{public}s!", snd_strerror(ret)); SndCloseHandle(&captureIns->soundCard); return HDF_FAILURE; } ret = SndOpenMixer(&captureIns->soundCard); if (ret != HDF_SUCCESS) { AUDIO_FUNC_LOGE("SndOpenMixer failed"); SndCloseHandle(&captureIns->soundCard); return HDF_FAILURE; } ret = CaptureInitImpl(captureIns); if (ret < 0) { AUDIO_FUNC_LOGE("ReOpenPcmAndSetParams CaptureInitImpl fail"); return HDF_FAILURE; } ret = CaptureSetHwParamsImpl(captureIns, handleData); if (ret < 0) { AUDIO_FUNC_LOGE("ReOpenPcmAndSetParams CaptureSetHwParamsImpl fail"); return HDF_FAILURE; } ret = snd_pcm_prepare(cardIns->pcmHandle); if (ret < 0) { AUDIO_FUNC_LOGE("snd_pcm_prepare fail: %{public}s", snd_strerror(ret)); return HDF_FAILURE; } return HDF_SUCCESS; } 详细解释代码
最新发布
07-30
<think>好的,我现在需要详细解释用户提供的C函数`ReOpenPcmAndSetParams`。首先,我会通读代码,理解其整体结构和功能。这个函数看起来是与音频处理相关的,特别是与ALSA(Advanced Linux Sound Architecture)库交互,重新打开PCM设备并设置参数。 首先,函数签名是`static int32_t ReOpenPcmAndSetParams(struct AlsaCapture *captureIns, const struct AudioHwCaptureParam *handleData)`。函数是静态的,意味着它只能在定义它的文件中使用。返回类型是`int32_t`,可能用于返回状态码,如成功或失败。 接下来,函数开始处有一些日志输出,使用`AUDIO_FUNC_LOGE`和`AUDIO_FUNC_LOGI`记录进入函数的信息。这可能用于调试或运行时监控。然后声明了几个变量,包括`ret`用于保存返回状态,`cardIns`是一个指向`AlsaSoundCard`结构的指针,由传入的`captureIns`转换而来。这里使用了`CHECK_NULL_PTR_RETURN_DEFAULT`宏来检查指针是否为NULL,如果是,则返回默认值(可能是HDF_FAILURE或其他错误码)。这确保了函数不会因为空指针而崩溃。 接下来,函数获取`handleData`中的`scene`变量,记录日志。然后尝试使用`snd_pcm_open`打开PCM设备。参数包括流类型为`SND_PCM_STREAM_CAPTURE`(捕获/录音),模式为非阻塞`SND_PCM_NONBLOCK`。如果打开失败,记录错误,调用`SndCloseHandle`关闭已打开的资源,并返回失败状态。 如果PCM打开成功,接下来调用`SndOpenMixer`打开混音器。如果失败,同样关闭资源并返回错误。然后调用`CaptureInitImpl`和`CaptureSetHwParamsImpl`函数进行初始化和设置硬件参数。这两个函数可能分别负责初始化捕获设备和配置硬件参数(如采样率、格式、通道数等)。如果其中任何一个步骤失败,返回错误。 最后,调用`snd_pcm_prepare`准备PCM设备,使其进入就绪状态,可以开始数据传输。如果失败,返回错误。 整个函数的流程大致是:检查参数有效性,打开PCM设备,打开混音器,初始化捕获设备,设置硬件参数,准备PCM设备。每一步都检查返回状态,失败时进行清理并返回错误码。 需要注意的点包括:错误处理是否完善,资源是否正确释放,以及各步骤之间的依赖关系。例如,如果`snd_pcm_open`成功但后续步骤失败,函数是否正确关闭了之前打开的PCM设备?查看代码,发现当`snd_pcm_open`失败时会调用`SndCloseHandle`,但该函数可能只关闭`cardIns`中的资源。需要确认`SndCloseHandle`是否会处理部分打开的资源,比如如果混音器还没打开,是否不会尝试关闭它。 另外,函数中的日志输出可能有助于调试,例如记录打开设备的名称和场景类型,这有助于追踪问题发生的位置。 总结来说,这个函数负责重新打开音频捕获设备并配置其参数,确保所有步骤成功完成,否则进行清理并返回错误。接下来需要详细解释每个步骤的作用,以及可能的错误处理情况。</think>该函数`ReOpenPcmAndSetParams`用于重新打开ALSA音频捕获设备(PCM)并配置其硬件参数,确保设备处于就绪状态。以下是详细解析: --- ### **功能概述** 1. **核心作用** - 重新打开PCM捕获设备并设置硬件参数(如采样率、格式等)。 - 管理混音器(Mixer)资源的开闭。 - 确保设备进入就绪状态(`snd_pcm_prepare`)以启动数据传输。 2. **关键数据结构** - `struct AlsaCapture *captureIns`:捕获设备实例,包含声卡信息。 - `struct AudioHwCaptureParam *handleData`:硬件参数配置(如采样类型、通道数)。 --- ### **代码流程解析** 1. **参数校验与日志记录** ```c CHECK_NULL_PTR_RETURN_DEFAULT(captureIns); // 检查空指针 CHECK_NULL_PTR_RETURN_DEFAULT(handleData); ``` - 使用宏`CHECK_NULL_PTR_RETURN_DEFAULT`确保输入参数非空,避免空指针访问。 2. **获取场景类型并记录日志** ```c enum AudioCategory scene = handleData->frameCaptureMode.attrs.type; AUDIO_FUNC_LOGE("capture ReOpenPcmAndSetParams scene: %d.", scene); ``` - 提取音频捕获场景类型(如语音、音乐),用于后续配置。 3. **打开PCM设备** ```c ret = snd_pcm_open(&cardIns->pcmHandle, cardIns->devName, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (ret < 0) { AUDIO_FUNC_LOGE("snd_pcm_open fail: %s!", snd_strerror(ret)); SndCloseHandle(&captureIns->soundCard); // 关闭已分配资源 return HDF_FAILURE; } ``` - 调用`snd_pcm_open`以非阻塞模式打开PCM捕获设备。 - 失败时记录错误并调用`SndCloseHandle`释放资源。 4. **打开混音器** ```c ret = SndOpenMixer(&captureIns->soundCard); if (ret != HDF_SUCCESS) { AUDIO_FUNC_LOGE("SndOpenMixer failed"); SndCloseHandle(&captureIns->soundCard); return HDF_FAILURE; } ``` - 调用`SndOpenMixer`初始化混音器句柄(用于音量控制等)。 - 失败时清理资源并返回错误。 5. **初始化捕获设备** ```c ret = CaptureInitImpl(captureIns); if (ret < 0) { AUDIO_FUNC_LOGE("ReOpenPcmAndSetParams CaptureInitImpl fail"); return HDF_FAILURE; } ``` - `CaptureInitImpl`可能负责底层捕获设备的初始化(如缓冲区分配)。 6. **设置硬件参数** ```c ret = CaptureSetHwParamsImpl(captureIns, handleData); if (ret < 0) { AUDIO_FUNC_LOGE("ReOpenPcmAndSetParams CaptureSetHwParamsImpl fail"); return HDF_FAILURE; } ``` - `CaptureSetHwParamsImpl`配置PCM硬件参数(如采样率、格式、通道数、周期大小等)。 7. **准备PCM设备** ```c ret = snd_pcm_prepare(cardIns->pcmHandle); if (ret < 0) { AUDIO_FUNC_LOGE("snd_pcm_prepare fail: %s", snd_strerror(ret)); return HDF_FAILURE; } ``` - 调用`snd_pcm_prepare`将PCM设备状态置为`PREPARED`,准备数据传输。 --- ### **关键函数与库调用** - **ALSA库函数** - `snd_pcm_open`:打开PCM设备,`SND_PCM_STREAM_CAPTURE`指定为捕获流。 - `snd_pcm_prepare`:将设备从`SETUP`状态转为`PREPARED`,确保可调用`snd_pcm_start`或直接开始读写。 - **自定义函数** - `SndOpenMixer`:打开混音器以控制音量或路由。 - `CaptureInitImpl/CaptureSetHwParamsImpl`:封装硬件初始化与参数配置逻辑。 --- ### **错误处理机制** - **资源清理** 任何步骤失败时,调用`SndCloseHandle`关闭已打开的PCM/混音器句柄,防止资源泄漏。 - **日志输出** 所有错误均通过`AUDIO_FUNC_LOGE`记录详细信息(如错误码描述),便于调试。 --- ### **设计意图** - **非阻塞模式(SND_PCM_NONBLOCK)** 避免`snd_pcm_readi`等读取操作阻塞线程,需配合轮询或异步处理。 - **分层设计** 将初始化、参数设置拆分为独立函数(`CaptureInitImpl`等),提高代码可维护性。 - **引用计数管理** 通过`cardStatus`(在`SndCloseHandle`中递减)确保资源仅在无引用时关闭。 --- ### **潜在问题与改进** 1. **资源泄漏风险** - 若`SndOpenMixer`失败,`snd_pcm_open`已打开的句柄是否被正确关闭?需确认`SndCloseHandle`是否处理部分资源。 2. **错误恢复** - 可增加重试机制(如`snd_pcm_open`失败后等待并重试),提升健壮性。 3. **参数动态调整** - 可支持根据场景(`scene`)动态调整硬件参数(如低延迟场景使用更小周期)。 --- ### **应用场景** - 音频捕获设备的动态重配置(如切换采样率)。 - 多场景切换时(如从语音通话切换到音乐录制)重新初始化设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值