Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
e1a164d7 | 2 | * Line6 Linux USB driver - 0.9.1beta |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
5 | * |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation, version 2. | |
9 | * | |
10 | */ | |
11 | ||
705ececd MG |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> | |
5a0e3ad6 | 14 | #include <linux/slab.h> |
705ececd MG |
15 | #include <linux/usb.h> |
16 | ||
17 | #include "audio.h" | |
18 | #include "capture.h" | |
1027f476 | 19 | #include "driver.h" |
705ececd MG |
20 | #include "midi.h" |
21 | #include "playback.h" | |
22 | #include "pod.h" | |
16dc1040 | 23 | #include "podhd.h" |
705ececd MG |
24 | #include "revision.h" |
25 | #include "toneport.h" | |
26 | #include "usbdefs.h" | |
27 | #include "variax.h" | |
28 | ||
705ececd MG |
29 | #define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" |
30 | #define DRIVER_DESC "Line6 USB Driver" | |
e1a164d7 | 31 | #define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION |
705ececd | 32 | |
f45be7dc | 33 | #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) |
7ad07310 | 34 | #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) |
daf54a59 | 35 | |
705ececd | 36 | /* table of devices that work with this driver */ |
a457732b | 37 | static const struct usb_device_id line6_id_table[] = { |
f45be7dc CR |
38 | { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, |
39 | { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, | |
40 | { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, | |
41 | { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, | |
3a3eae6c | 42 | { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, |
f45be7dc CR |
43 | { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, |
44 | { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, | |
951dd316 CR |
45 | { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, |
46 | { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, | |
f45be7dc CR |
47 | { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, |
48 | { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, | |
b98a8115 | 49 | { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, |
f45be7dc | 50 | { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, |
7ad07310 CR |
51 | { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, |
52 | { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, | |
f45be7dc CR |
53 | { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, |
54 | { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, | |
55 | { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, | |
b98a8115 | 56 | { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, |
f45be7dc | 57 | { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, |
daf54a59 | 58 | {} |
705ececd | 59 | }; |
e1a164d7 | 60 | |
36445bc1 | 61 | MODULE_DEVICE_TABLE(usb, line6_id_table); |
705ececd | 62 | |
ba79e52b | 63 | static const struct line6_properties line6_properties_table[] = { |
4d947546 CR |
64 | [LINE6_BASSPODXT] = { |
65 | .id = "BassPODxt", | |
66 | .name = "BassPODxt", | |
7b3e4d47 CR |
67 | .capabilities = LINE6_CAP_CONTROL |
68 | | LINE6_CAP_PCM | |
69 | | LINE6_CAP_HWMON, | |
7b9584fa | 70 | .altsetting = 5, |
9e165be7 CR |
71 | .ep_ctrl_r = 0x84, |
72 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
73 | .ep_audio_r = 0x82, |
74 | .ep_audio_w = 0x01, | |
4d947546 CR |
75 | }, |
76 | [LINE6_BASSPODXTLIVE] = { | |
77 | .id = "BassPODxtLive", | |
78 | .name = "BassPODxt Live", | |
7b3e4d47 CR |
79 | .capabilities = LINE6_CAP_CONTROL |
80 | | LINE6_CAP_PCM | |
81 | | LINE6_CAP_HWMON, | |
7b9584fa | 82 | .altsetting = 1, |
9e165be7 CR |
83 | .ep_ctrl_r = 0x84, |
84 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
85 | .ep_audio_r = 0x82, |
86 | .ep_audio_w = 0x01, | |
4d947546 CR |
87 | }, |
88 | [LINE6_BASSPODXTPRO] = { | |
89 | .id = "BassPODxtPro", | |
90 | .name = "BassPODxt Pro", | |
7b3e4d47 CR |
91 | .capabilities = LINE6_CAP_CONTROL |
92 | | LINE6_CAP_PCM | |
93 | | LINE6_CAP_HWMON, | |
7b9584fa | 94 | .altsetting = 5, |
9e165be7 CR |
95 | .ep_ctrl_r = 0x84, |
96 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
97 | .ep_audio_r = 0x82, |
98 | .ep_audio_w = 0x01, | |
4d947546 CR |
99 | }, |
100 | [LINE6_GUITARPORT] = { | |
101 | .id = "GuitarPort", | |
102 | .name = "GuitarPort", | |
7b3e4d47 | 103 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 104 | .altsetting = 2, /* 1..4 seem to be ok */ |
9e165be7 | 105 | /* no control channel */ |
16d603d3 CR |
106 | .ep_audio_r = 0x82, |
107 | .ep_audio_w = 0x01, | |
4d947546 CR |
108 | }, |
109 | [LINE6_POCKETPOD] = { | |
110 | .id = "PocketPOD", | |
111 | .name = "Pocket POD", | |
7b3e4d47 | 112 | .capabilities = LINE6_CAP_CONTROL, |
7b9584fa | 113 | .altsetting = 0, |
9e165be7 CR |
114 | .ep_ctrl_r = 0x82, |
115 | .ep_ctrl_w = 0x02, | |
16d603d3 | 116 | /* no audio channel */ |
4d947546 CR |
117 | }, |
118 | [LINE6_PODHD300] = { | |
119 | .id = "PODHD300", | |
120 | .name = "POD HD300", | |
7b3e4d47 CR |
121 | .capabilities = LINE6_CAP_CONTROL |
122 | | LINE6_CAP_PCM | |
123 | | LINE6_CAP_HWMON, | |
7b9584fa | 124 | .altsetting = 5, |
9e165be7 CR |
125 | .ep_ctrl_r = 0x84, |
126 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
127 | .ep_audio_r = 0x82, |
128 | .ep_audio_w = 0x01, | |
4d947546 CR |
129 | }, |
130 | [LINE6_PODHD400] = { | |
131 | .id = "PODHD400", | |
132 | .name = "POD HD400", | |
7b3e4d47 CR |
133 | .capabilities = LINE6_CAP_CONTROL |
134 | | LINE6_CAP_PCM | |
135 | | LINE6_CAP_HWMON, | |
7b9584fa | 136 | .altsetting = 5, |
9e165be7 CR |
137 | .ep_ctrl_r = 0x84, |
138 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
139 | .ep_audio_r = 0x82, |
140 | .ep_audio_w = 0x01, | |
4d947546 | 141 | }, |
951dd316 CR |
142 | [LINE6_PODHD500_0] = { |
143 | .id = "PODHD500", | |
144 | .name = "POD HD500", | |
145 | .capabilities = LINE6_CAP_CONTROL | |
146 | | LINE6_CAP_PCM | |
147 | | LINE6_CAP_HWMON, | |
7b9584fa | 148 | .altsetting = 1, |
9e165be7 CR |
149 | .ep_ctrl_r = 0x81, |
150 | .ep_ctrl_w = 0x01, | |
16d603d3 CR |
151 | .ep_audio_r = 0x86, |
152 | .ep_audio_w = 0x02, | |
951dd316 CR |
153 | }, |
154 | [LINE6_PODHD500_1] = { | |
4d947546 CR |
155 | .id = "PODHD500", |
156 | .name = "POD HD500", | |
7b3e4d47 CR |
157 | .capabilities = LINE6_CAP_CONTROL |
158 | | LINE6_CAP_PCM | |
159 | | LINE6_CAP_HWMON, | |
7b9584fa | 160 | .altsetting = 1, |
9e165be7 CR |
161 | .ep_ctrl_r = 0x81, |
162 | .ep_ctrl_w = 0x01, | |
16d603d3 CR |
163 | .ep_audio_r = 0x86, |
164 | .ep_audio_w = 0x02, | |
4d947546 CR |
165 | }, |
166 | [LINE6_PODSTUDIO_GX] = { | |
167 | .id = "PODStudioGX", | |
168 | .name = "POD Studio GX", | |
7b3e4d47 | 169 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 170 | .altsetting = 2, /* 1..4 seem to be ok */ |
9e165be7 | 171 | /* no control channel */ |
16d603d3 CR |
172 | .ep_audio_r = 0x82, |
173 | .ep_audio_w = 0x01, | |
4d947546 CR |
174 | }, |
175 | [LINE6_PODSTUDIO_UX1] = { | |
176 | .id = "PODStudioUX1", | |
177 | .name = "POD Studio UX1", | |
7b3e4d47 | 178 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 179 | .altsetting = 2, /* 1..4 seem to be ok */ |
9e165be7 | 180 | /* no control channel */ |
16d603d3 CR |
181 | .ep_audio_r = 0x82, |
182 | .ep_audio_w = 0x01, | |
4d947546 CR |
183 | }, |
184 | [LINE6_PODSTUDIO_UX2] = { | |
185 | .id = "PODStudioUX2", | |
186 | .name = "POD Studio UX2", | |
7b3e4d47 | 187 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 188 | .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ |
9e165be7 | 189 | /* no control channel */ |
16d603d3 CR |
190 | .ep_audio_r = 0x82, |
191 | .ep_audio_w = 0x01, | |
4d947546 CR |
192 | }, |
193 | [LINE6_PODXT] = { | |
194 | .id = "PODxt", | |
195 | .name = "PODxt", | |
7b3e4d47 CR |
196 | .capabilities = LINE6_CAP_CONTROL |
197 | | LINE6_CAP_PCM | |
198 | | LINE6_CAP_HWMON, | |
7b9584fa | 199 | .altsetting = 5, |
9e165be7 CR |
200 | .ep_ctrl_r = 0x84, |
201 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
202 | .ep_audio_r = 0x82, |
203 | .ep_audio_w = 0x01, | |
4d947546 | 204 | }, |
7ad07310 CR |
205 | [LINE6_PODXTLIVE_POD] = { |
206 | .id = "PODxtLive", | |
207 | .name = "PODxt Live", | |
208 | .capabilities = LINE6_CAP_CONTROL | |
209 | | LINE6_CAP_PCM | |
210 | | LINE6_CAP_HWMON, | |
7b9584fa | 211 | .altsetting = 1, |
9e165be7 CR |
212 | .ep_ctrl_r = 0x84, |
213 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
214 | .ep_audio_r = 0x82, |
215 | .ep_audio_w = 0x01, | |
7ad07310 CR |
216 | }, |
217 | [LINE6_PODXTLIVE_VARIAX] = { | |
4d947546 CR |
218 | .id = "PODxtLive", |
219 | .name = "PODxt Live", | |
7b3e4d47 CR |
220 | .capabilities = LINE6_CAP_CONTROL |
221 | | LINE6_CAP_PCM | |
222 | | LINE6_CAP_HWMON, | |
7b9584fa | 223 | .altsetting = 1, |
9e165be7 CR |
224 | .ep_ctrl_r = 0x86, |
225 | .ep_ctrl_w = 0x05, | |
16d603d3 CR |
226 | .ep_audio_r = 0x82, |
227 | .ep_audio_w = 0x01, | |
4d947546 CR |
228 | }, |
229 | [LINE6_PODXTPRO] = { | |
230 | .id = "PODxtPro", | |
231 | .name = "PODxt Pro", | |
7b3e4d47 CR |
232 | .capabilities = LINE6_CAP_CONTROL |
233 | | LINE6_CAP_PCM | |
234 | | LINE6_CAP_HWMON, | |
7b9584fa | 235 | .altsetting = 5, |
9e165be7 CR |
236 | .ep_ctrl_r = 0x84, |
237 | .ep_ctrl_w = 0x03, | |
16d603d3 CR |
238 | .ep_audio_r = 0x82, |
239 | .ep_audio_w = 0x01, | |
4d947546 CR |
240 | }, |
241 | [LINE6_TONEPORT_GX] = { | |
242 | .id = "TonePortGX", | |
243 | .name = "TonePort GX", | |
7b3e4d47 | 244 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 245 | .altsetting = 2, /* 1..4 seem to be ok */ |
9e165be7 | 246 | /* no control channel */ |
16d603d3 CR |
247 | .ep_audio_r = 0x82, |
248 | .ep_audio_w = 0x01, | |
4d947546 CR |
249 | }, |
250 | [LINE6_TONEPORT_UX1] = { | |
251 | .id = "TonePortUX1", | |
252 | .name = "TonePort UX1", | |
7b3e4d47 | 253 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 254 | .altsetting = 2, /* 1..4 seem to be ok */ |
9e165be7 | 255 | /* no control channel */ |
16d603d3 CR |
256 | .ep_audio_r = 0x82, |
257 | .ep_audio_w = 0x01, | |
4d947546 CR |
258 | }, |
259 | [LINE6_TONEPORT_UX2] = { | |
260 | .id = "TonePortUX2", | |
261 | .name = "TonePort UX2", | |
7b3e4d47 | 262 | .capabilities = LINE6_CAP_PCM, |
7b9584fa | 263 | .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ |
9e165be7 | 264 | /* no control channel */ |
16d603d3 CR |
265 | .ep_audio_r = 0x82, |
266 | .ep_audio_w = 0x01, | |
4d947546 CR |
267 | }, |
268 | [LINE6_VARIAX] = { | |
269 | .id = "Variax", | |
270 | .name = "Variax Workbench", | |
7b3e4d47 | 271 | .capabilities = LINE6_CAP_CONTROL, |
7b9584fa | 272 | .altsetting = 1, |
9e165be7 CR |
273 | .ep_ctrl_r = 0x82, |
274 | .ep_ctrl_w = 0x01, | |
16d603d3 | 275 | /* no audio channel */ |
4d947546 | 276 | } |
705ececd | 277 | }; |
705ececd MG |
278 | |
279 | /* | |
280 | This is Line6's MIDI manufacturer ID. | |
281 | */ | |
1027f476 MG |
282 | const unsigned char line6_midi_id[] = { |
283 | 0x00, 0x01, 0x0c | |
284 | }; | |
285 | ||
286 | /* | |
287 | Code to request version of POD, Variax interface | |
288 | (and maybe other devices). | |
289 | */ | |
c46b8a65 | 290 | static const char line6_request_version[] = { |
1027f476 MG |
291 | 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 |
292 | }; | |
293 | ||
705ececd MG |
294 | /** |
295 | Class for asynchronous messages. | |
296 | */ | |
36445bc1 | 297 | struct message { |
705ececd MG |
298 | struct usb_line6 *line6; |
299 | const char *buffer; | |
300 | int size; | |
301 | int done; | |
302 | }; | |
303 | ||
705ececd MG |
304 | /* |
305 | Forward declarations. | |
306 | */ | |
0c7ab158 | 307 | static void line6_data_received(struct urb *urb); |
36445bc1 GKH |
308 | static int line6_send_raw_message_async_part(struct message *msg, |
309 | struct urb *urb); | |
705ececd | 310 | |
705ececd MG |
311 | /* |
312 | Start to listen on endpoint. | |
313 | */ | |
314 | static int line6_start_listen(struct usb_line6 *line6) | |
315 | { | |
1027f476 | 316 | int err; |
a6b4699d | 317 | |
36445bc1 | 318 | usb_fill_int_urb(line6->urb_listen, line6->usbdev, |
9e165be7 CR |
319 | usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r), |
320 | line6->buffer_listen, LINE6_BUFSIZE_LISTEN, | |
321 | line6_data_received, line6, line6->interval); | |
705ececd | 322 | line6->urb_listen->actual_length = 0; |
e1a164d7 | 323 | err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); |
1027f476 MG |
324 | return err; |
325 | } | |
326 | ||
327 | /* | |
328 | Stop listening on endpoint. | |
329 | */ | |
330 | static void line6_stop_listen(struct usb_line6 *line6) | |
331 | { | |
332 | usb_kill_urb(line6->urb_listen); | |
705ececd MG |
333 | } |
334 | ||
705ececd | 335 | /* |
1027f476 | 336 | Send raw message in pieces of wMaxPacketSize bytes. |
705ececd | 337 | */ |
36445bc1 GKH |
338 | int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, |
339 | int size) | |
705ececd MG |
340 | { |
341 | int i, done = 0; | |
342 | ||
1027f476 MG |
343 | for (i = 0; i < size; i += line6->max_packet_size) { |
344 | int partial; | |
705ececd MG |
345 | const char *frag_buf = buffer + i; |
346 | int frag_size = min(line6->max_packet_size, size - i); | |
36445bc1 GKH |
347 | int retval; |
348 | ||
349 | retval = usb_interrupt_msg(line6->usbdev, | |
ba79e52b | 350 | usb_sndintpipe(line6->usbdev, |
9e165be7 | 351 | line6->properties->ep_ctrl_w), |
ba79e52b DB |
352 | (char *)frag_buf, frag_size, |
353 | &partial, LINE6_TIMEOUT * HZ); | |
705ececd | 354 | |
36445bc1 GKH |
355 | if (retval) { |
356 | dev_err(line6->ifcdev, | |
357 | "usb_interrupt_msg failed (%d)\n", retval); | |
705ececd MG |
358 | break; |
359 | } | |
360 | ||
1027f476 | 361 | done += frag_size; |
705ececd MG |
362 | } |
363 | ||
364 | return done; | |
365 | } | |
366 | ||
367 | /* | |
368 | Notification of completion of asynchronous request transmission. | |
369 | */ | |
0c7ab158 | 370 | static void line6_async_request_sent(struct urb *urb) |
705ececd MG |
371 | { |
372 | struct message *msg = (struct message *)urb->context; | |
373 | ||
36445bc1 | 374 | if (msg->done >= msg->size) { |
705ececd MG |
375 | usb_free_urb(urb); |
376 | kfree(msg); | |
36445bc1 | 377 | } else |
705ececd MG |
378 | line6_send_raw_message_async_part(msg, urb); |
379 | } | |
380 | ||
381 | /* | |
382 | Asynchronously send part of a raw message. | |
383 | */ | |
36445bc1 GKH |
384 | static int line6_send_raw_message_async_part(struct message *msg, |
385 | struct urb *urb) | |
705ececd MG |
386 | { |
387 | int retval; | |
388 | struct usb_line6 *line6 = msg->line6; | |
389 | int done = msg->done; | |
390 | int bytes = min(msg->size - done, line6->max_packet_size); | |
391 | ||
36445bc1 | 392 | usb_fill_int_urb(urb, line6->usbdev, |
9e165be7 CR |
393 | usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w), |
394 | (char *)msg->buffer + done, bytes, | |
395 | line6_async_request_sent, msg, line6->interval); | |
705ececd | 396 | |
705ececd MG |
397 | msg->done += bytes; |
398 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |
399 | ||
36445bc1 GKH |
400 | if (retval < 0) { |
401 | dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", | |
402 | __func__, retval); | |
705ececd MG |
403 | usb_free_urb(urb); |
404 | kfree(msg); | |
056e0af2 | 405 | return retval; |
705ececd MG |
406 | } |
407 | ||
408 | return 0; | |
409 | } | |
410 | ||
1027f476 MG |
411 | /* |
412 | Setup and start timer. | |
413 | */ | |
414 | void line6_start_timer(struct timer_list *timer, unsigned int msecs, | |
1cad608e | 415 | void (*function)(unsigned long), unsigned long data) |
1027f476 MG |
416 | { |
417 | setup_timer(timer, function, data); | |
418 | timer->expires = jiffies + msecs * HZ / 1000; | |
419 | add_timer(timer); | |
420 | } | |
421 | ||
705ececd MG |
422 | /* |
423 | Asynchronously send raw message. | |
424 | */ | |
36445bc1 GKH |
425 | int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, |
426 | int size) | |
705ececd MG |
427 | { |
428 | struct message *msg; | |
429 | struct urb *urb; | |
430 | ||
431 | /* create message: */ | |
432 | msg = kmalloc(sizeof(struct message), GFP_ATOMIC); | |
78110bb8 | 433 | if (msg == NULL) |
705ececd | 434 | return -ENOMEM; |
705ececd MG |
435 | |
436 | /* create URB: */ | |
437 | urb = usb_alloc_urb(0, GFP_ATOMIC); | |
438 | ||
36445bc1 | 439 | if (urb == NULL) { |
705ececd MG |
440 | kfree(msg); |
441 | dev_err(line6->ifcdev, "Out of memory\n"); | |
442 | return -ENOMEM; | |
443 | } | |
444 | ||
445 | /* set message data: */ | |
446 | msg->line6 = line6; | |
447 | msg->buffer = buffer; | |
448 | msg->size = size; | |
449 | msg->done = 0; | |
450 | ||
451 | /* start sending: */ | |
452 | return line6_send_raw_message_async_part(msg, urb); | |
453 | } | |
454 | ||
1027f476 MG |
455 | /* |
456 | Send asynchronous device version request. | |
457 | */ | |
458 | int line6_version_request_async(struct usb_line6 *line6) | |
459 | { | |
c46b8a65 GKH |
460 | char *buffer; |
461 | int retval; | |
462 | ||
77ecb6fe LN |
463 | buffer = kmemdup(line6_request_version, |
464 | sizeof(line6_request_version), GFP_ATOMIC); | |
c46b8a65 GKH |
465 | if (buffer == NULL) { |
466 | dev_err(line6->ifcdev, "Out of memory"); | |
467 | return -ENOMEM; | |
468 | } | |
469 | ||
c46b8a65 GKH |
470 | retval = line6_send_raw_message_async(line6, buffer, |
471 | sizeof(line6_request_version)); | |
472 | kfree(buffer); | |
473 | return retval; | |
1027f476 MG |
474 | } |
475 | ||
705ececd MG |
476 | /* |
477 | Send sysex message in pieces of wMaxPacketSize bytes. | |
478 | */ | |
36445bc1 GKH |
479 | int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, |
480 | int size) | |
705ececd | 481 | { |
e1a164d7 MG |
482 | return line6_send_raw_message(line6, buffer, |
483 | size + SYSEX_EXTRA_SIZE) - | |
484 | SYSEX_EXTRA_SIZE; | |
705ececd MG |
485 | } |
486 | ||
487 | /* | |
488 | Allocate buffer for sysex message and prepare header. | |
489 | @param code sysex message code | |
490 | @param size number of bytes between code and sysex end | |
491 | */ | |
36445bc1 GKH |
492 | char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, |
493 | int size) | |
705ececd | 494 | { |
1027f476 | 495 | char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); |
705ececd | 496 | |
78110bb8 | 497 | if (!buffer) |
536165d8 | 498 | return NULL; |
705ececd MG |
499 | |
500 | buffer[0] = LINE6_SYSEX_BEGIN; | |
501 | memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); | |
502 | buffer[sizeof(line6_midi_id) + 1] = code1; | |
503 | buffer[sizeof(line6_midi_id) + 2] = code2; | |
504 | buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; | |
505 | return buffer; | |
506 | } | |
507 | ||
508 | /* | |
509 | Notification of data received from the Line6 device. | |
510 | */ | |
0c7ab158 | 511 | static void line6_data_received(struct urb *urb) |
705ececd MG |
512 | { |
513 | struct usb_line6 *line6 = (struct usb_line6 *)urb->context; | |
269edc8e | 514 | struct midi_buffer *mb = &line6->line6midi->midibuf_in; |
705ececd MG |
515 | int done; |
516 | ||
36445bc1 | 517 | if (urb->status == -ESHUTDOWN) |
705ececd MG |
518 | return; |
519 | ||
e1a164d7 MG |
520 | done = |
521 | line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); | |
705ececd | 522 | |
36445bc1 | 523 | if (done < urb->actual_length) { |
1027f476 | 524 | line6_midibuf_ignore(mb, done); |
e00d33cb SH |
525 | dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", |
526 | done, urb->actual_length); | |
705ececd MG |
527 | } |
528 | ||
36445bc1 | 529 | for (;;) { |
e1a164d7 MG |
530 | done = |
531 | line6_midibuf_read(mb, line6->buffer_message, | |
532 | LINE6_MESSAGE_MAXLEN); | |
705ececd | 533 | |
36445bc1 | 534 | if (done == 0) |
705ececd MG |
535 | break; |
536 | ||
705ececd | 537 | line6->message_length = done; |
705ececd MG |
538 | line6_midi_receive(line6, line6->buffer_message, done); |
539 | ||
a23a8bff CR |
540 | switch (line6->type) { |
541 | case LINE6_BASSPODXT: | |
542 | case LINE6_BASSPODXTLIVE: | |
543 | case LINE6_BASSPODXTPRO: | |
544 | case LINE6_PODXT: | |
545 | case LINE6_PODXTPRO: | |
546 | case LINE6_POCKETPOD: | |
1cad3e8d | 547 | line6_pod_process_message(line6); |
705ececd MG |
548 | break; |
549 | ||
a23a8bff CR |
550 | case LINE6_PODHD300: |
551 | case LINE6_PODHD400: | |
951dd316 CR |
552 | case LINE6_PODHD500_0: |
553 | case LINE6_PODHD500_1: | |
16dc1040 SH |
554 | break; /* let userspace handle MIDI */ |
555 | ||
7ad07310 | 556 | case LINE6_PODXTLIVE_POD: |
1cad3e8d | 557 | line6_pod_process_message(line6); |
7ad07310 CR |
558 | break; |
559 | ||
560 | case LINE6_PODXTLIVE_VARIAX: | |
1cad3e8d | 561 | line6_variax_process_message(line6); |
705ececd MG |
562 | break; |
563 | ||
a23a8bff | 564 | case LINE6_VARIAX: |
1cad3e8d | 565 | line6_variax_process_message(line6); |
705ececd MG |
566 | break; |
567 | ||
568 | default: | |
569 | MISSING_CASE; | |
570 | } | |
571 | } | |
572 | ||
573 | line6_start_listen(line6); | |
574 | } | |
575 | ||
576 | /* | |
577 | Send channel number (i.e., switch to a different sound). | |
578 | */ | |
317e188b | 579 | int line6_send_program(struct usb_line6 *line6, u8 value) |
705ececd | 580 | { |
1027f476 | 581 | int retval; |
705ececd | 582 | unsigned char *buffer; |
1027f476 MG |
583 | int partial; |
584 | ||
585 | buffer = kmalloc(2, GFP_KERNEL); | |
78110bb8 | 586 | if (!buffer) |
705ececd | 587 | return -ENOMEM; |
705ececd MG |
588 | |
589 | buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; | |
590 | buffer[1] = value; | |
591 | ||
1027f476 MG |
592 | retval = usb_interrupt_msg(line6->usbdev, |
593 | usb_sndintpipe(line6->usbdev, | |
9e165be7 | 594 | line6->properties->ep_ctrl_w), |
1027f476 MG |
595 | buffer, 2, &partial, LINE6_TIMEOUT * HZ); |
596 | ||
597 | if (retval) | |
e1a164d7 MG |
598 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", |
599 | retval); | |
1027f476 MG |
600 | |
601 | kfree(buffer); | |
602 | return retval; | |
705ececd MG |
603 | } |
604 | ||
605 | /* | |
606 | Transmit Line6 control parameter. | |
607 | */ | |
2471c093 | 608 | int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) |
705ececd | 609 | { |
1027f476 | 610 | int retval; |
705ececd | 611 | unsigned char *buffer; |
1027f476 MG |
612 | int partial; |
613 | ||
614 | buffer = kmalloc(3, GFP_KERNEL); | |
78110bb8 | 615 | if (!buffer) |
705ececd | 616 | return -ENOMEM; |
705ececd MG |
617 | |
618 | buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; | |
619 | buffer[1] = param; | |
620 | buffer[2] = value; | |
621 | ||
1027f476 | 622 | retval = usb_interrupt_msg(line6->usbdev, |
e1a164d7 | 623 | usb_sndintpipe(line6->usbdev, |
9e165be7 | 624 | line6->properties->ep_ctrl_w), |
1027f476 MG |
625 | buffer, 3, &partial, LINE6_TIMEOUT * HZ); |
626 | ||
627 | if (retval) | |
e1a164d7 MG |
628 | dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", |
629 | retval); | |
1027f476 MG |
630 | |
631 | kfree(buffer); | |
632 | return retval; | |
705ececd MG |
633 | } |
634 | ||
635 | /* | |
636 | Read data from device. | |
637 | */ | |
e1a164d7 MG |
638 | int line6_read_data(struct usb_line6 *line6, int address, void *data, |
639 | size_t datalen) | |
705ececd MG |
640 | { |
641 | struct usb_device *usbdev = line6->usbdev; | |
642 | int ret; | |
643 | unsigned char len; | |
644 | ||
645 | /* query the serial number: */ | |
646 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, | |
1027f476 MG |
647 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, |
648 | (datalen << 8) | 0x21, address, | |
649 | NULL, 0, LINE6_TIMEOUT * HZ); | |
705ececd | 650 | |
36445bc1 | 651 | if (ret < 0) { |
705ececd MG |
652 | dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); |
653 | return ret; | |
654 | } | |
655 | ||
bc776f27 | 656 | /* Wait for data length. We'll get 0xff until length arrives. */ |
705ececd MG |
657 | do { |
658 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | |
36445bc1 GKH |
659 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | |
660 | USB_DIR_IN, | |
661 | 0x0012, 0x0000, &len, 1, | |
662 | LINE6_TIMEOUT * HZ); | |
663 | if (ret < 0) { | |
664 | dev_err(line6->ifcdev, | |
665 | "receive length failed (error %d)\n", ret); | |
705ececd MG |
666 | return ret; |
667 | } | |
d722a510 | 668 | } while (len == 0xff); |
36445bc1 GKH |
669 | |
670 | if (len != datalen) { | |
671 | /* should be equal or something went wrong */ | |
672 | dev_err(line6->ifcdev, | |
673 | "length mismatch (expected %d, got %d)\n", | |
674 | (int)datalen, (int)len); | |
705ececd MG |
675 | return -EINVAL; |
676 | } | |
677 | ||
678 | /* receive the result: */ | |
679 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, | |
36445bc1 GKH |
680 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
681 | 0x0013, 0x0000, data, datalen, | |
682 | LINE6_TIMEOUT * HZ); | |
705ececd | 683 | |
36445bc1 | 684 | if (ret < 0) { |
705ececd MG |
685 | dev_err(line6->ifcdev, "read failed (error %d)\n", ret); |
686 | return ret; | |
687 | } | |
688 | ||
689 | return 0; | |
690 | } | |
691 | ||
692 | /* | |
693 | Write data to device. | |
694 | */ | |
36445bc1 GKH |
695 | int line6_write_data(struct usb_line6 *line6, int address, void *data, |
696 | size_t datalen) | |
705ececd MG |
697 | { |
698 | struct usb_device *usbdev = line6->usbdev; | |
699 | int ret; | |
700 | unsigned char status; | |
701 | ||
36445bc1 GKH |
702 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, |
703 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | |
704 | 0x0022, address, data, datalen, | |
705 | LINE6_TIMEOUT * HZ); | |
705ececd | 706 | |
36445bc1 GKH |
707 | if (ret < 0) { |
708 | dev_err(line6->ifcdev, | |
709 | "write request failed (error %d)\n", ret); | |
705ececd MG |
710 | return ret; |
711 | } | |
712 | ||
713 | do { | |
36445bc1 GKH |
714 | ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), |
715 | 0x67, | |
716 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | | |
717 | USB_DIR_IN, | |
718 | 0x0012, 0x0000, | |
719 | &status, 1, LINE6_TIMEOUT * HZ); | |
720 | ||
721 | if (ret < 0) { | |
722 | dev_err(line6->ifcdev, | |
723 | "receiving status failed (error %d)\n", ret); | |
705ececd MG |
724 | return ret; |
725 | } | |
027360c5 | 726 | } while (status == 0xff); |
705ececd | 727 | |
36445bc1 | 728 | if (status != 0) { |
705ececd MG |
729 | dev_err(line6->ifcdev, "write failed (error %d)\n", ret); |
730 | return -EINVAL; | |
731 | } | |
732 | ||
733 | return 0; | |
734 | } | |
735 | ||
736 | /* | |
737 | Read Line6 device serial number. | |
738 | (POD, TonePort, GuitarPort) | |
739 | */ | |
740 | int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) | |
741 | { | |
e1a164d7 MG |
742 | return line6_read_data(line6, 0x80d0, serial_number, |
743 | sizeof(*serial_number)); | |
705ececd MG |
744 | } |
745 | ||
746 | /* | |
747 | No operation (i.e., unsupported). | |
748 | */ | |
77491e52 GKH |
749 | ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, |
750 | char *buf) | |
705ececd MG |
751 | { |
752 | return 0; | |
753 | } | |
754 | ||
705ececd MG |
755 | /* |
756 | Generic destructor. | |
757 | */ | |
758 | static void line6_destruct(struct usb_interface *interface) | |
759 | { | |
760 | struct usb_line6 *line6; | |
36445bc1 GKH |
761 | |
762 | if (interface == NULL) | |
763 | return; | |
705ececd | 764 | line6 = usb_get_intfdata(interface); |
36445bc1 GKH |
765 | if (line6 == NULL) |
766 | return; | |
705ececd MG |
767 | |
768 | /* free buffer memory first: */ | |
36445bc1 GKH |
769 | kfree(line6->buffer_message); |
770 | kfree(line6->buffer_listen); | |
705ececd MG |
771 | |
772 | /* then free URBs: */ | |
36445bc1 | 773 | usb_free_urb(line6->urb_listen); |
705ececd MG |
774 | |
775 | /* make sure the device isn't destructed twice: */ | |
776 | usb_set_intfdata(interface, NULL); | |
777 | ||
778 | /* free interface data: */ | |
779 | kfree(line6); | |
780 | } | |
781 | ||
705ececd MG |
782 | /* |
783 | Probe USB device. | |
784 | */ | |
e1a164d7 MG |
785 | static int line6_probe(struct usb_interface *interface, |
786 | const struct usb_device_id *id) | |
705ececd | 787 | { |
c33a20b7 | 788 | enum line6_device_type devtype; |
4bd8b4de DC |
789 | struct usb_device *usbdev; |
790 | struct usb_line6 *line6; | |
705ececd | 791 | const struct line6_properties *properties; |
7b9584fa | 792 | int interface_number; |
705ececd | 793 | int size = 0; |
705ececd MG |
794 | int ret; |
795 | ||
36445bc1 GKH |
796 | if (interface == NULL) |
797 | return -ENODEV; | |
705ececd | 798 | usbdev = interface_to_usbdev(interface); |
36445bc1 GKH |
799 | if (usbdev == NULL) |
800 | return -ENODEV; | |
705ececd | 801 | |
705ececd | 802 | /* we don't handle multiple configurations */ |
ab366c1a KV |
803 | if (usbdev->descriptor.bNumConfigurations != 1) { |
804 | ret = -ENODEV; | |
805 | goto err_put; | |
806 | } | |
705ececd | 807 | |
c33a20b7 | 808 | devtype = id->driver_info; |
705ececd | 809 | |
705ececd MG |
810 | /* initialize device info: */ |
811 | properties = &line6_properties_table[devtype]; | |
812 | dev_info(&interface->dev, "Line6 %s found\n", properties->name); | |
705ececd MG |
813 | |
814 | /* query interface number */ | |
815 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | |
816 | ||
7b9584fa CR |
817 | ret = usb_set_interface(usbdev, interface_number, |
818 | properties->altsetting); | |
36445bc1 | 819 | if (ret < 0) { |
705ececd | 820 | dev_err(&interface->dev, "set_interface failed\n"); |
ab366c1a | 821 | goto err_put; |
705ececd MG |
822 | } |
823 | ||
a23a8bff CR |
824 | /* initialize device data based on device: */ |
825 | switch (devtype) { | |
826 | case LINE6_BASSPODXT: | |
827 | case LINE6_BASSPODXTLIVE: | |
828 | case LINE6_BASSPODXTPRO: | |
829 | case LINE6_PODXT: | |
830 | case LINE6_PODXTPRO: | |
705ececd | 831 | size = sizeof(struct usb_line6_pod); |
705ececd MG |
832 | break; |
833 | ||
a23a8bff CR |
834 | case LINE6_PODHD300: |
835 | case LINE6_PODHD400: | |
16dc1040 | 836 | size = sizeof(struct usb_line6_podhd); |
16dc1040 SH |
837 | break; |
838 | ||
951dd316 CR |
839 | case LINE6_PODHD500_0: |
840 | case LINE6_PODHD500_1: | |
4c6fb5fc | 841 | size = sizeof(struct usb_line6_podhd); |
4c6fb5fc MG |
842 | break; |
843 | ||
a23a8bff | 844 | case LINE6_POCKETPOD: |
1027f476 | 845 | size = sizeof(struct usb_line6_pod); |
1027f476 MG |
846 | break; |
847 | ||
a23a8bff CR |
848 | case LINE6_PODSTUDIO_GX: |
849 | case LINE6_PODSTUDIO_UX1: | |
850 | case LINE6_PODSTUDIO_UX2: | |
851 | case LINE6_TONEPORT_GX: | |
852 | case LINE6_TONEPORT_UX1: | |
853 | case LINE6_TONEPORT_UX2: | |
854 | case LINE6_GUITARPORT: | |
705ececd | 855 | size = sizeof(struct usb_line6_toneport); |
705ececd MG |
856 | break; |
857 | ||
7ad07310 CR |
858 | case LINE6_PODXTLIVE_POD: |
859 | size = sizeof(struct usb_line6_pod); | |
7ad07310 | 860 | break; |
705ececd | 861 | |
7ad07310 CR |
862 | case LINE6_PODXTLIVE_VARIAX: |
863 | size = sizeof(struct usb_line6_variax); | |
705ececd MG |
864 | break; |
865 | ||
a23a8bff | 866 | case LINE6_VARIAX: |
705ececd | 867 | size = sizeof(struct usb_line6_variax); |
705ececd MG |
868 | break; |
869 | ||
870 | default: | |
871 | MISSING_CASE; | |
ab366c1a KV |
872 | ret = -ENODEV; |
873 | goto err_put; | |
705ececd MG |
874 | } |
875 | ||
36445bc1 | 876 | if (size == 0) { |
4bd8b4de | 877 | dev_err(&interface->dev, |
e1a164d7 | 878 | "driver bug: interface data size not set\n"); |
ab366c1a KV |
879 | ret = -ENODEV; |
880 | goto err_put; | |
705ececd MG |
881 | } |
882 | ||
883 | line6 = kzalloc(size, GFP_KERNEL); | |
36445bc1 | 884 | if (line6 == NULL) { |
ab366c1a KV |
885 | ret = -ENODEV; |
886 | goto err_put; | |
705ececd MG |
887 | } |
888 | ||
889 | /* store basic data: */ | |
705ececd MG |
890 | line6->properties = properties; |
891 | line6->usbdev = usbdev; | |
892 | line6->ifcdev = &interface->dev; | |
a23a8bff | 893 | line6->type = devtype; |
705ececd MG |
894 | |
895 | /* get data from endpoint descriptor (see usb_maxpacket): */ | |
896 | { | |
897 | struct usb_host_endpoint *ep; | |
9e165be7 CR |
898 | unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); |
899 | unsigned epnum = usb_pipeendpoint(pipe); | |
705ececd MG |
900 | ep = usbdev->ep_in[epnum]; |
901 | ||
36445bc1 | 902 | if (ep != NULL) { |
705ececd | 903 | line6->interval = ep->desc.bInterval; |
e1a164d7 MG |
904 | line6->max_packet_size = |
905 | le16_to_cpu(ep->desc.wMaxPacketSize); | |
36445bc1 | 906 | } else { |
705ececd MG |
907 | line6->interval = LINE6_FALLBACK_INTERVAL; |
908 | line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; | |
e1a164d7 MG |
909 | dev_err(line6->ifcdev, |
910 | "endpoint not available, using fallback values"); | |
705ececd MG |
911 | } |
912 | } | |
913 | ||
914 | usb_set_intfdata(interface, line6); | |
915 | ||
4cb1a4ae | 916 | if (properties->capabilities & LINE6_CAP_CONTROL) { |
705ececd | 917 | /* initialize USB buffers: */ |
e1a164d7 MG |
918 | line6->buffer_listen = |
919 | kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); | |
36445bc1 | 920 | if (line6->buffer_listen == NULL) { |
ab366c1a KV |
921 | ret = -ENOMEM; |
922 | goto err_destruct; | |
705ececd MG |
923 | } |
924 | ||
e1a164d7 MG |
925 | line6->buffer_message = |
926 | kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); | |
36445bc1 | 927 | if (line6->buffer_message == NULL) { |
ab366c1a KV |
928 | ret = -ENOMEM; |
929 | goto err_destruct; | |
705ececd MG |
930 | } |
931 | ||
932 | line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); | |
933 | ||
36445bc1 | 934 | if (line6->urb_listen == NULL) { |
705ececd MG |
935 | dev_err(&interface->dev, "Out of memory\n"); |
936 | line6_destruct(interface); | |
ab366c1a KV |
937 | ret = -ENOMEM; |
938 | goto err_destruct; | |
705ececd MG |
939 | } |
940 | ||
36445bc1 GKH |
941 | ret = line6_start_listen(line6); |
942 | if (ret < 0) { | |
943 | dev_err(&interface->dev, "%s: usb_submit_urb failed\n", | |
944 | __func__); | |
ab366c1a | 945 | goto err_destruct; |
705ececd MG |
946 | } |
947 | } | |
948 | ||
a23a8bff CR |
949 | /* initialize device data based on device: */ |
950 | switch (devtype) { | |
951 | case LINE6_BASSPODXT: | |
952 | case LINE6_BASSPODXTLIVE: | |
953 | case LINE6_BASSPODXTPRO: | |
954 | case LINE6_POCKETPOD: | |
955 | case LINE6_PODXT: | |
956 | case LINE6_PODXTPRO: | |
a221dd45 | 957 | ret = line6_pod_init(interface, line6); |
705ececd MG |
958 | break; |
959 | ||
a23a8bff CR |
960 | case LINE6_PODHD300: |
961 | case LINE6_PODHD400: | |
951dd316 CR |
962 | case LINE6_PODHD500_0: |
963 | case LINE6_PODHD500_1: | |
a221dd45 | 964 | ret = line6_podhd_init(interface, line6); |
16dc1040 SH |
965 | break; |
966 | ||
7ad07310 | 967 | case LINE6_PODXTLIVE_POD: |
a221dd45 | 968 | ret = line6_pod_init(interface, line6); |
7ad07310 | 969 | break; |
705ececd | 970 | |
7ad07310 | 971 | case LINE6_PODXTLIVE_VARIAX: |
a221dd45 | 972 | ret = line6_variax_init(interface, line6); |
705ececd MG |
973 | break; |
974 | ||
a23a8bff | 975 | case LINE6_VARIAX: |
a221dd45 | 976 | ret = line6_variax_init(interface, line6); |
705ececd MG |
977 | break; |
978 | ||
a23a8bff CR |
979 | case LINE6_PODSTUDIO_GX: |
980 | case LINE6_PODSTUDIO_UX1: | |
981 | case LINE6_PODSTUDIO_UX2: | |
982 | case LINE6_TONEPORT_GX: | |
983 | case LINE6_TONEPORT_UX1: | |
984 | case LINE6_TONEPORT_UX2: | |
985 | case LINE6_GUITARPORT: | |
a221dd45 | 986 | ret = line6_toneport_init(interface, line6); |
705ececd MG |
987 | break; |
988 | ||
989 | default: | |
990 | MISSING_CASE; | |
991 | ret = -ENODEV; | |
992 | } | |
993 | ||
ab366c1a KV |
994 | if (ret < 0) |
995 | goto err_destruct; | |
705ececd | 996 | |
36445bc1 GKH |
997 | ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, |
998 | "usb_device"); | |
ab366c1a KV |
999 | if (ret < 0) |
1000 | goto err_destruct; | |
705ececd | 1001 | |
1027f476 MG |
1002 | /* creation of additional special files should go here */ |
1003 | ||
36445bc1 GKH |
1004 | dev_info(&interface->dev, "Line6 %s now attached\n", |
1005 | line6->properties->name); | |
1027f476 | 1006 | |
1027f476 MG |
1007 | /* increment reference counters: */ |
1008 | usb_get_intf(interface); | |
1009 | usb_get_dev(usbdev); | |
1010 | ||
ab366c1a KV |
1011 | return 0; |
1012 | ||
1013 | err_destruct: | |
1014 | line6_destruct(interface); | |
1015 | err_put: | |
705ececd MG |
1016 | return ret; |
1017 | } | |
1018 | ||
1019 | /* | |
1020 | Line6 device disconnected. | |
1021 | */ | |
1022 | static void line6_disconnect(struct usb_interface *interface) | |
1023 | { | |
1024 | struct usb_line6 *line6; | |
1025 | struct usb_device *usbdev; | |
b430b3db | 1026 | int interface_number; |
705ececd | 1027 | |
36445bc1 GKH |
1028 | if (interface == NULL) |
1029 | return; | |
705ececd | 1030 | usbdev = interface_to_usbdev(interface); |
36445bc1 GKH |
1031 | if (usbdev == NULL) |
1032 | return; | |
705ececd | 1033 | |
1027f476 MG |
1034 | /* removal of additional special files should go here */ |
1035 | ||
705ececd MG |
1036 | sysfs_remove_link(&interface->dev.kobj, "usb_device"); |
1037 | ||
1038 | interface_number = interface->cur_altsetting->desc.bInterfaceNumber; | |
1039 | line6 = usb_get_intfdata(interface); | |
1040 | ||
36445bc1 GKH |
1041 | if (line6 != NULL) { |
1042 | if (line6->urb_listen != NULL) | |
1027f476 | 1043 | line6_stop_listen(line6); |
705ececd | 1044 | |
36445bc1 GKH |
1045 | if (usbdev != line6->usbdev) |
1046 | dev_err(line6->ifcdev, | |
1047 | "driver bug: inconsistent usb device\n"); | |
705ececd | 1048 | |
a23a8bff CR |
1049 | switch (line6->type) { |
1050 | case LINE6_BASSPODXT: | |
1051 | case LINE6_BASSPODXTLIVE: | |
1052 | case LINE6_BASSPODXTPRO: | |
1053 | case LINE6_POCKETPOD: | |
1054 | case LINE6_PODXT: | |
1055 | case LINE6_PODXTPRO: | |
1027f476 | 1056 | line6_pod_disconnect(interface); |
705ececd MG |
1057 | break; |
1058 | ||
a23a8bff CR |
1059 | case LINE6_PODHD300: |
1060 | case LINE6_PODHD400: | |
951dd316 CR |
1061 | case LINE6_PODHD500_0: |
1062 | case LINE6_PODHD500_1: | |
16dc1040 SH |
1063 | line6_podhd_disconnect(interface); |
1064 | break; | |
1065 | ||
7ad07310 CR |
1066 | case LINE6_PODXTLIVE_POD: |
1067 | line6_pod_disconnect(interface); | |
1068 | break; | |
705ececd | 1069 | |
7ad07310 CR |
1070 | case LINE6_PODXTLIVE_VARIAX: |
1071 | line6_variax_disconnect(interface); | |
705ececd MG |
1072 | break; |
1073 | ||
a23a8bff | 1074 | case LINE6_VARIAX: |
1027f476 | 1075 | line6_variax_disconnect(interface); |
705ececd MG |
1076 | break; |
1077 | ||
a23a8bff CR |
1078 | case LINE6_PODSTUDIO_GX: |
1079 | case LINE6_PODSTUDIO_UX1: | |
1080 | case LINE6_PODSTUDIO_UX2: | |
1081 | case LINE6_TONEPORT_GX: | |
1082 | case LINE6_TONEPORT_UX1: | |
1083 | case LINE6_TONEPORT_UX2: | |
1084 | case LINE6_GUITARPORT: | |
1027f476 | 1085 | line6_toneport_disconnect(interface); |
705ececd MG |
1086 | break; |
1087 | ||
1088 | default: | |
1089 | MISSING_CASE; | |
1090 | } | |
1091 | ||
e1a164d7 MG |
1092 | dev_info(&interface->dev, "Line6 %s now disconnected\n", |
1093 | line6->properties->name); | |
705ececd MG |
1094 | } |
1095 | ||
1096 | line6_destruct(interface); | |
1097 | ||
1098 | /* decrement reference counters: */ | |
1099 | usb_put_intf(interface); | |
1100 | usb_put_dev(usbdev); | |
1027f476 MG |
1101 | } |
1102 | ||
1103 | #ifdef CONFIG_PM | |
1104 | ||
1105 | /* | |
1106 | Suspend Line6 device. | |
1107 | */ | |
1108 | static int line6_suspend(struct usb_interface *interface, pm_message_t message) | |
1109 | { | |
1110 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
1111 | struct snd_line6_pcm *line6pcm = line6->line6pcm; | |
1112 | ||
1113 | snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); | |
1114 | ||
4cb1a4ae | 1115 | if (line6->properties->capabilities & LINE6_CAP_CONTROL) |
1027f476 MG |
1116 | line6_stop_listen(line6); |
1117 | ||
1118 | if (line6pcm != NULL) { | |
1119 | snd_pcm_suspend_all(line6pcm->pcm); | |
1120 | line6_pcm_disconnect(line6pcm); | |
1121 | line6pcm->flags = 0; | |
1122 | } | |
1123 | ||
1124 | return 0; | |
1125 | } | |
1126 | ||
1127 | /* | |
1128 | Resume Line6 device. | |
1129 | */ | |
1130 | static int line6_resume(struct usb_interface *interface) | |
1131 | { | |
1132 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
1133 | ||
4cb1a4ae | 1134 | if (line6->properties->capabilities & LINE6_CAP_CONTROL) |
1027f476 MG |
1135 | line6_start_listen(line6); |
1136 | ||
1137 | snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); | |
1138 | return 0; | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | Resume Line6 device after reset. | |
1143 | */ | |
1144 | static int line6_reset_resume(struct usb_interface *interface) | |
1145 | { | |
1146 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
705ececd | 1147 | |
a23a8bff CR |
1148 | switch (line6->type) { |
1149 | case LINE6_PODSTUDIO_GX: | |
1150 | case LINE6_PODSTUDIO_UX1: | |
1151 | case LINE6_PODSTUDIO_UX2: | |
1152 | case LINE6_TONEPORT_GX: | |
1153 | case LINE6_TONEPORT_UX1: | |
1154 | case LINE6_TONEPORT_UX2: | |
1155 | case LINE6_GUITARPORT: | |
1027f476 | 1156 | line6_toneport_reset_resume((struct usb_line6_toneport *)line6); |
a23a8bff CR |
1157 | |
1158 | default: | |
1159 | break; | |
1027f476 MG |
1160 | } |
1161 | ||
1162 | return line6_resume(interface); | |
705ececd MG |
1163 | } |
1164 | ||
e1a164d7 | 1165 | #endif /* CONFIG_PM */ |
1027f476 | 1166 | |
705ececd | 1167 | static struct usb_driver line6_driver = { |
705ececd MG |
1168 | .name = DRIVER_NAME, |
1169 | .probe = line6_probe, | |
1170 | .disconnect = line6_disconnect, | |
1027f476 MG |
1171 | #ifdef CONFIG_PM |
1172 | .suspend = line6_suspend, | |
1173 | .resume = line6_resume, | |
1174 | .reset_resume = line6_reset_resume, | |
1175 | #endif | |
705ececd MG |
1176 | .id_table = line6_id_table, |
1177 | }; | |
1178 | ||
4a631364 | 1179 | module_usb_driver(line6_driver); |
705ececd MG |
1180 | |
1181 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
1182 | MODULE_DESCRIPTION(DRIVER_DESC); | |
1183 | MODULE_LICENSE("GPL"); | |
1184 | MODULE_VERSION(DRIVER_VERSION); |