转载:alsa hwdep

参考:

简介:https://blog.csdn.net/azloong/article/details/6932272#

driver例子:https://elixir.bootlin.com/linux/v5.4.240/source/sound/pci/rme9652/hdsp.c#L4906

hwdep API:https://www.alsa-project.org/alsa-doc/alsa-lib/group___hw_dep.html

alsa core hwdep:https://elixir.bootlin.com/linux/v5.4.240/source/sound/core/hwdep.c#L351

alsa可以实现类似于ioctl的函数的,只不过它封装成一个设备模块SNDRV_DEV_HWDEP,代码sound\core\ hwdep.c。该模块实现了read/write/ioctl/llseek/poll/mmap等接口。hwdep是Hardware Dependant Interface的简称。
可以通过cat /proc/asound/devices 看到系统上已经注册的hw dep device.

Driver实现hw dep device的方法可参考:https://elixir.bootlin.com/linux/v5.4.240/source/sound/pci/rme9652/hdsp.c#L4906

与snd_pcm_new() 类似,call snd_hwdep_new在card上创建一个hw dep device

static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
{
    struct snd_hwdep *hw;
    int err;

    if ((err = snd_hwdep_new(card, "HDSP hwdep", 0, &hw)) < 0)
        return err;

    hdsp->hwdep = hw;
    hw->private_data = hdsp;
    strcpy(hw->name, "HDSP hwdep interface");

    hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
    hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl;

    return 0;
}

snd_hwdep_new()内alloc struct snd_hwdep后,call snd_device_new 创建struct snd_deviece, device name为hwCXDX, 指定其ops, 并将snd_device 插入card->device list中,后续在register card时会register card的device,调用snd_device->ops->dev_register()

/**
 * snd_hwdep_new - create a new hwdep instance
 * @card: the card instance
 * @id: the id string
 * @device: the device index (zero-based)
 * @rhwdep: the pointer to store the new hwdep instance
 *
 * Creates a new hwdep instance with the given index on the card.
 * The callbacks (hwdep->ops) must be set on the returned instance
 * after this call manually by the caller.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_hwdep_new(struct snd_card *card, char *id, int device,
          struct snd_hwdep **rhwdep)
{
    struct snd_hwdep *hwdep;
    int err;
    static struct snd_device_ops ops = {
        .dev_free = snd_hwdep_dev_free,
        .dev_register = snd_hwdep_dev_register,
        .dev_disconnect = snd_hwdep_dev_disconnect,
    };

    if (snd_BUG_ON(!card))
        return -ENXIO;
    if (rhwdep)
        *rhwdep = NULL;
    hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
    if (!hwdep)
        return -ENOMEM;

    init_waitqueue_head(&hwdep->open_wait);
    mutex_init(&hwdep->open_mutex);
    hwdep->card = card;
    hwdep->device = device;
    if (id)
        strlcpy(hwdep->id, id, sizeof(hwdep->id));

    snd_device_initialize(&hwdep->dev, card);
    hwdep->dev.release = release_hwdep_device;
    dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device);
#ifdef CONFIG_SND_OSSEMUL
    hwdep->oss_type = -1;
#endif

    err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
    if (err < 0) {
        put_device(&hwdep->dev);
        return err;
    }

    if (rhwdep)
        *rhwdep = hwdep;
    return 0;
}
EXPORT_SYMBOL(snd_hwdep_new);
/**
 * snd_device_new - create an ALSA device component
 * @card: the card instance
 * @type: the device type, SNDRV_DEV_XXX
 * @device_data: the data pointer of this device
 * @ops: the operator table
 *
 * Creates a new device component for the given data pointer.
 * The device will be assigned to the card and managed together
 * by the card.
 *
 * The data pointer plays a role as the identifier, too, so the
 * pointer address must be unique and unchanged.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_device_new(struct snd_card *card, enum snd_device_type type,
           void *device_data, struct snd_device_ops *ops)
{
    struct snd_device *dev;
    struct list_head *p;

    if (snd_BUG_ON(!card || !device_data || !ops))
        return -ENXIO;
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    INIT_LIST_HEAD(&dev->list);
    dev->card = card;
    dev->type = type;
    dev->state = SNDRV_DEV_BUILD;
    dev->device_data = device_data;
    dev->ops = ops;

    /* insert the entry in an incrementally sorted list */
    list_for_each_prev(p, &card->devices) {
        struct snd_device *pdev = list_entry(p, struct snd_device, list);
        if ((unsigned int)pdev->type <= (unsigned int)type)
            break;
    }

    list_add(&dev->list, p);
    return 0;
}
EXPORT_SYMBOL(snd_device_new);

 

