mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
6.7 KiB
276 lines
6.7 KiB
// 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 <[email protected]> |
|
* Antonio Ospite <[email protected]> |
|
* |
|
* 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 <[email protected]>"); |
|
MODULE_AUTHOR("Antonio Ospite <[email protected]>"); |
|
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);
|
|
|