Linux ALSA驱动之二:声卡的创建流程 下

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: Linux ALSA驱动之二:声卡的创建流程 下

Linux ALSA驱动之二:声卡的创建流程 下


3、snd_card_new函数详解

       用于创建并初始化一个声卡的结构体

/**
 *  snd_card_new - create and initialize a soundcard structure
 *  @parent: the parent device object
 *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
 *  @xid: card identification (ASCII string)
 *  @module: top level module for locking
 *  @extra_size: allocate this extra size after the main soundcard structure
 *  @card_ret: the pointer to store the created card instance
 *
 *  The function allocates snd_card instance via kzalloc with the given
 *  space for the driver to use freely.  The allocated struct is stored
 *  in the given card_ret pointer.
 *
 *  Return: Zero if successful or a negative error code.
 */
int snd_card_new(struct device *parent, int idx, const char *xid,
        struct module *module, int extra_size,
        struct snd_card **card_ret)
{
  struct snd_card *card;
  int err;
  if (snd_BUG_ON(!card_ret))
    return -EINVAL;
  *card_ret = NULL;
  if (extra_size < 0)
    extra_size = 0;
    /* 1. 分配snd_card和private_data的空间
     在snd_card后面的空间分配,card->private_data指向该空间
    */
  card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
  if (!card)
    return -ENOMEM;
  err = snd_card_init(card, parent, idx, xid, module, extra_size);
  if (err < 0) {
    kfree(card);
    return err;
  }
  *card_ret = card;
  return 0;
}
static int snd_card_init(struct snd_card *card, struct device *parent,
       int idx, const char *xid, struct module *module,
       size_t extra_size)
{
  int err;
#ifdef CONFIG_SND_DEBUG
  char name[8];
#endif
    /* 1、根据传入的参数赋值xid, idx, module, parent */
    /* (1). 为 card->private_datad 赋值 */
  if (extra_size > 0)
    card->private_data = (char *)card + sizeof(struct snd_card);
    /* (2). 为 card->id 赋值 */
  if (xid)
    strscpy(card->id, xid, sizeof(card->id));
  err = 0;
  mutex_lock(&snd_card_mutex);
  if (idx < 0) /* first check the matching module-name slot */
    idx = get_slot_from_bitmask(idx, module_slot_match, module);
  if (idx < 0) /* if not matched, assign an empty slot */
    idx = get_slot_from_bitmask(idx, check_empty_slot, module);
  if (idx < 0)
    err = -ENODEV;
  else if (idx < snd_ecards_limit) {
    if (test_bit(idx, snd_cards_lock))
      err = -EBUSY; /* invalid */
  } else if (idx >= SNDRV_CARDS)
    err = -ENODEV;
  if (err < 0) {
    mutex_unlock(&snd_card_mutex);
    dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
       idx, snd_ecards_limit - 1, err);
    return err;
  }
  set_bit(idx, snd_cards_lock);   /* lock it */
  if (idx >= snd_ecards_limit)
    snd_ecards_limit = idx + 1; /* increase the limit */
  mutex_unlock(&snd_card_mutex);
  /* (3). 赋值parent */
    card->dev = parent;
    /* (4). 分配snd_card的序号 */
  card->number = idx;
#ifdef MODULE
  WARN_ON(!module);
  /* (5). 赋值module */
  card->module = module;
#endif
    /* 2、初始化结构体和变量 */
  INIT_LIST_HEAD(&card->devices);
  init_rwsem(&card->controls_rwsem);
  rwlock_init(&card->ctl_files_rwlock);
  INIT_LIST_HEAD(&card->controls);
  INIT_LIST_HEAD(&card->ctl_files);
  spin_lock_init(&card->files_lock);
  INIT_LIST_HEAD(&card->files_list);
  mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM
  init_waitqueue_head(&card->power_sleep);
  init_waitqueue_head(&card->power_ref_sleep);
  atomic_set(&card->power_ref, 0);
