1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Line 6 Pod HD |
4 | * |
5 | * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> |
6 | * Copyright (C) 2015 Andrej Krutak <dev@andree.sk> |
7 | * Copyright (C) 2017 Hans P. Moller <hmoller@uc.cl> |
8 | */ |
9 | |
10 | #include <linux/usb.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/module.h> |
13 | #include <sound/core.h> |
14 | #include <sound/control.h> |
15 | #include <sound/pcm.h> |
16 | |
17 | #include "driver.h" |
18 | #include "pcm.h" |
19 | |
20 | #define PODHD_STARTUP_DELAY 500 |
21 | |
22 | enum { |
23 | LINE6_PODHD300, |
24 | LINE6_PODHD400, |
25 | LINE6_PODHD500, |
26 | LINE6_PODX3, |
27 | LINE6_PODX3LIVE, |
28 | LINE6_PODHD500X, |
29 | LINE6_PODHDDESKTOP |
30 | }; |
31 | |
32 | struct usb_line6_podhd { |
33 | /* Generic Line 6 USB data */ |
34 | struct usb_line6 line6; |
35 | |
36 | /* Serial number of device */ |
37 | u32 serial_number; |
38 | |
39 | /* Firmware version */ |
40 | int firmware_version; |
41 | |
42 | /* Monitor level */ |
43 | int monitor_level; |
44 | }; |
45 | |
46 | #define line6_to_podhd(x) container_of(x, struct usb_line6_podhd, line6) |
47 | |
48 | static const struct snd_ratden podhd_ratden = { |
49 | .num_min = 48000, |
50 | .num_max = 48000, |
51 | .num_step = 1, |
52 | .den = 1, |
53 | }; |
54 | |
55 | static struct line6_pcm_properties podhd_pcm_properties = { |
56 | .playback_hw = { |
57 | .info = (SNDRV_PCM_INFO_MMAP | |
58 | SNDRV_PCM_INFO_INTERLEAVED | |
59 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
60 | SNDRV_PCM_INFO_MMAP_VALID | |
61 | SNDRV_PCM_INFO_PAUSE | |
62 | SNDRV_PCM_INFO_SYNC_START), |
63 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, |
64 | .rates = SNDRV_PCM_RATE_48000, |
65 | .rate_min = 48000, |
66 | .rate_max = 48000, |
67 | .channels_min = 2, |
68 | .channels_max = 2, |
69 | .buffer_bytes_max = 60000, |
70 | .period_bytes_min = 64, |
71 | .period_bytes_max = 8192, |
72 | .periods_min = 1, |
73 | .periods_max = 1024}, |
74 | .capture_hw = { |
75 | .info = (SNDRV_PCM_INFO_MMAP | |
76 | SNDRV_PCM_INFO_INTERLEAVED | |
77 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
78 | SNDRV_PCM_INFO_MMAP_VALID | |
79 | SNDRV_PCM_INFO_SYNC_START), |
80 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, |
81 | .rates = SNDRV_PCM_RATE_48000, |
82 | .rate_min = 48000, |
83 | .rate_max = 48000, |
84 | .channels_min = 2, |
85 | .channels_max = 2, |
86 | .buffer_bytes_max = 60000, |
87 | .period_bytes_min = 64, |
88 | .period_bytes_max = 8192, |
89 | .periods_min = 1, |
90 | .periods_max = 1024}, |
91 | .rates = { |
92 | .nrats = 1, |
93 | .rats = &podhd_ratden}, |
94 | .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ |
95 | }; |
96 | |
97 | static struct line6_pcm_properties podx3_pcm_properties = { |
98 | .playback_hw = { |
99 | .info = (SNDRV_PCM_INFO_MMAP | |
100 | SNDRV_PCM_INFO_INTERLEAVED | |
101 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
102 | SNDRV_PCM_INFO_MMAP_VALID | |
103 | SNDRV_PCM_INFO_PAUSE | |
104 | SNDRV_PCM_INFO_SYNC_START), |
105 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, |
106 | .rates = SNDRV_PCM_RATE_48000, |
107 | .rate_min = 48000, |
108 | .rate_max = 48000, |
109 | .channels_min = 2, |
110 | .channels_max = 2, |
111 | .buffer_bytes_max = 60000, |
112 | .period_bytes_min = 64, |
113 | .period_bytes_max = 8192, |
114 | .periods_min = 1, |
115 | .periods_max = 1024}, |
116 | .capture_hw = { |
117 | .info = (SNDRV_PCM_INFO_MMAP | |
118 | SNDRV_PCM_INFO_INTERLEAVED | |
119 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
120 | SNDRV_PCM_INFO_MMAP_VALID | |
121 | SNDRV_PCM_INFO_SYNC_START), |
122 | .formats = SNDRV_PCM_FMTBIT_S24_3LE, |
123 | .rates = SNDRV_PCM_RATE_48000, |
124 | .rate_min = 48000, |
125 | .rate_max = 48000, |
126 | /* 1+2: Main signal (out), 3+4: Tone 1, |
127 | * 5+6: Tone 2, 7+8: raw |
128 | */ |
129 | .channels_min = 8, |
130 | .channels_max = 8, |
131 | .buffer_bytes_max = 60000, |
132 | .period_bytes_min = 64, |
133 | .period_bytes_max = 8192, |
134 | .periods_min = 1, |
135 | .periods_max = 1024}, |
136 | .rates = { |
137 | .nrats = 1, |
138 | .rats = &podhd_ratden}, |
139 | .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ |
140 | }; |
141 | static struct usb_driver podhd_driver; |
142 | |
143 | static ssize_t serial_number_show(struct device *dev, |
144 | struct device_attribute *attr, char *buf) |
145 | { |
146 | struct snd_card *card = dev_to_snd_card(dev); |
147 | struct usb_line6_podhd *pod = card->private_data; |
148 | |
149 | return sysfs_emit(buf, fmt: "%u\n" , pod->serial_number); |
150 | } |
151 | |
152 | static ssize_t firmware_version_show(struct device *dev, |
153 | struct device_attribute *attr, char *buf) |
154 | { |
155 | struct snd_card *card = dev_to_snd_card(dev); |
156 | struct usb_line6_podhd *pod = card->private_data; |
157 | |
158 | return sysfs_emit(buf, fmt: "%06x\n" , pod->firmware_version); |
159 | } |
160 | |
161 | static DEVICE_ATTR_RO(firmware_version); |
162 | static DEVICE_ATTR_RO(serial_number); |
163 | |
164 | static struct attribute *podhd_dev_attrs[] = { |
165 | &dev_attr_firmware_version.attr, |
166 | &dev_attr_serial_number.attr, |
167 | NULL |
168 | }; |
169 | |
170 | static const struct attribute_group podhd_dev_attr_group = { |
171 | .name = "podhd" , |
172 | .attrs = podhd_dev_attrs, |
173 | }; |
174 | |
175 | /* |
176 | * POD X3 startup procedure. |
177 | * |
178 | * May be compatible with other POD HD's, since it's also similar to the |
179 | * previous POD setup. In any case, it doesn't seem to be required for the |
180 | * audio nor bulk interfaces to work. |
181 | */ |
182 | |
183 | static int podhd_dev_start(struct usb_line6_podhd *pod) |
184 | { |
185 | int ret; |
186 | u8 init_bytes[8]; |
187 | int i; |
188 | struct usb_device *usbdev = pod->line6.usbdev; |
189 | |
190 | ret = usb_control_msg_send(dev: usbdev, endpoint: 0, |
191 | request: 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, |
192 | value: 0x11, index: 0, |
193 | NULL, size: 0, LINE6_TIMEOUT, GFP_KERNEL); |
194 | if (ret) { |
195 | dev_err(pod->line6.ifcdev, "read request failed (error %d)\n" , ret); |
196 | goto exit; |
197 | } |
198 | |
199 | /* NOTE: looks like some kind of ping message */ |
200 | ret = usb_control_msg_recv(dev: usbdev, endpoint: 0, request: 0x67, |
201 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
202 | value: 0x11, index: 0x0, |
203 | data: init_bytes, size: 3, LINE6_TIMEOUT, GFP_KERNEL); |
204 | if (ret) { |
205 | dev_err(pod->line6.ifcdev, |
206 | "receive length failed (error %d)\n" , ret); |
207 | goto exit; |
208 | } |
209 | |
210 | pod->firmware_version = |
211 | (init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0); |
212 | |
213 | for (i = 0; i <= 16; i++) { |
214 | ret = line6_read_data(line6: &pod->line6, address: 0xf000 + 0x08 * i, data: init_bytes, datalen: 8); |
215 | if (ret < 0) |
216 | goto exit; |
217 | } |
218 | |
219 | ret = usb_control_msg_send(dev: usbdev, endpoint: 0, |
220 | USB_REQ_SET_FEATURE, |
221 | USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT, |
222 | value: 1, index: 0, |
223 | NULL, size: 0, LINE6_TIMEOUT, GFP_KERNEL); |
224 | exit: |
225 | return ret; |
226 | } |
227 | |
228 | static void podhd_startup(struct usb_line6 *line6) |
229 | { |
230 | struct usb_line6_podhd *pod = line6_to_podhd(line6); |
231 | |
232 | podhd_dev_start(pod); |
233 | line6_read_serial_number(line6: &pod->line6, serial_number: &pod->serial_number); |
234 | if (snd_card_register(card: line6->card)) |
235 | dev_err(line6->ifcdev, "Failed to register POD HD card.\n" ); |
236 | } |
237 | |
238 | static void podhd_disconnect(struct usb_line6 *line6) |
239 | { |
240 | struct usb_line6_podhd *pod = line6_to_podhd(line6); |
241 | |
242 | if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) { |
243 | struct usb_interface *intf; |
244 | |
245 | intf = usb_ifnum_to_if(dev: line6->usbdev, |
246 | ifnum: pod->line6.properties->ctrl_if); |
247 | if (intf) |
248 | usb_driver_release_interface(driver: &podhd_driver, iface: intf); |
249 | } |
250 | } |
251 | |
252 | static const unsigned int float_zero_to_one_lookup[] = { |
253 | 0x00000000, 0x3c23d70a, 0x3ca3d70a, 0x3cf5c28f, 0x3d23d70a, 0x3d4ccccd, |
254 | 0x3d75c28f, 0x3d8f5c29, 0x3da3d70a, 0x3db851ec, 0x3dcccccd, 0x3de147ae, |
255 | 0x3df5c28f, 0x3e051eb8, 0x3e0f5c29, 0x3e19999a, 0x3e23d70a, 0x3e2e147b, |
256 | 0x3e3851ec, 0x3e428f5c, 0x3e4ccccd, 0x3e570a3d, 0x3e6147ae, 0x3e6b851f, |
257 | 0x3e75c28f, 0x3e800000, 0x3e851eb8, 0x3e8a3d71, 0x3e8f5c29, 0x3e947ae1, |
258 | 0x3e99999a, 0x3e9eb852, 0x3ea3d70a, 0x3ea8f5c3, 0x3eae147b, 0x3eb33333, |
259 | 0x3eb851ec, 0x3ebd70a4, 0x3ec28f5c, 0x3ec7ae14, 0x3ecccccd, 0x3ed1eb85, |
260 | 0x3ed70a3d, 0x3edc28f6, 0x3ee147ae, 0x3ee66666, 0x3eeb851f, 0x3ef0a3d7, |
261 | 0x3ef5c28f, 0x3efae148, 0x3f000000, 0x3f028f5c, 0x3f051eb8, 0x3f07ae14, |
262 | 0x3f0a3d71, 0x3f0ccccd, 0x3f0f5c29, 0x3f11eb85, 0x3f147ae1, 0x3f170a3d, |
263 | 0x3f19999a, 0x3f1c28f6, 0x3f1eb852, 0x3f2147ae, 0x3f23d70a, 0x3f266666, |
264 | 0x3f28f5c3, 0x3f2b851f, 0x3f2e147b, 0x3f30a3d7, 0x3f333333, 0x3f35c28f, |
265 | 0x3f3851ec, 0x3f3ae148, 0x3f3d70a4, 0x3f400000, 0x3f428f5c, 0x3f451eb8, |
266 | 0x3f47ae14, 0x3f4a3d71, 0x3f4ccccd, 0x3f4f5c29, 0x3f51eb85, 0x3f547ae1, |
267 | 0x3f570a3d, 0x3f59999a, 0x3f5c28f6, 0x3f5eb852, 0x3f6147ae, 0x3f63d70a, |
268 | 0x3f666666, 0x3f68f5c3, 0x3f6b851f, 0x3f6e147b, 0x3f70a3d7, 0x3f733333, |
269 | 0x3f75c28f, 0x3f7851ec, 0x3f7ae148, 0x3f7d70a4, 0x3f800000 |
270 | }; |
271 | |
272 | static void podhd_set_monitor_level(struct usb_line6_podhd *podhd, int value) |
273 | { |
274 | unsigned int fl; |
275 | static const unsigned char msg[16] = { |
276 | /* Chunk is 0xc bytes (without first word) */ |
277 | 0x0c, 0x00, |
278 | /* First chunk in the message */ |
279 | 0x01, 0x00, |
280 | /* Message size is 2 4-byte words */ |
281 | 0x02, 0x00, |
282 | /* Unknown */ |
283 | 0x04, 0x41, |
284 | /* Unknown */ |
285 | 0x04, 0x00, 0x13, 0x00, |
286 | /* Volume, LE float32, 0.0 - 1.0 */ |
287 | 0x00, 0x00, 0x00, 0x00 |
288 | }; |
289 | unsigned char *buf; |
290 | |
291 | buf = kmemdup(p: msg, size: sizeof(msg), GFP_KERNEL); |
292 | if (!buf) |
293 | return; |
294 | |
295 | if (value < 0) |
296 | value = 0; |
297 | |
298 | if (value >= ARRAY_SIZE(float_zero_to_one_lookup)) |
299 | value = ARRAY_SIZE(float_zero_to_one_lookup) - 1; |
300 | |
301 | fl = float_zero_to_one_lookup[value]; |
302 | |
303 | buf[12] = (fl >> 0) & 0xff; |
304 | buf[13] = (fl >> 8) & 0xff; |
305 | buf[14] = (fl >> 16) & 0xff; |
306 | buf[15] = (fl >> 24) & 0xff; |
307 | |
308 | line6_send_raw_message(line6: &podhd->line6, buffer: buf, size: sizeof(msg)); |
309 | kfree(objp: buf); |
310 | |
311 | podhd->monitor_level = value; |
312 | } |
313 | |
314 | /* control info callback */ |
315 | static int snd_podhd_control_monitor_info(struct snd_kcontrol *kcontrol, |
316 | struct snd_ctl_elem_info *uinfo) |
317 | { |
318 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
319 | uinfo->count = 1; |
320 | uinfo->value.integer.min = 0; |
321 | uinfo->value.integer.max = 100; |
322 | uinfo->value.integer.step = 1; |
323 | return 0; |
324 | } |
325 | |
326 | /* control get callback */ |
327 | static int snd_podhd_control_monitor_get(struct snd_kcontrol *kcontrol, |
328 | struct snd_ctl_elem_value *ucontrol) |
329 | { |
330 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
331 | struct usb_line6_podhd *podhd = line6_to_podhd(line6pcm->line6); |
332 | |
333 | ucontrol->value.integer.value[0] = podhd->monitor_level; |
334 | return 0; |
335 | } |
336 | |
337 | /* control put callback */ |
338 | static int snd_podhd_control_monitor_put(struct snd_kcontrol *kcontrol, |
339 | struct snd_ctl_elem_value *ucontrol) |
340 | { |
341 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
342 | struct usb_line6_podhd *podhd = line6_to_podhd(line6pcm->line6); |
343 | |
344 | if (ucontrol->value.integer.value[0] == podhd->monitor_level) |
345 | return 0; |
346 | |
347 | podhd_set_monitor_level(podhd, value: ucontrol->value.integer.value[0]); |
348 | return 1; |
349 | } |
350 | |
351 | /* control definition */ |
352 | static const struct snd_kcontrol_new podhd_control_monitor = { |
353 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
354 | .name = "Monitor Playback Volume" , |
355 | .index = 0, |
356 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
357 | .info = snd_podhd_control_monitor_info, |
358 | .get = snd_podhd_control_monitor_get, |
359 | .put = snd_podhd_control_monitor_put |
360 | }; |
361 | |
362 | /* |
363 | Try to init POD HD device. |
364 | */ |
365 | static int podhd_init(struct usb_line6 *line6, |
366 | const struct usb_device_id *id) |
367 | { |
368 | int err; |
369 | struct usb_line6_podhd *pod = line6_to_podhd(line6); |
370 | struct usb_interface *intf; |
371 | |
372 | line6->disconnect = podhd_disconnect; |
373 | line6->startup = podhd_startup; |
374 | |
375 | if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { |
376 | /* claim the data interface */ |
377 | intf = usb_ifnum_to_if(dev: line6->usbdev, |
378 | ifnum: pod->line6.properties->ctrl_if); |
379 | if (!intf) { |
380 | dev_err(pod->line6.ifcdev, "interface %d not found\n" , |
381 | pod->line6.properties->ctrl_if); |
382 | return -ENODEV; |
383 | } |
384 | |
385 | err = usb_driver_claim_interface(driver: &podhd_driver, iface: intf, NULL); |
386 | if (err != 0) { |
387 | dev_err(pod->line6.ifcdev, "can't claim interface %d, error %d\n" , |
388 | pod->line6.properties->ctrl_if, err); |
389 | return err; |
390 | } |
391 | } |
392 | |
393 | if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) { |
394 | /* create sysfs entries: */ |
395 | err = snd_card_add_dev_attr(card: line6->card, group: &podhd_dev_attr_group); |
396 | if (err < 0) |
397 | return err; |
398 | } |
399 | |
400 | if (pod->line6.properties->capabilities & LINE6_CAP_PCM) { |
401 | /* initialize PCM subsystem: */ |
402 | err = line6_init_pcm(line6, |
403 | properties: (id->driver_info == LINE6_PODX3 || |
404 | id->driver_info == LINE6_PODX3LIVE) ? &podx3_pcm_properties : |
405 | &podhd_pcm_properties); |
406 | if (err < 0) |
407 | return err; |
408 | } |
409 | |
410 | if (pod->line6.properties->capabilities & LINE6_CAP_HWMON_CTL) { |
411 | podhd_set_monitor_level(podhd: pod, value: 100); |
412 | err = snd_ctl_add(card: line6->card, |
413 | kcontrol: snd_ctl_new1(kcontrolnew: &podhd_control_monitor, |
414 | private_data: line6->line6pcm)); |
415 | if (err < 0) |
416 | return err; |
417 | } |
418 | |
419 | if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO)) { |
420 | /* register USB audio system directly */ |
421 | return snd_card_register(card: line6->card); |
422 | } |
423 | |
424 | /* init device and delay registering */ |
425 | schedule_delayed_work(dwork: &line6->startup_work, |
426 | delay: msecs_to_jiffies(PODHD_STARTUP_DELAY)); |
427 | return 0; |
428 | } |
429 | |
430 | #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) |
431 | #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) |
432 | |
433 | /* table of devices that work with this driver */ |
434 | static const struct usb_device_id podhd_id_table[] = { |
435 | /* TODO: no need to alloc data interfaces when only audio is used */ |
436 | { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, |
437 | { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, |
438 | { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 }, |
439 | { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, |
440 | { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, |
441 | { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, |
442 | { LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP }, |
443 | {} |
444 | }; |
445 | |
446 | MODULE_DEVICE_TABLE(usb, podhd_id_table); |
447 | |
448 | static const struct line6_properties podhd_properties_table[] = { |
449 | [LINE6_PODHD300] = { |
450 | .id = "PODHD300" , |
451 | .name = "POD HD300" , |
452 | .capabilities = LINE6_CAP_PCM |
453 | | LINE6_CAP_HWMON, |
454 | .altsetting = 5, |
455 | .ep_ctrl_r = 0x84, |
456 | .ep_ctrl_w = 0x03, |
457 | .ep_audio_r = 0x82, |
458 | .ep_audio_w = 0x01, |
459 | }, |
460 | [LINE6_PODHD400] = { |
461 | .id = "PODHD400" , |
462 | .name = "POD HD400" , |
463 | .capabilities = LINE6_CAP_PCM |
464 | | LINE6_CAP_HWMON, |
465 | .altsetting = 5, |
466 | .ep_ctrl_r = 0x84, |
467 | .ep_ctrl_w = 0x03, |
468 | .ep_audio_r = 0x82, |
469 | .ep_audio_w = 0x01, |
470 | }, |
471 | [LINE6_PODHD500] = { |
472 | .id = "PODHD500" , |
473 | .name = "POD HD500" , |
474 | .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL |
475 | | LINE6_CAP_HWMON | LINE6_CAP_HWMON_CTL, |
476 | .altsetting = 1, |
477 | .ctrl_if = 1, |
478 | .ep_ctrl_r = 0x81, |
479 | .ep_ctrl_w = 0x01, |
480 | .ep_audio_r = 0x86, |
481 | .ep_audio_w = 0x02, |
482 | }, |
483 | [LINE6_PODX3] = { |
484 | .id = "PODX3" , |
485 | .name = "POD X3" , |
486 | .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO |
487 | | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, |
488 | .altsetting = 1, |
489 | .ep_ctrl_r = 0x81, |
490 | .ep_ctrl_w = 0x01, |
491 | .ctrl_if = 1, |
492 | .ep_audio_r = 0x86, |
493 | .ep_audio_w = 0x02, |
494 | }, |
495 | [LINE6_PODX3LIVE] = { |
496 | .id = "PODX3LIVE" , |
497 | .name = "POD X3 LIVE" , |
498 | .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO |
499 | | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, |
500 | .altsetting = 1, |
501 | .ep_ctrl_r = 0x81, |
502 | .ep_ctrl_w = 0x01, |
503 | .ctrl_if = 1, |
504 | .ep_audio_r = 0x86, |
505 | .ep_audio_w = 0x02, |
506 | }, |
507 | [LINE6_PODHD500X] = { |
508 | .id = "PODHD500X" , |
509 | .name = "POD HD500X" , |
510 | .capabilities = LINE6_CAP_CONTROL |
511 | | LINE6_CAP_PCM | LINE6_CAP_HWMON, |
512 | .altsetting = 1, |
513 | .ep_ctrl_r = 0x81, |
514 | .ep_ctrl_w = 0x01, |
515 | .ctrl_if = 1, |
516 | .ep_audio_r = 0x86, |
517 | .ep_audio_w = 0x02, |
518 | }, |
519 | [LINE6_PODHDDESKTOP] = { |
520 | .id = "PODHDDESKTOP" , |
521 | .name = "POD HDDESKTOP" , |
522 | .capabilities = LINE6_CAP_CONTROL |
523 | | LINE6_CAP_PCM | LINE6_CAP_HWMON, |
524 | .altsetting = 1, |
525 | .ep_ctrl_r = 0x81, |
526 | .ep_ctrl_w = 0x01, |
527 | .ctrl_if = 1, |
528 | .ep_audio_r = 0x86, |
529 | .ep_audio_w = 0x02, |
530 | }, |
531 | }; |
532 | |
533 | /* |
534 | Probe USB device. |
535 | */ |
536 | static int podhd_probe(struct usb_interface *interface, |
537 | const struct usb_device_id *id) |
538 | { |
539 | return line6_probe(interface, id, driver_name: "Line6-PODHD" , |
540 | properties: &podhd_properties_table[id->driver_info], |
541 | private_init: podhd_init, data_size: sizeof(struct usb_line6_podhd)); |
542 | } |
543 | |
544 | static struct usb_driver podhd_driver = { |
545 | .name = KBUILD_MODNAME, |
546 | .probe = podhd_probe, |
547 | .disconnect = line6_disconnect, |
548 | #ifdef CONFIG_PM |
549 | .suspend = line6_suspend, |
550 | .resume = line6_resume, |
551 | .reset_resume = line6_resume, |
552 | #endif |
553 | .id_table = podhd_id_table, |
554 | }; |
555 | |
556 | module_usb_driver(podhd_driver); |
557 | |
558 | MODULE_DESCRIPTION("Line 6 PODHD USB driver" ); |
559 | MODULE_LICENSE("GPL" ); |
560 | |