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(parent: &intf->dev, idx: index[idx], xid: id[idx], THIS_MODULE,
60 extra_size: sizeof(*chip), card_ret: &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(p: card->longname, q: card->shortname, avail: sizeof(card->longname));
74 len = strlcat(p: card->longname, q: " at ", avail: sizeof(card->longname));
75 if (len < sizeof(card->longname))
76 usb_make_path(dev: device, buf: card->longname + len,
77 size: 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(dev: device, ifnum: 0, alternate: 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 guard(mutex)(T: &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 return -ENODEV;
113 }
114
115 ret = hiface_chip_create(intf, device, idx: i, quirk, rchip: &chip);
116 if (ret < 0)
117 return ret;
118
119 ret = hiface_pcm_init(chip, extra_freq: quirk ? quirk->extra_freq : 0);
120 if (ret < 0)
121 goto err_chip_destroy;
122
123 ret = snd_card_register(card: chip->card);
124 if (ret < 0) {
125 dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
126 goto err_chip_destroy;
127 }
128
129 usb_set_intfdata(intf, data: chip);
130 return 0;
131
132err_chip_destroy:
133 snd_card_free(card: chip->card);
134 return ret;
135}
136
137static void hiface_chip_disconnect(struct usb_interface *intf)
138{
139 struct hiface_chip *chip;
140 struct snd_card *card;
141
142 chip = usb_get_intfdata(intf);
143 if (!chip)
144 return;
145
146 card = chip->card;
147
148 /* Make sure that the userspace cannot create new request */
149 snd_card_disconnect(card);
150
151 hiface_pcm_abort(chip);
152 snd_card_free_when_closed(card);
153}
154
155static const struct usb_device_id device_table[] = {
156 {
157 USB_DEVICE(0x04b4, 0x0384),
158 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
159 .device_name = "Young",
160 .extra_freq = 1,
161 }
162 },
163 {
164 USB_DEVICE(0x04b4, 0x930b),
165 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
166 .device_name = "hiFace",
167 }
168 },
169 {
170 USB_DEVICE(0x04b4, 0x931b),
171 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
172 .device_name = "North Star",
173 }
174 },
175 {
176 USB_DEVICE(0x04b4, 0x931c),
177 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
178 .device_name = "W4S Young",
179 }
180 },
181 {
182 USB_DEVICE(0x04b4, 0x931d),
183 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
184 .device_name = "Corrson",
185 }
186 },
187 {
188 USB_DEVICE(0x04b4, 0x931e),
189 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
190 .device_name = "AUDIA",
191 }
192 },
193 {
194 USB_DEVICE(0x04b4, 0x931f),
195 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
196 .device_name = "SL Audio",
197 }
198 },
199 {
200 USB_DEVICE(0x04b4, 0x9320),
201 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
202 .device_name = "Empirical",
203 }
204 },
205 {
206 USB_DEVICE(0x04b4, 0x9321),
207 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
208 .device_name = "Rockna",
209 }
210 },
211 {
212 USB_DEVICE(0x249c, 0x9001),
213 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
214 .device_name = "Pathos",
215 }
216 },
217 {
218 USB_DEVICE(0x249c, 0x9002),
219 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
220 .device_name = "Metronome",
221 }
222 },
223 {
224 USB_DEVICE(0x249c, 0x9006),
225 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
226 .device_name = "CAD",
227 }
228 },
229 {
230 USB_DEVICE(0x249c, 0x9008),
231 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
232 .device_name = "Audio Esclusive",
233 }
234 },
235 {
236 USB_DEVICE(0x249c, 0x931c),
237 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
238 .device_name = "Rotel",
239 }
240 },
241 {
242 USB_DEVICE(0x249c, 0x932c),
243 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
244 .device_name = "Eeaudio",
245 }
246 },
247 {
248 USB_DEVICE(0x245f, 0x931c),
249 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
250 .device_name = "CHORD",
251 }
252 },
253 {
254 USB_DEVICE(0x25c6, 0x9002),
255 .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
256 .device_name = "Vitus",
257 }
258 },
259 {}
260};
261
262MODULE_DEVICE_TABLE(usb, device_table);
263
264static struct usb_driver hiface_usb_driver = {
265 .name = DRIVER_NAME,
266 .probe = hiface_chip_probe,
267 .disconnect = hiface_chip_disconnect,
268 .id_table = device_table,
269};
270
271module_usb_driver(hiface_usb_driver);
272

source code of linux/sound/usb/hiface/chip.c