#endif
  init_waitqueue_head(&card->remove_sleep);
  card->sync_irq = -1;
    /* 设置设备文件节点的名字 */
  device_initialize(&card->card_dev);
  card->card_dev.parent = parent;
  card->card_dev.class = sound_class;
  card->card_dev.release = release_card_device;
  card->card_dev.groups = card->dev_groups;
  card->dev_groups[0] = &card_dev_attr_group;
  err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
  if (err < 0)
    goto __error;
  snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
     dev_driver_string(card->dev), dev_name(&card->card_dev));
  /* the control interface cannot be accessed from the user space until */
  /* snd_cards_bitmask and snd_cards are set with snd_card_register */
    /* 创建一个control设备 */
  err = snd_ctl_create(card);
  if (err < 0) {
    dev_err(parent, "unable to register control minors\n");
    goto __error;
  }
    /* 生成声卡的proc文件 */
  err = snd_info_card_create(card);
  if (err < 0) {
    dev_err(parent, "unable to create card info\n");
    goto __error_ctl;
  }
#ifdef CONFIG_SND_DEBUG
  sprintf(name, "card%d", idx);
  card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
#endif
  return 0;
      __error_ctl:
  snd_device_free_all(card);
      __error:
  put_device(&card->card_dev);
    return err;
}

这函数会执行如下操作:

1、分配snd_card和private_data的空间。

2、初始化结构体、变量、创建control设备、生成声卡的proc文件等全部都在snd_card_init函数中完成。

3、获取private_data的地址等。

4、sound_class会在init_soundcore中做初始化操作。

static int __init init_soundcore(void)
{
  int rc;
  rc = init_oss_soundcore();
  if (rc)
    return rc;
  sound_class = class_create(THIS_MODULE, "sound");
  if (IS_ERR(sound_class)) {
    cleanup_oss_soundcore();
    return PTR_ERR(sound_class);
  }
  sound_class->devnode = sound_devnode;
  return 0;
}

5、创建card的control设备。根据注释control接口在snd_card_register之后,用户空间才可以访问。

6、调用snd_info_card_create函数在proc下创建card0目录,最終会根据entry的mode,创建目录。

/*
 * create a card proc file
 * called from init.c
 */
int snd_info_card_create(struct snd_card *card)
{
  char str[8];
  struct snd_info_entry *entry;
  if (snd_BUG_ON(!card))
    return -ENXIO;
  sprintf(str, "card%i", card->number);
  entry = create_subdir(card->module, str);
  if (!entry)
    return -ENOMEM;
  card->proc_root = entry;
  return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}

4、snd_card_register函数详解

       用来注册声卡,主要完成了如下操作:

               1、创建声卡的设备节点。

               2、注册所有的逻辑设备。

               3、添加当前的声卡到声卡数组。

               4、注册声卡的proc文件

/**
 *  snd_card_register - register the soundcard
 *  @card: soundcard structure
 *
 *  This function registers all the devices assigned to the soundcard.
 *  Until calling this, the ALSA control interface is blocked from the
 *  external accesses.  Thus, you should call this function at the end
 *  of the initialization of the card.
 *
 *  Return: Zero otherwise a negative error code if the registration failed.
 */