在dev_register ops中,call snd_register_device为hw dep device创建struct snd_minor, 将其放到全局数组snd_minors[minor].

 

/**
 * snd_register_device - Register the ALSA device file for the card
 * @type: the device type, SNDRV_DEVICE_TYPE_XXX
 * @card: the card instance
 * @dev: the device index
 * @f_ops: the file operations
 * @private_data: user pointer for f_ops->open()
 * @device: the device to register
 *
 * Registers an ALSA device file for the given card.
 * The operators have to be set in reg parameter.
 *
 * Return: Zero if successful, or a negative error code on failure.
 */
int snd_register_device(int type, struct snd_card *card, int dev,
            const struct file_operations *f_ops,
            void *private_data, struct device *device)
{
    int minor;
    int err = 0;
    struct snd_minor *preg;

    if (snd_BUG_ON(!device))
        return -EINVAL;

    preg = kmalloc(sizeof *preg, GFP_KERNEL);
    if (preg == NULL)
        return -ENOMEM;
    preg->type = type;
    preg->card = card ? card->number : -1;
    preg->device = dev;
    preg->f_ops = f_ops;
    preg->private_data = private_data;
    preg->card_ptr = card;
    mutex_lock(&sound_mutex);
    minor = snd_find_free_minor(type, card, dev);
    if (minor < 0) {
        err = minor;
        goto error;
    }

    preg->dev = device;
    device->devt = MKDEV(major, minor);
    err = device_add(device);
    if (err < 0)
        goto error;

    snd_minors[minor] = preg;
 error:
    mutex_unlock(&sound_mutex);
    if (err < 0)
        kfree(preg);
    return err;
}
EXPORT_SYMBOL(snd_register_device);

snd_register_devic() 传入的f_ops为snd_hwdep_f_ops.

static const struct file_operations snd_hwdep_f_ops =
{
    .owner =     THIS_MODULE,
    .llseek =    snd_hwdep_llseek,
    .read =     snd_hwdep_read,
    .write =    snd_hwdep_write,
    .open =        snd_hwdep_open,
    .release =    snd_hwdep_release,
    .poll =        snd_hwdep_poll,
    .unlocked_ioctl =    snd_hwdep_ioctl,
    .compat_ioctl =    snd_hwdep_ioctl_compat,
    .mmap =        snd_hwdep_mmap,
};

当user space call snd_hwdep_open 时,就会call到snd_hwdep_f_ops.open即snd_hwdep_open

