Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Linux driver for M2Tech hiFace compatible devices
  4 *
  5 * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
  6 *
  7 * Authors:  Michael Trimarchi <michael@amarulasolutions.com>
  8 *           Antonio Ospite <ao2@amarulasolutions.com>
  9 *
 10 * The driver is based on the work done in TerraTec DMX 6Fire USB
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/slab.h>
 15#include <sound/initval.h>
 16
 17#include "chip.h"
 18#include "pcm.h"
 19
 20MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
 21MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
 22MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
 23MODULE_LICENSE("GPL v2");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 24
 25static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 26static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
 27static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
 28
 29#define DRIVER_NAME "snd-usb-hiface"
 30#define CARD_NAME "hiFace"
 31
 32module_param_array(index, int, NULL, 0444);
 33MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
 34module_param_array(id, charp, NULL, 0444);
 35MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
 36module_param_array(enable, bool, NULL, 0444);
 37MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
 38
 39static DEFINE_MUTEX(register_mutex);
 40
 41struct hiface_vendor_quirk {
 42	const char *device_name;
 43	u8 extra_freq;
 44};
 45
 46static int hiface_chip_create(struct usb_interface *intf,
 47			      struct usb_device *device, int idx,
 48			      const struct hiface_vendor_quirk *quirk,
 49			      struct hiface_chip **rchip)
 50{
 51	struct snd_card *card = NULL;
 52	struct hiface_chip *chip;
 53	int ret;
 54	int len;
 55
 56	*rchip = NULL;
 57
 58	/* if we are here, card can be registered in alsa. */
 59	ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
 60			   sizeof(*chip), &card);
 61	if (ret < 0) {
 62		dev_err(&device->dev, "cannot create alsa card.\n");
 63		return ret;
 64	}
 65
 66	strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
 67
 68	if (quirk && quirk->device_name)
 69		strscpy(card->shortname, quirk->device_name, sizeof(card->shortname));
 70	else
 71		strscpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
 72
 73	strlcat(card->longname, card->shortname, sizeof(card->longname));
 74	len = strlcat(card->longname, " at ", sizeof(card->longname));
 75	if (len < sizeof(card->longname))
 76		usb_make_path(device, card->longname + len,
 77			      sizeof(card->longname) - len);
 78
 79	chip = card->private_data;
 80	chip->dev = device;
 81	chip->card = card;
 82
 83	*rchip = chip;
 84	return 0;
 85}
 86
 87static int hiface_chip_probe(struct usb_interface *intf,
 88			     const struct usb_device_id *usb_id)
 89{
 90	const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
 91	int ret;
 92	int i;
 93	struct hiface_chip *chip;
 94	struct usb_device *device = interface_to_usbdev(intf);
 95
 96	ret = usb_set_interface(device, 0, 0);
 97	if (ret != 0) {
 98		dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
 99		return -EIO;
100	}
101
102	/* check whether the card is already registered */
103	chip = NULL;
104	mutex_lock(&register_mutex);
105
106	for (i = 0; i < SNDRV_CARDS; i++)
107		if (enable[i])
108			break;
109
110	if (i >= SNDRV_CARDS) {
111		dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
112		ret = -ENODEV;
113		goto err;
114	}
115
116	ret = hiface_chip_create(intf, device, i, quirk, &chip);
117	if (ret < 0)
118		goto err;
119
120	ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
121	if (ret < 0)
122		goto err_chip_destroy;
123
124	ret = snd_card_register(chip->card);
125	if (ret < 0) {
126		dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
127		goto err_chip_destroy;
128	}
129
130	mutex_unlock(&register_mutex);
131
132	usb_set_intfdata(intf, chip);
133	return 0;
134
135err_chip_destroy:
136	snd_card_free(chip->card);
137err:
138	mutex_unlock(&register_mutex);
139	return ret;
140}
141
142static void hiface_chip_disconnect(struct usb_interface *intf)
143{
144	struct hiface_chip *chip;
145	struct snd_card *card;
146
147	chip = usb_get_intfdata(intf);
148	if (!chip)
149		return;
150
151	card = chip->card;
152
153	/* Make sure that the userspace cannot create new request */
154	snd_card_disconnect(card);
155
156	hiface_pcm_abort(chip);
157	snd_card_free_when_closed(card);
158}
159
160static const struct usb_device_id device_table[] = {
161	{
162		USB_DEVICE(0x04b4, 0x0384),
163		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
164			.device_name = "Young",
165			.extra_freq = 1,
166		}
167	},
168	{
169		USB_DEVICE(0x04b4, 0x930b),
170		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
171			.device_name = "hiFace",
172		}
173	},
174	{
175		USB_DEVICE(0x04b4, 0x931b),
176		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
177			.device_name = "North Star",
178		}
179	},
180	{
181		USB_DEVICE(0x04b4, 0x931c),
182		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
183			.device_name = "W4S Young",
184		}
185	},
186	{
187		USB_DEVICE(0x04b4, 0x931d),
188		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
189			.device_name = "Corrson",
190		}
191	},
192	{
193		USB_DEVICE(0x04b4, 0x931e),
194		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
195			.device_name = "AUDIA",
196		}
197	},
198	{
199		USB_DEVICE(0x04b4, 0x931f),
200		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
201			.device_name = "SL Audio",
202		}
203	},
204	{
205		USB_DEVICE(0x04b4, 0x9320),
206		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
207			.device_name = "Empirical",
208		}
209	},
210	{
211		USB_DEVICE(0x04b4, 0x9321),
212		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
213			.device_name = "Rockna",
214		}
215	},
216	{
217		USB_DEVICE(0x249c, 0x9001),
218		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
219			.device_name = "Pathos",
220		}
221	},
222	{
223		USB_DEVICE(0x249c, 0x9002),
224		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
225			.device_name = "Metronome",
226		}
227	},
228	{
229		USB_DEVICE(0x249c, 0x9006),
230		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
231			.device_name = "CAD",
232		}
233	},
234	{
235		USB_DEVICE(0x249c, 0x9008),
236		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
237			.device_name = "Audio Esclusive",
238		}
239	},
240	{
241		USB_DEVICE(0x249c, 0x931c),
242		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
243			.device_name = "Rotel",
244		}
245	},
246	{
247		USB_DEVICE(0x249c, 0x932c),
248		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
249			.device_name = "Eeaudio",
250		}
251	},
252	{
253		USB_DEVICE(0x245f, 0x931c),
254		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
255			.device_name = "CHORD",
256		}
257	},
258	{
259		USB_DEVICE(0x25c6, 0x9002),
260		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
261			.device_name = "Vitus",
262		}
263	},
264	{}
265};
266
267MODULE_DEVICE_TABLE(usb, device_table);
268
269static struct usb_driver hiface_usb_driver = {
270	.name = DRIVER_NAME,
271	.probe = hiface_chip_probe,
272	.disconnect = hiface_chip_disconnect,
273	.id_table = device_table,
274};
275
276module_usb_driver(hiface_usb_driver);
v5.4
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Linux driver for M2Tech hiFace compatible devices
  4 *
  5 * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
  6 *
  7 * Authors:  Michael Trimarchi <michael@amarulasolutions.com>
  8 *           Antonio Ospite <ao2@amarulasolutions.com>
  9 *
 10 * The driver is based on the work done in TerraTec DMX 6Fire USB
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/slab.h>
 15#include <sound/initval.h>
 16
 17#include "chip.h"
 18#include "pcm.h"
 19
 20MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
 21MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
 22MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
 23MODULE_LICENSE("GPL v2");
 24MODULE_SUPPORTED_DEVICE("{{M2Tech,Young},"
 25			 "{M2Tech,hiFace},"
 26			 "{M2Tech,North Star},"
 27			 "{M2Tech,W4S Young},"
 28			 "{M2Tech,Corrson},"
 29			 "{M2Tech,AUDIA},"
 30			 "{M2Tech,SL Audio},"
 31			 "{M2Tech,Empirical},"
 32			 "{M2Tech,Rockna},"
 33			 "{M2Tech,Pathos},"
 34			 "{M2Tech,Metronome},"
 35			 "{M2Tech,CAD},"
 36			 "{M2Tech,Audio Esclusive},"
 37			 "{M2Tech,Rotel},"
 38			 "{M2Tech,Eeaudio},"
 39			 "{The Chord Company,CHORD},"
 40			 "{AVA Group A/S,Vitus}}");
 41
 42static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 43static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
 44static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
 45
 46#define DRIVER_NAME "snd-usb-hiface"
 47#define CARD_NAME "hiFace"
 48
 49module_param_array(index, int, NULL, 0444);
 50MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
 51module_param_array(id, charp, NULL, 0444);
 52MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
 53module_param_array(enable, bool, NULL, 0444);
 54MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
 55
 56static DEFINE_MUTEX(register_mutex);
 57
 58struct hiface_vendor_quirk {
 59	const char *device_name;
 60	u8 extra_freq;
 61};
 62
 63static int hiface_chip_create(struct usb_interface *intf,
 64			      struct usb_device *device, int idx,
 65			      const struct hiface_vendor_quirk *quirk,
 66			      struct hiface_chip **rchip)
 67{
 68	struct snd_card *card = NULL;
 69	struct hiface_chip *chip;
 70	int ret;
 71	int len;
 72
 73	*rchip = NULL;
 74
 75	/* if we are here, card can be registered in alsa. */
 76	ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
 77			   sizeof(*chip), &card);
 78	if (ret < 0) {
 79		dev_err(&device->dev, "cannot create alsa card.\n");
 80		return ret;
 81	}
 82
 83	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
 84
 85	if (quirk && quirk->device_name)
 86		strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname));
 87	else
 88		strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
 89
 90	strlcat(card->longname, card->shortname, sizeof(card->longname));
 91	len = strlcat(card->longname, " at ", sizeof(card->longname));
 92	if (len < sizeof(card->longname))
 93		usb_make_path(device, card->longname + len,
 94			      sizeof(card->longname) - len);
 95
 96	chip = card->private_data;
 97	chip->dev = device;
 98	chip->card = card;
 99
