Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
c078a4aa | 2 | * Line 6 Linux USB driver |
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 | ||
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 { | |
ccddbe4a | 29 | VARIAX_STARTUP_VERSIONREQ, |
ccddbe4a | 30 | VARIAX_STARTUP_ACTIVATE, |
ccddbe4a | 31 | VARIAX_STARTUP_SETUP, |
ccddbe4a TI |
32 | }; |
33 | ||
34 | enum { | |
35 | LINE6_PODXTLIVE_VARIAX, | |
36 | LINE6_VARIAX | |
37 | }; | |
38 | ||
39 | struct usb_line6_variax { | |
cddbd4f1 | 40 | /* Generic Line 6 USB data */ |
ccddbe4a TI |
41 | struct usb_line6 line6; |
42 | ||
cddbd4f1 | 43 | /* Buffer for activation code */ |
ccddbe4a TI |
44 | unsigned char *buffer_activate; |
45 | ||
cddbd4f1 | 46 | /* Current progress in startup procedure */ |
ccddbe4a TI |
47 | int startup_progress; |
48 | }; | |
705ececd | 49 | |
705ececd MG |
50 | #define VARIAX_OFFSET_ACTIVATE 7 |
51 | ||
1027f476 MG |
52 | /* |
53 | This message is sent by the device during initialization and identifies | |
54 | the connected guitar version. | |
55 | */ | |
56 | static const char variax_init_version[] = { | |
57 | 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, | |
58 | 0x07, 0x00, 0x00, 0x00 | |
59 | }; | |
60 | ||
61 | /* | |
62 | This message is the last one sent by the device during initialization. | |
63 | */ | |
64 | static const char variax_init_done[] = { | |
65 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b | |
66 | }; | |
67 | ||
705ececd MG |
68 | static const char variax_activate[] = { |
69 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | |
70 | 0xf7 | |
71 | }; | |
1027f476 | 72 | |
1027f476 | 73 | static void variax_activate_async(struct usb_line6_variax *variax, int a) |
705ececd | 74 | { |
1027f476 | 75 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; |
9cd57f77 GKH |
76 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, |
77 | sizeof(variax_activate)); | |
705ececd MG |
78 | } |
79 | ||
80 | /* | |
1027f476 MG |
81 | Variax startup procedure. |
82 | This is a sequence of functions with special requirements (e.g., must | |
83 | not run immediately after initialization, must not run in interrupt | |
84 | context). After the last one has finished, the device is ready to use. | |
705ececd | 85 | */ |
1027f476 | 86 | |
6ea53391 | 87 | static void variax_startup(struct usb_line6 *line6) |
1027f476 | 88 | { |
6ea53391 | 89 | struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; |
1027f476 | 90 | |
6ea53391 TI |
91 | switch (variax->startup_progress) { |
92 | case VARIAX_STARTUP_VERSIONREQ: | |
93 | /* repeat request until getting the response */ | |
94 | schedule_delayed_work(&line6->startup_work, | |
95 | msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); | |
96 | /* request firmware version: */ | |
97 | line6_version_request_async(line6); | |
98 | break; | |
99 | case VARIAX_STARTUP_ACTIVATE: | |
100 | /* activate device: */ | |
101 | variax_activate_async(variax, 1); | |
102 | variax->startup_progress = VARIAX_STARTUP_SETUP; | |
103 | schedule_delayed_work(&line6->startup_work, | |
104 | msecs_to_jiffies(VARIAX_STARTUP_DELAY4)); | |
105 | break; | |
106 | case VARIAX_STARTUP_SETUP: | |
107 | /* ALSA audio interface: */ | |
108 | snd_card_register(variax->line6.card); | |
109 | break; | |
110 | } | |
705ececd MG |
111 | } |
112 | ||
113 | /* | |
114 | Process a completely received message. | |
115 | */ | |
01f6b2bc | 116 | static void line6_variax_process_message(struct usb_line6 *line6) |
705ececd | 117 | { |
1cad3e8d | 118 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; |
705ececd MG |
119 | const unsigned char *buf = variax->line6.buffer_message; |
120 | ||
9cd57f77 | 121 | switch (buf[0]) { |
705ececd MG |
122 | case LINE6_RESET: |
123 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | |
705ececd MG |
124 | break; |
125 | ||
126 | case LINE6_SYSEX_BEGIN: | |
323246b2 SH |
127 | if (memcmp(buf + 1, variax_init_version + 1, |
128 | sizeof(variax_init_version) - 1) == 0) { | |
6ea53391 TI |
129 | if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE) |
130 | break; | |
131 | variax->startup_progress = VARIAX_STARTUP_ACTIVATE; | |
132 | cancel_delayed_work(&line6->startup_work); | |
133 | schedule_delayed_work(&line6->startup_work, | |
134 | msecs_to_jiffies(VARIAX_STARTUP_DELAY3)); | |
1027f476 MG |
135 | } else if (memcmp(buf + 1, variax_init_done + 1, |
136 | sizeof(variax_init_done) - 1) == 0) { | |
137 | /* notify of complete initialization: */ | |
6ea53391 TI |
138 | if (variax->startup_progress >= VARIAX_STARTUP_SETUP) |
139 | break; | |
140 | cancel_delayed_work(&line6->startup_work); | |
141 | schedule_delayed_work(&line6->startup_work, 0); | |
705ececd | 142 | } |
705ececd | 143 | break; |
705ececd MG |
144 | } |
145 | } | |
146 | ||
705ececd MG |
147 | /* |
148 | Variax destructor. | |
149 | */ | |
f66fd990 | 150 | static void line6_variax_disconnect(struct usb_line6 *line6) |
705ececd | 151 | { |
f66fd990 | 152 | struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; |
705ececd | 153 | |
9cd57f77 | 154 | kfree(variax->buffer_activate); |
705ececd MG |
155 | } |
156 | ||
705ececd | 157 | /* |
1027f476 | 158 | Try to init workbench device. |
705ececd | 159 | */ |
f66fd990 TI |
160 | static int variax_init(struct usb_line6 *line6, |
161 | const struct usb_device_id *id) | |
705ececd | 162 | { |
a221dd45 | 163 | struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; |
705ececd MG |
164 | int err; |
165 | ||
01f6b2bc | 166 | line6->process_message = line6_variax_process_message; |
a46c4672 | 167 | line6->disconnect = line6_variax_disconnect; |
6ea53391 | 168 | line6->startup = variax_startup; |
e1a164d7 | 169 | |
705ececd | 170 | /* initialize USB buffers: */ |
94002c07 JL |
171 | variax->buffer_activate = kmemdup(variax_activate, |
172 | sizeof(variax_activate), GFP_KERNEL); | |
705ececd | 173 | |
a019f5e8 | 174 | if (variax->buffer_activate == NULL) |
705ececd | 175 | return -ENOMEM; |
705ececd | 176 | |
705ececd | 177 | /* initialize MIDI subsystem: */ |
9cd57f77 | 178 | err = line6_init_midi(&variax->line6); |
027360c5 | 179 | if (err < 0) |
705ececd | 180 | return err; |
705ececd | 181 | |
1027f476 | 182 | /* initiate startup procedure: */ |
6ea53391 TI |
183 | schedule_delayed_work(&line6->startup_work, |
184 | msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); | |
1027f476 MG |
185 | return 0; |
186 | } | |
187 | ||
ccddbe4a TI |
188 | #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) |
189 | #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) | |
190 | ||
191 | /* table of devices that work with this driver */ | |
192 | static const struct usb_device_id variax_id_table[] = { | |
193 | { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, | |
194 | { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, | |
195 | {} | |
196 | }; | |
197 | ||
198 | MODULE_DEVICE_TABLE(usb, variax_id_table); | |
199 | ||
200 | static const struct line6_properties variax_properties_table[] = { | |
201 | [LINE6_PODXTLIVE_VARIAX] = { | |
202 | .id = "PODxtLive", | |
203 | .name = "PODxt Live", | |
174e1fc0 AK |
204 | .capabilities = LINE6_CAP_CONTROL |
205 | | LINE6_CAP_CONTROL_MIDI, | |
ccddbe4a TI |
206 | .altsetting = 1, |
207 | .ep_ctrl_r = 0x86, | |
208 | .ep_ctrl_w = 0x05, | |
209 | .ep_audio_r = 0x82, | |
210 | .ep_audio_w = 0x01, | |
211 | }, | |
212 | [LINE6_VARIAX] = { | |
213 | .id = "Variax", | |
214 | .name = "Variax Workbench", | |
174e1fc0 AK |
215 | .capabilities = LINE6_CAP_CONTROL |
216 | | LINE6_CAP_CONTROL_MIDI, | |
ccddbe4a TI |
217 | .altsetting = 1, |
218 | .ep_ctrl_r = 0x82, | |
219 | .ep_ctrl_w = 0x01, | |
220 | /* no audio channel */ | |
221 | } | |
222 | }; | |
223 | ||
224 | /* | |
225 | Probe USB device. | |
226 | */ | |
227 | static int variax_probe(struct usb_interface *interface, | |
228 | const struct usb_device_id *id) | |
229 | { | |
12865cac | 230 | return line6_probe(interface, id, "Line6-Variax", |
85a9339b | 231 | &variax_properties_table[id->driver_info], |
aca514b8 | 232 | variax_init, sizeof(struct usb_line6_variax)); |
ccddbe4a TI |
233 | } |
234 | ||
235 | static struct usb_driver variax_driver = { | |
236 | .name = KBUILD_MODNAME, | |
237 | .probe = variax_probe, | |
238 | .disconnect = line6_disconnect, | |
239 | #ifdef CONFIG_PM | |
240 | .suspend = line6_suspend, | |
241 | .resume = line6_resume, | |
242 | .reset_resume = line6_resume, | |
243 | #endif | |
244 | .id_table = variax_id_table, | |
245 | }; | |
246 | ||
247 | module_usb_driver(variax_driver); | |
248 | ||
249 | MODULE_DESCRIPTION("Vairax Workbench USB driver"); | |
250 | MODULE_LICENSE("GPL"); |