static int snd_hwdep_open(struct inode *inode, struct file * file)
{
    int major = imajor(inode);
    struct snd_hwdep *hw;
    int err;
    wait_queue_entry_t wait;

    if (major == snd_major) {
        hw = snd_lookup_minor_data(iminor(inode),
                       SNDRV_DEVICE_TYPE_HWDEP);
#ifdef CONFIG_SND_OSSEMUL
    } else if (major == SOUND_MAJOR) {
        hw = snd_lookup_oss_minor_data(iminor(inode),
                           SNDRV_OSS_DEVICE_TYPE_DMFM);
#endif
    } else
        return -ENXIO;
    if (hw == NULL)
        return -ENODEV;

    if (!try_module_get(hw->card->module)) {
        snd_card_unref(hw->card);
        return -EFAULT;
    }

    init_waitqueue_entry(&wait, current);
    add_wait_queue(&hw->open_wait, &wait);
    mutex_lock(&hw->open_mutex);
    while (1) {
        if (hw->exclusive && hw->used > 0) {
            err = -EBUSY;
            break;
        }
        if (!hw->ops.open) {
            err = 0;
            break;
        }
        err = hw->ops.open(hw, file);
        if (err >= 0)
            break;
        if (err == -EAGAIN) {
            if (file->f_flags & O_NONBLOCK) {
                err = -EBUSY;
                break;
            }
        } else
            break;
        set_current_state(TASK_INTERRUPTIBLE);
        mutex_unlock(&hw->open_mutex);
        schedule();
        mutex_lock(&hw->open_mutex);
        if (hw->card->shutdown) {
            err = -ENODEV;
            break;
        }
        if (signal_pending(current)) {
            err = -ERESTARTSYS;
            break;
        }
    }
    remove_wait_queue(&hw->open_wait, &wait);
    if (err >= 0) {
        err = snd_card_file_add(hw->card, file);
        if (err >= 0) {
            file->private_data = hw;
            hw->used++;
        } else {
            if (hw->ops.release)
                hw->ops.release(hw, file);
        }
    }
    mutex_unlock(&hw->open_mutex);
    if (err < 0)
        module_put(hw->card->module);
    snd_card_unref(hw->card);
    return err;
}

snd_hwdep_open会透过file获取major和mior找到找到注册在snd_minors[]中的struct snd_minor,从而找到snd_hwdep_new中创建的struct snd_hwdep, 调用struct snd_hwdep->ops.open, 并将struct snd_hwdep赋值给file->private_data, 后续snd_hwdep_f_ops内其他ops就可通过file直接拿到struct snd_hwdep.

user space call snd_hwdep_ioctl 时,也会call 到就会call到snd_hwdep_f_ops.ioctl即snd_hwdep_ioctl

 

static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
                unsigned long arg)
{
    struct snd_hwdep *hw = file->private_data;
    void __user *argp = (void __user *)arg;
    switch (cmd) {
    case SNDRV_HWDEP_IOCTL_PVERSION:
        return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
    case SNDRV_HWDEP_IOCTL_INFO:
        return snd_hwdep_info(hw, argp);
    case SNDRV_HWDEP_IOCTL_DSP_STATUS:
        return snd_hwdep_dsp_status(hw, argp);
    case SNDRV_HWDEP_IOCTL_DSP_LOAD:
        return snd_hwdep_dsp_load(hw, argp);
    }
    if (hw->ops.ioctl)
        return hw->ops.ioctl(hw, file, cmd, arg);
    return -ENOTTY;
}

snd_hwdep_ioctl也会call到driver实现的struct snd_hwdep->ops.ioctl

driver实现了hw dep device后,user space即可call alsa lib接口访问hw dep device

#include <fcntl.h>
#include <sys/ioctl.h>
#include <alsa/hwdep.h>
#include <alsa/error.h>
#include <stdio.h>
 
#define MY_SOC_IOCTL_SET_CALL_PATH   _IOWR('H', 0x10, int)
 
int main()
{
    const char *devicename = "hw:0,0";
    snd_hwdep_t *hwdep;
    int err;
    int enable = 1;
    
    if ((err = snd_hwdep_open(&hwdep, devicename, O_RDWR)) < 0) {
        printf("hwdep interface open error: %s \n", snd_strerror(err));
        return -1;
    }
    
    if ((err = snd_hwdep_ioctl(hwdep, MY_SOC_IOCTL_SET_CALL_PATH, &enable)) < 0) {
        printf("hwdep ioctl error: %s \n", snd_strerror(err));
    }
    
    snd_hwdep_close(hwdep);
    
    return 0;
}

 

posted @ 2023-04-05 23:03  fellow_jing  阅读(90)  评论(0编辑  收藏  举报