]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Line 6 Linux USB driver | |
3 | * | |
4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) | |
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 | ||
12 | #include <linux/slab.h> | |
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> | |
18 | ||
19 | #include "driver.h" | |
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 { | |
44 | /* Generic Line 6 USB data */ | |
45 | struct usb_line6 line6; | |
46 | ||
47 | /* Buffer for activation code */ | |
48 | unsigned char *buffer_activate; | |
49 | ||
50 | /* Handler for device initialization */ | |
51 | struct work_struct startup_work; | |
52 | ||
53 | /* Timers for device initialization */ | |
54 | struct timer_list startup_timer1; | |
55 | struct timer_list startup_timer2; | |
56 | ||
57 | /* Current progress in startup procedure */ | |
58 | int startup_progress; | |
59 | }; | |
60 | ||
61 | #define VARIAX_OFFSET_ACTIVATE 7 | |
62 | ||
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 | ||
79 | static const char variax_activate[] = { | |
80 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | |
81 | 0xf7 | |
82 | }; | |
83 | ||
84 | /* forward declarations: */ | |
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 | ||
89 | static void variax_activate_async(struct usb_line6_variax *variax, int a) | |
90 | { | |
91 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; | |
92 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, | |
93 | sizeof(variax_activate)); | |
94 | } | |
95 | ||
96 | /* | |
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. | |
101 | */ | |
102 | ||
103 | static void variax_startup1(struct usb_line6_variax *variax) | |
104 | { | |
105 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); | |
106 | ||
107 | /* delay startup procedure: */ | |
108 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, | |
109 | variax_startup2, (unsigned long)variax); | |
110 | } | |
111 | ||
112 | static void variax_startup2(unsigned long data) | |
113 | { | |
114 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
115 | struct usb_line6 *line6 = &variax->line6; | |
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); | |
124 | ||
125 | /* request firmware version: */ | |
126 | line6_version_request_async(line6); | |
127 | } | |
128 | ||
129 | static void variax_startup3(struct usb_line6_variax *variax) | |
130 | { | |
131 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); | |
132 | ||
133 | /* delay startup procedure: */ | |
134 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, | |
135 | variax_startup4, (unsigned long)variax); | |
136 | } | |
137 | ||
138 | static void variax_startup4(unsigned long data) | |
139 | { | |
140 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
141 | ||
142 | CHECK_STARTUP_PROGRESS(variax->startup_progress, | |
143 | VARIAX_STARTUP_ACTIVATE); | |
144 | ||
145 | /* activate device: */ | |
146 | variax_activate_async(variax, 1); | |
147 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, | |
148 | variax_startup5, (unsigned long)variax); | |
149 | } | |
150 | ||
151 | static void variax_startup5(unsigned long data) | |
152 | { | |
153 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
154 | ||
155 | CHECK_STARTUP_PROGRESS(variax->startup_progress, | |
156 | VARIAX_STARTUP_WORKQUEUE); | |
157 | ||
158 | /* schedule work for global work queue: */ | |
159 | schedule_work(&variax->startup_work); | |
160 | } | |
161 | ||
162 | static void variax_startup6(struct work_struct *work) | |
163 | { | |
164 | struct usb_line6_variax *variax = | |
165 | container_of(work, struct usb_line6_variax, startup_work); | |
166 | ||
167 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); | |
168 | ||
169 | /* ALSA audio interface: */ | |
170 | snd_card_register(variax->line6.card); | |
171 | } | |
172 | ||
173 | /* | |
174 | Process a completely received message. | |
175 | */ | |
176 | static void line6_variax_process_message(struct usb_line6 *line6) | |
177 | { | |
178 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; | |
179 | const unsigned char *buf = variax->line6.buffer_message; | |
180 | ||
181 | switch (buf[0]) { | |
182 | case LINE6_RESET: | |
183 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | |
184 | break; | |
185 | ||
186 | case LINE6_SYSEX_BEGIN: | |
187 | if (memcmp(buf + 1, variax_init_version + 1, | |
188 | sizeof(variax_init_version) - 1) == 0) { | |
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); | |
194 | } | |
195 | break; | |
196 | } | |
197 | } | |
198 | ||
199 | /* | |
200 | Variax destructor. | |
201 | */ | |
202 | static void line6_variax_disconnect(struct usb_line6 *line6) | |
203 | { | |
204 | struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; | |
205 | ||
206 | del_timer(&variax->startup_timer1); | |
207 | del_timer(&variax->startup_timer2); | |
208 | cancel_work_sync(&variax->startup_work); | |
209 | ||
210 | kfree(variax->buffer_activate); | |
211 | } | |
212 | ||
213 | /* | |
214 | Try to init workbench device. | |
215 | */ | |
216 | static int variax_init(struct usb_line6 *line6, | |
217 | const struct usb_device_id *id) | |
218 | { | |
219 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; | |
220 | int err; | |
221 | ||
222 | line6->process_message = line6_variax_process_message; | |
223 | line6->disconnect = line6_variax_disconnect; | |
224 | ||
225 | init_timer(&variax->startup_timer1); | |
226 | init_timer(&variax->startup_timer2); | |
227 | INIT_WORK(&variax->startup_work, variax_startup6); | |
228 | ||
229 | /* initialize USB buffers: */ | |
230 | variax->buffer_activate = kmemdup(variax_activate, | |
231 | sizeof(variax_activate), GFP_KERNEL); | |
232 | ||
233 | if (variax->buffer_activate == NULL) | |
234 | return -ENOMEM; | |
235 | ||
236 | /* initialize MIDI subsystem: */ | |
237 | err = line6_init_midi(&variax->line6); | |
238 | if (err < 0) | |
239 | return err; | |
240 | ||
241 | /* initiate startup procedure: */ | |
242 | variax_startup1(variax); | |
243 | return 0; | |
244 | } | |
245 | ||
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", | |
262 | .capabilities = LINE6_CAP_CONTROL | |
263 | | LINE6_CAP_CONTROL_MIDI, | |
264 | .altsetting = 1, | |
265 | .ep_ctrl_r = 0x86, | |
266 | .ep_ctrl_w = 0x05, | |
267 | .ep_audio_r = 0x82, | |
268 | .ep_audio_w = 0x01, | |
269 | }, | |
270 | [LINE6_VARIAX] = { | |
271 | .id = "Variax", | |
272 | .name = "Variax Workbench", | |
273 | .capabilities = LINE6_CAP_CONTROL | |
274 | | LINE6_CAP_CONTROL_MIDI, | |
275 | .altsetting = 1, | |
276 | .ep_ctrl_r = 0x82, | |
277 | .ep_ctrl_w = 0x01, | |
278 | /* no audio channel */ | |
279 | } | |
280 | }; | |
281 | ||
282 | /* | |
283 | Probe USB device. | |
284 | */ | |
285 | static int variax_probe(struct usb_interface *interface, | |
286 | const struct usb_device_id *id) | |
287 | { | |
288 | return line6_probe(interface, id, "Line6-Variax", | |
289 | &variax_properties_table[id->driver_info], | |
290 | variax_init, sizeof(struct usb_line6_variax)); | |
291 | } | |
292 | ||
293 | static struct usb_driver variax_driver = { | |
294 | .name = KBUILD_MODNAME, | |
295 | .probe = variax_probe, | |
296 | .disconnect = line6_disconnect, | |
297 | #ifdef CONFIG_PM | |
298 | .suspend = line6_suspend, | |
299 | .resume = line6_resume, | |
300 | .reset_resume = line6_resume, | |
301 | #endif | |
302 | .id_table = variax_id_table, | |
303 | }; | |
304 | ||
305 | module_usb_driver(variax_driver); | |
306 | ||
307 | MODULE_DESCRIPTION("Vairax Workbench USB driver"); | |
308 | MODULE_LICENSE("GPL"); |