int snd_card_register(struct snd_card *card)
{
  int err;
    /* 合法性判断,如果此处card不存在,panic。 */
  if (snd_BUG_ON(!card))
    return -EINVAL;
    /* 1、根据card的registered判断是否已经注册,如果注册继续。否则调用device_add添加设备,设置registered标志。创建声卡的sysfs设备节点。其中card->card_dev在创建声卡结构体的时候被赋值。
       card->card_dev.class = sound_class;
       sound_class在sound模块被加载的时候创建
       设备节点:/dev/snd/cartd%i
    */
  if (!card->registered) {
    err = device_add(&card->card_dev);
    if (err < 0)
      return err;
    card->registered = true;
  } else {
    if (card->managed)
      devm_remove_action(card->dev, trigger_card_free, card);
  }
  if (card->managed) {
    err = devm_add_action(card->dev, trigger_card_free, card);
    if (err < 0)
      return err;
  }
    /* 2、调用snd_device_register_all注册所有card的设备,包括pcm, control等 */
  err = snd_device_register_all(card);
  if (err < 0)
    return err;
  mutex_lock(&snd_card_mutex);
    /* 3、添加当前的声卡到声卡数组 */
  if (snd_cards[card->number]) {
    /* already registered */
    mutex_unlock(&snd_card_mutex);
    return snd_info_card_register(card); /* register pending info */
  }
  if (*card->id) {
    /* make a unique id name from the given string */
    char tmpid[sizeof(card->id)];
    memcpy(tmpid, card->id, sizeof(card->id));
    snd_card_set_id_no_lock(card, tmpid, tmpid);
  } else {
    /* create an id from either shortname or longname */
    const char *src;
    src = *card->shortname ? card->shortname : card->longname;
    snd_card_set_id_no_lock(card, src,
          retrieve_id_from_card_name(src));
  }
  snd_cards[card->number] = card;
  mutex_unlock(&snd_card_mutex);
    /* 4、注册声卡的proc文件 */
  err = snd_info_card_register(card);
  if (err < 0)
    return err;
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
  if (snd_mixer_oss_notify_callback)
    snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
  return 0;
}
/*
 * register all the devices on the card.
 * called from init.c
 */
int snd_device_register_all(struct snd_card *card)
{
  struct snd_device *dev;
  int err;
  if (snd_BUG_ON(!card))
    return -ENXIO;
    /* 遍历注册所有的snd_device,调用__snd_device_register函数完成注册 */
  list_for_each_entry(dev, &card->devices, list) {
    err = __snd_device_register(dev);
    if (err < 0)
      return err;
  }
  return 0;
}
static int __snd_device_register(struct snd_device *dev)
{
  if (dev->state == SNDRV_DEV_BUILD) {
    if (dev->ops->dev_register) {
      int err = dev->ops->dev_register(dev);
      if (err < 0)
        return err;
    }
    dev->state = SNDRV_DEV_REGISTERED;
  }
  return 0;
}

此函数最终会调用各个devices的snd_device_ops中的dev_register函数。

       声卡注册完成之后,声卡的软件逻辑结果如下:

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
2月前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。
|
4月前
|
监控 安全 Java
linux服务器上启动framework应用程序流程
【10月更文挑战第17天】在Linux服务器上启动Framework应用程序需经过准备工作、部署、启动、监控及访问五个步骤。首先确保服务器满足系统要求并安装依赖项;接着上传应用文件,编译构建,配置参数;然后通过脚本、命令行或系统服务启动应用;启动后检查日志,监控性能;最后确认访问地址,验证应用运行状态。具体操作应参照应用文档。
|
4月前
|
监控 Java Linux
linux服务器上启动framework应用程序流程
【10月更文挑战第18天】在 Linux 服务器上启动框架应用程序的流程包括:准备工作(确保访问权限、上传部署文件、了解启动要求)、检查依赖项、配置环境变量、切换到应用程序目录、启动应用程序、监控启动过程以及验证应用程序是否正常运行。具体步骤可能因应用程序类型和框架而异。
|
6月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
71 6
|
6月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
80 5
|
6月前
|
Ubuntu NoSQL Linux
Linux内核和驱动
Linux内核和驱动
54 2
|
5月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
6月前
|
网络协议 Linux 网络安全
在Linux中,我们都知道FTP协议有两种工作模式,它们的大概的⼀个工作流程是怎样的?
在Linux中,我们都知道FTP协议有两种工作模式,它们的大概的⼀个工作流程是怎样的?
|
6月前
|
缓存 网络协议 Linux
在Linux中,当用户在浏览器当中输入⼀个网站,计算机对dns解释经过那些流程?
在Linux中,当用户在浏览器当中输入⼀个网站,计算机对dns解释经过那些流程?
|
6月前
|
Linux
【linux】【驱动】<specifier>-map-pass-thru讲解
【linux】【驱动】<specifier>-map-pass-thru讲解
35 0