1
2
3
4
5
6
7
8
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
27
28enum {
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
38enum {
39 LINE6_PODXTLIVE_VARIAX,
40 LINE6_VARIAX
41};
42
43struct usb_line6_variax {
44
45 struct usb_line6 line6;
46
47
48 unsigned char *buffer_activate;
49
50
51 struct work_struct startup_work;
52
53
54 struct timer_list startup_timer1;
55 struct timer_list startup_timer2;
56
57
58 int startup_progress;
59};
60
61#define VARIAX_OFFSET_ACTIVATE 7
62
63
64
65
66
67static const char variax_init_version[] = {
68 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
69 0x07, 0x00, 0x00, 0x00
70};
71
72
73
74
75static const char variax_init_done[] = {
76 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
77};
78
79static const char variax_activate[] = {
80 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
81 0xf7
82};
83
84
85static void variax_startup2(unsigned long data);
86static void variax_startup4(unsigned long data);
87static void variax_startup5(unsigned long data);
88
89static 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
98
99
100
101
102
103static void variax_startup1(struct usb_line6_variax *variax)
104{
105 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
106
107
108 line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
109 variax_startup2, (unsigned long)variax);
110}
111
112static 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
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
126 line6_version_request_async(line6);
127}
128
129static void variax_startup3(struct usb_line6_variax *variax)
130{
131 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
132
133
134 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
135 variax_startup4, (unsigned long)variax);
136}
137
138static 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
146 variax_activate_async(variax, 1);
147 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
148 variax_startup5, (unsigned long)variax);
149}
150
151static 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
159 schedule_work(&variax->startup_work);
160}
161
162static 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
170 snd_card_register(variax->line6.card);
171}
172
173
174
175
176static 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
193 variax_startup4((unsigned long)variax);
194 }
195 break;
196 }
197}
198
199
200
201
202static 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
215
216static 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
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
237 err = line6_init_midi(&variax->line6);
238 if (err < 0)
239 return err;
240
241
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
250static 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
256MODULE_DEVICE_TABLE(usb, variax_id_table);
257
258static 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
279 }
280};
281
282
283
284
285static 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
293static 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
305module_usb_driver(variax_driver);
306
307MODULE_DESCRIPTION("Vairax Workbench USB driver");
308MODULE_LICENSE("GPL");
309