100	*rchip = chip;
101	return 0;
102}
103
104static int hiface_chip_probe(struct usb_interface *intf,
105			     const struct usb_device_id *usb_id)
106{
107	const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
108	int ret;
109	int i;
110	struct hiface_chip *chip;
111	struct usb_device *device = interface_to_usbdev(intf);
112
113	ret = usb_set_interface(device, 0, 0);
114	if (ret != 0) {
115		dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
116		return -EIO;
117	}
118
119	/* check whether the card is already registered */
120	chip = NULL;
121	mutex_lock(&register_mutex);
122
123	for (i = 0; i < SNDRV_CARDS; i++)
124		if (enable[i])
125			break;
126
127	if (i >= SNDRV_CARDS) {
128		dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
129		ret = -ENODEV;
130		goto err;
131	}
132
133	ret = hiface_chip_create(intf, device, i, quirk, &chip);
134	if (ret < 0)
135		goto err;
136
137	ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
138	if (ret < 0)
139		goto err_chip_destroy;
140
141	ret = snd_card_register(chip->card);
142	if (ret < 0) {
143		dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
144		goto err_chip_destroy;
145	}
146
147	mutex_unlock(&register_mutex);
148
149	usb_set_intfdata(intf, chip);
150	return 0;
151
152err_chip_destroy:
153	snd_card_free(chip->card);
154err:
155	mutex_unlock(&register_mutex);
156	return ret;
157}
158
159static void hiface_chip_disconnect(struct usb_interface *intf)
160{
161	struct hiface_chip *chip;
162	struct snd_card *card;
163
164	chip = usb_get_intfdata(intf);
165	if (!chip)
166		return;
167
168	card = chip->card;
169
170	/* Make sure that the userspace cannot create new request */
171	snd_card_disconnect(card);
172
173	hiface_pcm_abort(chip);
174	snd_card_free_when_closed(card);
175}
176
177static const struct usb_device_id device_table[] = {
178	{
179		USB_DEVICE(0x04b4, 0x0384),
180		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
181			.device_name = "Young",
182			.extra_freq = 1,
183		}
184	},
185	{
186		USB_DEVICE(0x04b4, 0x930b),
187		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
188			.device_name = "hiFace",
189		}
190	},
191	{
192		USB_DEVICE(0x04b4, 0x931b),
193		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
194			.device_name = "North Star",
195		}
196	},
197	{
198		USB_DEVICE(0x04b4, 0x931c),
199		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
200			.device_name = "W4S Young",
201		}
202	},
203	{
204		USB_DEVICE(0x04b4, 0x931d),
205		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
206			.device_name = "Corrson",
207		}
208	},
209	{
210		USB_DEVICE(0x04b4, 0x931e),
211		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
212			.device_name = "AUDIA",
213		}
214	},
215	{
216		USB_DEVICE(0x04b4, 0x931f),
217		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
218			.device_name = "SL Audio",
219		}
220	},
221	{
222		USB_DEVICE(0x04b4, 0x9320),
223		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
224			.device_name = "Empirical",
225		}
226	},
227	{
228		USB_DEVICE(0x04b4, 0x9321),
229		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
230			.device_name = "Rockna",
231		}
232	},
233	{
234		USB_DEVICE(0x249c, 0x9001),
235		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
236			.device_name = "Pathos",
237		}
238	},
239	{
240		USB_DEVICE(0x249c, 0x9002),
241		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
242			.device_name = "Metronome",
243		}
244	},
245	{
246		USB_DEVICE(0x249c, 0x9006),
247		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
248			.device_name = "CAD",
249		}
250	},
251	{
252		USB_DEVICE(0x249c, 0x9008),
253		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
254			.device_name = "Audio Esclusive",
255		}
256	},
257	{
258		USB_DEVICE(0x249c, 0x931c),
259		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
260			.device_name = "Rotel",
261		}
262	},
263	{
264		USB_DEVICE(0x249c, 0x932c),
265		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
266			.device_name = "Eeaudio",
267		}
268	},
269	{
270		USB_DEVICE(0x245f, 0x931c),
271		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
272			.device_name = "CHORD",
273		}
274	},
275	{
276		USB_DEVICE(0x25c6, 0x9002),
277		.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
278			.device_name = "Vitus",
279		}
280	},
281	{}
282};
283
284MODULE_DEVICE_TABLE(usb, device_table);
285
286static struct usb_driver hiface_usb_driver = {
287	.name = DRIVER_NAME,
288	.probe = hiface_chip_probe,
289	.disconnect = hiface_chip_disconnect,
290	.id_table = device_table,
291};
292
293module_usb_driver(hiface_usb_driver);