mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
6417f03132
MODULE_SUPPORTED_DEVICE was added in pre-git era and never was implemented. We can safely remove it, because the kernel has grown to have many more reliable mechanisms to determine if device is supported or not. Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
277 lines
6.7 KiB
C
277 lines
6.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Linux driver for M2Tech hiFace compatible devices
|
|
*
|
|
* Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
|
|
*
|
|
* Authors: Michael Trimarchi <michael@amarulasolutions.com>
|
|
* Antonio Ospite <ao2@amarulasolutions.com>
|
|
*
|
|
* The driver is based on the work done in TerraTec DMX 6Fire USB
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <sound/initval.h>
|
|
|
|
#include "chip.h"
|
|
#include "pcm.h"
|
|
|
|
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
|
|
MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
|
|
MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
|
|
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
|
|
|
|
#define DRIVER_NAME "snd-usb-hiface"
|
|
#define CARD_NAME "hiFace"
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
|
|
module_param_array(id, charp, NULL, 0444);
|
|
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
|
|
|
|
static DEFINE_MUTEX(register_mutex);
|
|
|
|
struct hiface_vendor_quirk {
|
|
const char *device_name;
|
|
u8 extra_freq;
|
|
};
|
|
|
|
static int hiface_chip_create(struct usb_interface *intf,
|
|
struct usb_device *device, int idx,
|
|
const struct hiface_vendor_quirk *quirk,
|
|
struct hiface_chip **rchip)
|
|
{
|
|
struct snd_card *card = NULL;
|
|
struct hiface_chip *chip;
|
|
int ret;
|
|
int len;
|
|
|
|
*rchip = NULL;
|
|
|
|
/* if we are here, card can be registered in alsa. */
|
|
ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
|
|
sizeof(*chip), &card);
|
|
if (ret < 0) {
|
|
dev_err(&device->dev, "cannot create alsa card.\n");
|
|
return ret;
|
|
}
|
|
|
|
strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
|
|
|
|
if (quirk && quirk->device_name)
|
|
strscpy(card->shortname, quirk->device_name, sizeof(card->shortname));
|
|
else
|
|
strscpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
|
|
|
|
strlcat(card->longname, card->shortname, sizeof(card->longname));
|
|
len = strlcat(card->longname, " at ", sizeof(card->longname));
|
|
if (len < sizeof(card->longname))
|
|
usb_make_path(device, card->longname + len,
|
|
sizeof(card->longname) - len);
|
|
|
|
chip = card->private_data;
|
|
chip->dev = device;
|
|
chip->card = card;
|
|
|
|
*rchip = chip;
|
|
return 0;
|
|
}
|
|
|
|
static int hiface_chip_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *usb_id)
|
|
{
|
|
const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
|
|
int ret;
|
|
int i;
|
|
struct hiface_chip *chip;
|
|
struct usb_device *device = interface_to_usbdev(intf);
|
|
|
|
ret = usb_set_interface(device, 0, 0);
|
|
if (ret != 0) {
|
|
dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* check whether the card is already registered */
|
|
chip = NULL;
|
|
mutex_lock(®ister_mutex);
|
|
|
|
for (i = 0; i < SNDRV_CARDS; i++)
|
|
if (enable[i])
|
|
break;
|
|
|
|
if (i >= SNDRV_CARDS) {
|
|
dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
|
|
ret = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
ret = hiface_chip_create(intf, device, i, quirk, &chip);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
|
|
if (ret < 0)
|
|
goto err_chip_destroy;
|
|
|
|
ret = snd_card_register(chip->card);
|
|
if (ret < 0) {
|
|
dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
|
|
goto err_chip_destroy;
|
|
}
|
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
usb_set_intfdata(intf, chip);
|
|
return 0;
|
|
|
|
err_chip_destroy:
|
|
snd_card_free(chip->card);
|
|
err:
|
|
mutex_unlock(®ister_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static void hiface_chip_disconnect(struct usb_interface *intf)
|
|
{
|
|
struct hiface_chip *chip;
|
|
struct snd_card *card;
|
|
|
|
chip = usb_get_intfdata(intf);
|
|
if (!chip)
|
|
return;
|
|
|
|
card = chip->card;
|
|
|
|
/* Make sure that the userspace cannot create new request */
|
|
snd_card_disconnect(card);
|
|
|
|
hiface_pcm_abort(chip);
|
|
snd_card_free_when_closed(card);
|
|
}
|
|
|
|
static const struct usb_device_id device_table[] = {
|
|
{
|
|
USB_DEVICE(0x04b4, 0x0384),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Young",
|
|
.extra_freq = 1,
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x930b),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "hiFace",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931b),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "North Star",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "W4S Young",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931d),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Corrson",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931e),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "AUDIA",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931f),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "SL Audio",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x9320),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Empirical",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x9321),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Rockna",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9001),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Pathos",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9002),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Metronome",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9006),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "CAD",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9008),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Audio Esclusive",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Rotel",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x932c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Eeaudio",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x245f, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "CHORD",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x25c6, 0x9002),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Vitus",
|
|
}
|
|
},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(usb, device_table);
|
|
|
|
static struct usb_driver hiface_usb_driver = {
|
|
.name = DRIVER_NAME,
|
|
.probe = hiface_chip_probe,
|
|
.disconnect = hiface_chip_disconnect,
|
|
.id_table = device_table,
|
|
};
|
|
|
|
module_usb_driver(hiface_usb_driver);
|