]>
Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
c078a4aa | 2 | * Line 6 Linux USB driver |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner ([email protected]) |
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 | ||
5a0e3ad6 | 12 | #include <linux/slab.h> |
ccddbe4a TI |
13 | #include <linux/spinlock.h> |
14 | #include <linux/usb.h> | |
15 | #include <linux/wait.h> | |
16 | #include <linux/module.h> | |
17 | #include <sound/core.h> | |
5a0e3ad6 | 18 | |
1027f476 | 19 | #include "driver.h" |
ccddbe4a TI |
20 | |
21 | #define VARIAX_STARTUP_DELAY1 1000 | |
22 | #define VARIAX_STARTUP_DELAY3 100 | |
23 | #define VARIAX_STARTUP_DELAY4 100 | |
24 | ||
25 | /* | |
26 | Stages of Variax startup procedure | |
27 | */ | |
28 | enum { | |
29 | VARIAX_STARTUP_INIT = 1, | |
30 | VARIAX_STARTUP_VERSIONREQ, | |
31 | VARIAX_STARTUP_WAIT, | |
32 | VARIAX_STARTUP_ACTIVATE, | |
33 | VARIAX_STARTUP_WORKQUEUE, | |
34 | VARIAX_STARTUP_SETUP, | |
35 | VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 | |
36 | }; | |
37 | ||
38 | enum { | |
39 | LINE6_PODXTLIVE_VARIAX, | |
40 | LINE6_VARIAX | |
41 | }; | |
42 | ||
43 | struct usb_line6_variax { | |
cddbd4f1 | 44 | /* Generic Line 6 USB data */ |
ccddbe4a TI |
45 | struct usb_line6 line6; |
46 | ||
cddbd4f1 | 47 | /* Buffer for activation code */ |
ccddbe4a TI |
48 | unsigned char *buffer_activate; |
49 | ||
cddbd4f1 | 50 | /* Handler for device initialization */ |
ccddbe4a TI |
51 | struct work_struct startup_work; |
52 | ||
cddbd4f1 | 53 | /* Timers for device initialization */ |
ccddbe4a TI |
54 | struct timer_list startup_timer1; |
55 | struct timer_list startup_timer2; | |
56 | ||
cddbd4f1 | 57 | /* Current progress in startup procedure */ |
ccddbe4a TI |
58 | int startup_progress; |
59 | }; | |
705ececd | 60 | |
705ececd MG |
61 | #define VARIAX_OFFSET_ACTIVATE 7 |
62 | ||
1027f476 MG |
63 | /* |
64 | This message is sent by the device during initialization and identifies | |
65 | the connected guitar version. | |
66 | */ | |
67 | static const char variax_init_version[] = { | |
68 | 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, | |
69 | 0x07, 0x00, 0x00, 0x00 | |
70 | }; | |
71 | ||
72 | /* | |
73 | This message is the last one sent by the device during initialization. | |
74 | */ | |
75 | static const char variax_init_done[] = { | |
76 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b | |
77 | }; | |
78 | ||
705ececd MG |
79 | static const char variax_activate[] = { |
80 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | |
81 | 0xf7 | |
82 | }; | |
1027f476 | 83 | |
1027f476 | 84 | /* forward declarations: */ |
1027f476 MG |
85 | static void variax_startup2(unsigned long data); |
86 | static void variax_startup4(unsigned long data); | |
87 | static void variax_startup5(unsigned long data); | |
88 | ||
1027f476 | 89 | static void variax_activate_async(struct usb_line6_variax *variax, int a) |
705ececd | 90 | { |
1027f476 | 91 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; |
9cd57f77 GKH |
92 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, |
93 | sizeof(variax_activate)); | |
705ececd MG |
94 | } |
95 | ||
96 | /* | |
1027f476 MG |
97 | Variax startup procedure. |
98 | This is a sequence of functions with special requirements (e.g., must | |
99 | not run immediately after initialization, must not run in interrupt | |
100 | context). After the last one has finished, the device is ready to use. | |
705ececd | 101 | */ |
1027f476 MG |
102 | |
103 | static void variax_startup1(struct usb_line6_variax *variax) | |
705ececd | 104 | { |
e1a164d7 | 105 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); |
1027f476 MG |
106 | |
107 | /* delay startup procedure: */ | |
e1a164d7 MG |
108 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, |
109 | variax_startup2, (unsigned long)variax); | |
705ececd MG |
110 | } |
111 | ||
1027f476 | 112 | static void variax_startup2(unsigned long data) |
705ececd | 113 | { |
1027f476 MG |
114 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; |
115 | struct usb_line6 *line6 = &variax->line6; | |
e1a164d7 MG |
116 | |
117 | /* schedule another startup procedure until startup is complete: */ | |
118 | if (variax->startup_progress >= VARIAX_STARTUP_LAST) | |
119 | return; | |
120 | ||
121 | variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; | |
122 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, | |
123 | variax_startup2, (unsigned long)variax); | |
705ececd | 124 | |
1027f476 MG |
125 | /* request firmware version: */ |
126 | line6_version_request_async(line6); | |
127 | } | |
705ececd | 128 | |
1027f476 MG |
129 | static void variax_startup3(struct usb_line6_variax *variax) |
130 | { | |
e1a164d7 | 131 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); |
1027f476 MG |
132 | |
133 | /* delay startup procedure: */ | |
e1a164d7 MG |
134 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, |
135 | variax_startup4, (unsigned long)variax); | |
1027f476 MG |
136 | } |
137 | ||
138 | static void variax_startup4(unsigned long data) | |
139 | { | |
140 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
f3c5261e | 141 | |
e1a164d7 MG |
142 | CHECK_STARTUP_PROGRESS(variax->startup_progress, |
143 | VARIAX_STARTUP_ACTIVATE); | |
1027f476 MG |
144 | |
145 | /* activate device: */ | |
146 | variax_activate_async(variax, 1); | |
e1a164d7 MG |
147 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, |
148 | variax_startup5, (unsigned long)variax); | |
1027f476 MG |
149 | } |
150 | ||
151 | static void variax_startup5(unsigned long data) | |
152 | { | |
153 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
f3c5261e | 154 | |
e1a164d7 MG |
155 | CHECK_STARTUP_PROGRESS(variax->startup_progress, |
156 | VARIAX_STARTUP_WORKQUEUE); | |
1027f476 MG |
157 | |
158 | /* schedule work for global work queue: */ | |
159 | schedule_work(&variax->startup_work); | |
160 | } | |
161 | ||
323246b2 | 162 | static void variax_startup6(struct work_struct *work) |
1027f476 | 163 | { |
e1a164d7 MG |
164 | struct usb_line6_variax *variax = |
165 | container_of(work, struct usb_line6_variax, startup_work); | |
1027f476 | 166 | |
e1a164d7 | 167 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); |
1027f476 MG |
168 | |
169 | /* ALSA audio interface: */ | |
85a9339b | 170 | snd_card_register(variax->line6.card); |
705ececd MG |
171 | } |
172 | ||
173 | /* | |
174 | Process a completely received message. | |
175 | */ | |
01f6b2bc | 176 | static void line6_variax_process_message(struct usb_line6 *line6) |
705ececd | 177 | { |
1cad3e8d | 178 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; |
705ececd MG |
179 | const unsigned char *buf = variax->line6.buffer_message; |
180 | ||
9cd57f77 | 181 | switch (buf[0]) { |
705ececd MG |
182 | case LINE6_RESET: |
183 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | |
705ececd MG |
184 | break; |
185 | ||
186 | case LINE6_SYSEX_BEGIN: | |
323246b2 SH |
187 | if (memcmp(buf + 1, variax_init_version + 1, |
188 | sizeof(variax_init_version) - 1) == 0) { | |
1027f476 MG |
189 | variax_startup3(variax); |
190 | } else if (memcmp(buf + 1, variax_init_done + 1, | |
191 | sizeof(variax_init_done) - 1) == 0) { | |
192 | /* notify of complete initialization: */ | |
193 | variax_startup4((unsigned long)variax); | |
705ececd | 194 | } |
705ececd | 195 | break; |
705ececd MG |
196 | } |
197 | } | |
198 | ||
705ececd MG |
199 | /* |
200 | Variax destructor. | |
201 | */ | |
f66fd990 | 202 | static void line6_variax_disconnect(struct usb_line6 *line6) |
705ececd | 203 | { |
f66fd990 | 204 | struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; |
705ececd | 205 | |
e1a164d7 MG |
206 | del_timer(&variax->startup_timer1); |
207 | del_timer(&variax->startup_timer2); | |
208 | cancel_work_sync(&variax->startup_work); | |
209 | ||
9cd57f77 | 210 | kfree(variax->buffer_activate); |
705ececd MG |
211 | } |
212 | ||
705ececd | 213 | /* |
1027f476 | 214 | Try to init workbench device. |
705ececd | 215 | */ |
f66fd990 TI |
216 | static int variax_init(struct usb_line6 *line6, |
217 | const struct usb_device_id *id) | |
705ececd | 218 | { |
a221dd45 | 219 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; |
705ececd MG |
220 | int err; |
221 | ||
01f6b2bc | 222 | line6->process_message = line6_variax_process_message; |
a46c4672 | 223 | line6->disconnect = line6_variax_disconnect; |
01f6b2bc | 224 | |
e1a164d7 MG |
225 | init_timer(&variax->startup_timer1); |
226 | init_timer(&variax->startup_timer2); | |
323246b2 | 227 | INIT_WORK(&variax->startup_work, variax_startup6); |
e1a164d7 | 228 | |
705ececd | 229 | /* initialize USB buffers: */ |
94002c07 JL |
230 | variax->buffer_activate = kmemdup(variax_activate, |
231 | sizeof(variax_activate), GFP_KERNEL); | |
705ececd | 232 | |
a019f5e8 | 233 | if (variax->buffer_activate == NULL) |
705ececd | 234 | return -ENOMEM; |
705ececd | 235 | |
705ececd | 236 | /* initialize MIDI subsystem: */ |
9cd57f77 | 237 | err = line6_init_midi(&variax->line6); |
027360c5 | 238 | if (err < 0) |
705ececd | 239 | return err; |
705ececd | 240 | |
1027f476 MG |
241 | /* initiate startup procedure: */ |
242 | variax_startup1(variax); | |
243 | return 0; | |
244 | } | |
245 | ||
ccddbe4a TI |
246 | #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) |
247 | #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) | |
248 | ||
249 | /* table of devices that work with this driver */ | |
250 | static const struct usb_device_id variax_id_table[] = { | |
251 | { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, | |
252 | { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, | |
253 | {} | |
254 | }; | |
255 | ||
256 | MODULE_DEVICE_TABLE(usb, variax_id_table); | |
257 | ||
258 | static const struct line6_properties variax_properties_table[] = { | |
259 | [LINE6_PODXTLIVE_VARIAX] = { | |
260 | .id = "PODxtLive", | |
261 | .name = "PODxt Live", | |
72f18d00 | 262 | .capabilities = LINE6_CAP_CONTROL, |
ccddbe4a TI |
263 | .altsetting = 1, |
264 | .ep_ctrl_r = 0x86, | |
265 | .ep_ctrl_w = 0x05, | |
266 | .ep_audio_r = 0x82, | |
267 | .ep_audio_w = 0x01, | |
268 | }, | |
269 | [LINE6_VARIAX] = { | |
270 | .id = "Variax", | |
271 | .name = "Variax Workbench", | |
272 | .capabilities = LINE6_CAP_CONTROL, | |
273 | .altsetting = 1, | |
274 | .ep_ctrl_r = 0x82, | |
275 | .ep_ctrl_w = 0x01, | |
276 | /* no audio channel */ | |
277 | } | |
278 | }; | |
279 | ||
280 | /* | |
281 | Probe USB device. | |
282 | */ | |
283 | static int variax_probe(struct usb_interface *interface, | |
284 | const struct usb_device_id *id) | |
285 | { | |
12865cac | 286 | return line6_probe(interface, id, "Line6-Variax", |
85a9339b | 287 | &variax_properties_table[id->driver_info], |
aca514b8 | 288 | variax_init, sizeof(struct usb_line6_variax)); |
ccddbe4a TI |
289 | } |
290 | ||
291 | static struct usb_driver variax_driver = { | |
292 | .name = KBUILD_MODNAME, | |
293 | .probe = variax_probe, | |
294 | .disconnect = line6_disconnect, | |
295 | #ifdef CONFIG_PM | |
296 | .suspend = line6_suspend, | |
297 | .resume = line6_resume, | |
298 | .reset_resume = line6_resume, | |
299 | #endif | |
300 | .id_table = variax_id_table, | |
301 | }; | |
302 | ||
303 | module_usb_driver(variax_driver); | |
304 | ||
305 | MODULE_DESCRIPTION("Vairax Workbench USB driver"); | |
306 | MODULE_LICENSE("GPL"); |