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 | |
20 | MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>" ); |
21 | MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>" ); |
22 | MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver" ); |
23 | MODULE_LICENSE("GPL v2" ); |
24 | |
25 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ |
26 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ |
27 | static 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 | |
32 | module_param_array(index, int, NULL, 0444); |
33 | MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard." ); |
34 | module_param_array(id, charp, NULL, 0444); |
35 | MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard." ); |
36 | module_param_array(enable, bool, NULL, 0444); |
37 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard." ); |
38 | |
39 | static DEFINE_MUTEX(register_mutex); |
40 | |
41 | struct hiface_vendor_quirk { |
42 | const char *device_name; |
43 | u8 ; |
44 | }; |
45 | |
46 | static 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 | |
87 | static 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 | mutex_lock(®ister_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, idx: i, quirk, rchip: &chip); |
117 | if (ret < 0) |
118 | goto err; |
119 | |
120 | ret = hiface_pcm_init(chip, extra_freq: quirk ? quirk->extra_freq : 0); |
121 | if (ret < 0) |
122 | goto err_chip_destroy; |
123 | |
124 | ret = snd_card_register(card: 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(lock: ®ister_mutex); |
131 | |
132 | usb_set_intfdata(intf, data: chip); |
133 | return 0; |
134 | |
135 | err_chip_destroy: |
136 | snd_card_free(card: chip->card); |
137 | err: |
138 | mutex_unlock(lock: ®ister_mutex); |
139 | return ret; |
140 | } |
141 | |
142 | static 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 | |
160 | static 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 | |
267 | MODULE_DEVICE_TABLE(usb, device_table); |
268 | |
269 | static 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 | |
276 | module_usb_driver(hiface_usb_driver); |
277 | |