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 | ||
5a0e3ad6 TH |
12 | #include <linux/slab.h> |
13 | ||
705ececd MG |
14 | #include "audio.h" |
15 | #include "control.h" | |
1027f476 | 16 | #include "driver.h" |
705ececd MG |
17 | #include "variax.h" |
18 | ||
705ececd MG |
19 | #define VARIAX_SYSEX_CODE 7 |
20 | #define VARIAX_SYSEX_PARAM 0x3b | |
21 | #define VARIAX_SYSEX_ACTIVATE 0x2a | |
22 | #define VARIAX_MODEL_HEADER_LENGTH 7 | |
23 | #define VARIAX_MODEL_MESSAGE_LENGTH 199 | |
24 | #define VARIAX_OFFSET_ACTIVATE 7 | |
25 | ||
1027f476 MG |
26 | /* |
27 | This message is sent by the device during initialization and identifies | |
28 | the connected guitar model. | |
29 | */ | |
30 | static const char variax_init_model[] = { | |
31 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02, | |
32 | 0x00 | |
33 | }; | |
34 | ||
35 | /* | |
36 | This message is sent by the device during initialization and identifies | |
37 | the connected guitar version. | |
38 | */ | |
39 | static const char variax_init_version[] = { | |
40 | 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, | |
41 | 0x07, 0x00, 0x00, 0x00 | |
42 | }; | |
43 | ||
44 | /* | |
45 | This message is the last one sent by the device during initialization. | |
46 | */ | |
47 | static const char variax_init_done[] = { | |
48 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b | |
49 | }; | |
50 | ||
705ececd MG |
51 | static const char variax_activate[] = { |
52 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | |
53 | 0xf7 | |
54 | }; | |
1027f476 | 55 | |
705ececd MG |
56 | static const char variax_request_bank[] = { |
57 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 | |
58 | }; | |
1027f476 | 59 | |
705ececd MG |
60 | static const char variax_request_model1[] = { |
61 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | |
62 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, | |
63 | 0x00, 0x00, 0x00, 0xf7 | |
64 | }; | |
1027f476 | 65 | |
705ececd MG |
66 | static const char variax_request_model2[] = { |
67 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | |
68 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, | |
69 | 0x00, 0x00, 0x00, 0xf7 | |
70 | }; | |
71 | ||
1027f476 MG |
72 | /* forward declarations: */ |
73 | static int variax_create_files2(struct device *dev); | |
74 | static void variax_startup2(unsigned long data); | |
75 | static void variax_startup4(unsigned long data); | |
76 | static void variax_startup5(unsigned long data); | |
77 | ||
705ececd MG |
78 | /* |
79 | Decode data transmitted by workbench. | |
80 | */ | |
9cd57f77 GKH |
81 | static void variax_decode(const unsigned char *raw_data, unsigned char *data, |
82 | int raw_size) | |
705ececd | 83 | { |
9cd57f77 | 84 | for (; raw_size > 0; raw_size -= 6) { |
705ececd MG |
85 | data[2] = raw_data[0] | (raw_data[1] << 4); |
86 | data[1] = raw_data[2] | (raw_data[3] << 4); | |
87 | data[0] = raw_data[4] | (raw_data[5] << 4); | |
88 | raw_data += 6; | |
89 | data += 3; | |
90 | } | |
91 | } | |
92 | ||
1027f476 | 93 | static void variax_activate_async(struct usb_line6_variax *variax, int a) |
705ececd | 94 | { |
1027f476 | 95 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; |
9cd57f77 GKH |
96 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, |
97 | sizeof(variax_activate)); | |
705ececd MG |
98 | } |
99 | ||
100 | /* | |
1027f476 MG |
101 | Variax startup procedure. |
102 | This is a sequence of functions with special requirements (e.g., must | |
103 | not run immediately after initialization, must not run in interrupt | |
104 | context). After the last one has finished, the device is ready to use. | |
705ececd | 105 | */ |
1027f476 MG |
106 | |
107 | static void variax_startup1(struct usb_line6_variax *variax) | |
705ececd | 108 | { |
e1a164d7 | 109 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); |
1027f476 MG |
110 | |
111 | /* delay startup procedure: */ | |
e1a164d7 MG |
112 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, |
113 | variax_startup2, (unsigned long)variax); | |
705ececd MG |
114 | } |
115 | ||
1027f476 | 116 | static void variax_startup2(unsigned long data) |
705ececd | 117 | { |
1027f476 MG |
118 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; |
119 | struct usb_line6 *line6 = &variax->line6; | |
e1a164d7 MG |
120 | |
121 | /* schedule another startup procedure until startup is complete: */ | |
122 | if (variax->startup_progress >= VARIAX_STARTUP_LAST) | |
123 | return; | |
124 | ||
125 | variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; | |
126 | line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, | |
127 | variax_startup2, (unsigned long)variax); | |
705ececd | 128 | |
1027f476 MG |
129 | /* request firmware version: */ |
130 | line6_version_request_async(line6); | |
131 | } | |
705ececd | 132 | |
1027f476 MG |
133 | static void variax_startup3(struct usb_line6_variax *variax) |
134 | { | |
e1a164d7 | 135 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); |
1027f476 MG |
136 | |
137 | /* delay startup procedure: */ | |
e1a164d7 MG |
138 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, |
139 | variax_startup4, (unsigned long)variax); | |
1027f476 MG |
140 | } |
141 | ||
142 | static void variax_startup4(unsigned long data) | |
143 | { | |
144 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
e1a164d7 MG |
145 | CHECK_STARTUP_PROGRESS(variax->startup_progress, |
146 | VARIAX_STARTUP_ACTIVATE); | |
1027f476 MG |
147 | |
148 | /* activate device: */ | |
149 | variax_activate_async(variax, 1); | |
e1a164d7 MG |
150 | line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, |
151 | variax_startup5, (unsigned long)variax); | |
1027f476 MG |
152 | } |
153 | ||
154 | static void variax_startup5(unsigned long data) | |
155 | { | |
156 | struct usb_line6_variax *variax = (struct usb_line6_variax *)data; | |
e1a164d7 MG |
157 | CHECK_STARTUP_PROGRESS(variax->startup_progress, |
158 | VARIAX_STARTUP_DUMPREQ); | |
1027f476 MG |
159 | |
160 | /* current model dump: */ | |
e1a164d7 MG |
161 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, |
162 | VARIAX_DUMP_PASS1); | |
1027f476 MG |
163 | /* passes 2 and 3 are performed implicitly before entering variax_startup6 */ |
164 | } | |
165 | ||
166 | static void variax_startup6(struct usb_line6_variax *variax) | |
167 | { | |
e1a164d7 MG |
168 | CHECK_STARTUP_PROGRESS(variax->startup_progress, |
169 | VARIAX_STARTUP_WORKQUEUE); | |
1027f476 MG |
170 | |
171 | /* schedule work for global work queue: */ | |
172 | schedule_work(&variax->startup_work); | |
173 | } | |
174 | ||
175 | static void variax_startup7(struct work_struct *work) | |
176 | { | |
e1a164d7 MG |
177 | struct usb_line6_variax *variax = |
178 | container_of(work, struct usb_line6_variax, startup_work); | |
1027f476 MG |
179 | struct usb_line6 *line6 = &variax->line6; |
180 | ||
e1a164d7 | 181 | CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); |
1027f476 MG |
182 | |
183 | /* ALSA audio interface: */ | |
184 | line6_register_audio(&variax->line6); | |
185 | ||
186 | /* device files: */ | |
187 | line6_variax_create_files(0, 0, line6->ifcdev); | |
188 | variax_create_files2(line6->ifcdev); | |
705ececd MG |
189 | } |
190 | ||
191 | /* | |
192 | Process a completely received message. | |
193 | */ | |
1027f476 | 194 | void line6_variax_process_message(struct usb_line6_variax *variax) |
705ececd MG |
195 | { |
196 | const unsigned char *buf = variax->line6.buffer_message; | |
197 | ||
9cd57f77 | 198 | switch (buf[0]) { |
705ececd | 199 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: |
9cd57f77 | 200 | switch (buf[1]) { |
705ececd MG |
201 | case VARIAXMIDI_volume: |
202 | variax->volume = buf[2]; | |
203 | break; | |
204 | ||
205 | case VARIAXMIDI_tone: | |
206 | variax->tone = buf[2]; | |
207 | } | |
208 | ||
209 | break; | |
210 | ||
211 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | |
212 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | |
213 | variax->model = buf[1]; | |
e1a164d7 MG |
214 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0, |
215 | VARIAX_DUMP_PASS1); | |
705ececd MG |
216 | break; |
217 | ||
218 | case LINE6_RESET: | |
219 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | |
705ececd MG |
220 | break; |
221 | ||
222 | case LINE6_SYSEX_BEGIN: | |
9cd57f77 GKH |
223 | if (memcmp(buf + 1, variax_request_model1 + 1, |
224 | VARIAX_MODEL_HEADER_LENGTH - 1) == 0) { | |
225 | if (variax->line6.message_length == | |
226 | VARIAX_MODEL_MESSAGE_LENGTH) { | |
227 | switch (variax->dumpreq.in_progress) { | |
705ececd | 228 | case VARIAX_DUMP_PASS1: |
e1a164d7 MG |
229 | variax_decode(buf + |
230 | VARIAX_MODEL_HEADER_LENGTH, | |
231 | (unsigned char *) | |
232 | &variax->model_data, | |
233 | (sizeof | |
234 | (variax->model_data. | |
235 | name) + | |
236 | sizeof(variax-> | |
237 | model_data. | |
238 | control) | |
239 | / 2) * 2); | |
240 | line6_dump_request_async | |
241 | (&variax->dumpreq, &variax->line6, | |
242 | 1, VARIAX_DUMP_PASS2); | |
705ececd MG |
243 | break; |
244 | ||
245 | case VARIAX_DUMP_PASS2: | |
246 | /* model name is transmitted twice, so skip it here: */ | |
e1a164d7 MG |
247 | variax_decode(buf + |
248 | VARIAX_MODEL_HEADER_LENGTH, | |
249 | (unsigned char *) | |
250 | &variax-> | |
251 | model_data.control + | |
252 | sizeof(variax->model_data. | |
253 | control) | |
254 | / 2, | |
255 | sizeof(variax->model_data. | |
256 | control) | |
257 | / 2 * 2); | |
258 | line6_dump_request_async | |
259 | (&variax->dumpreq, &variax->line6, | |
260 | 2, VARIAX_DUMP_PASS3); | |
705ececd | 261 | } |
9cd57f77 | 262 | } else { |
e1a164d7 MG |
263 | DEBUG_MESSAGES(dev_err |
264 | (variax->line6.ifcdev, | |
265 | "illegal length %d of model data\n", | |
266 | variax->line6.message_length)); | |
705ececd MG |
267 | line6_dump_finished(&variax->dumpreq); |
268 | } | |
9cd57f77 | 269 | } else if (memcmp(buf + 1, variax_request_bank + 1, |
1027f476 | 270 | sizeof(variax_request_bank) - 2) == 0) { |
9cd57f77 GKH |
271 | memcpy(variax->bank, |
272 | buf + sizeof(variax_request_bank) - 1, | |
273 | sizeof(variax->bank)); | |
705ececd | 274 | line6_dump_finished(&variax->dumpreq); |
1027f476 MG |
275 | variax_startup6(variax); |
276 | } else if (memcmp(buf + 1, variax_init_model + 1, | |
277 | sizeof(variax_init_model) - 1) == 0) { | |
278 | memcpy(variax->guitar, | |
279 | buf + sizeof(variax_init_model), | |
280 | sizeof(variax->guitar)); | |
281 | } else if (memcmp(buf + 1, variax_init_version + 1, | |
282 | sizeof(variax_init_version) - 1) == 0) { | |
283 | variax_startup3(variax); | |
284 | } else if (memcmp(buf + 1, variax_init_done + 1, | |
285 | sizeof(variax_init_done) - 1) == 0) { | |
286 | /* notify of complete initialization: */ | |
287 | variax_startup4((unsigned long)variax); | |
705ececd MG |
288 | } |
289 | ||
290 | break; | |
291 | ||
292 | case LINE6_SYSEX_END: | |
293 | break; | |
294 | ||
295 | default: | |
e1a164d7 MG |
296 | DEBUG_MESSAGES(dev_err |
297 | (variax->line6.ifcdev, | |
298 | "Variax: unknown message %02X\n", buf[0])); | |
705ececd MG |
299 | } |
300 | } | |
301 | ||
302 | /* | |
303 | "read" request on "volume" special file. | |
304 | */ | |
77491e52 GKH |
305 | static ssize_t variax_get_volume(struct device *dev, |
306 | struct device_attribute *attr, char *buf) | |
705ececd | 307 | { |
e1a164d7 MG |
308 | struct usb_line6_variax *variax = |
309 | usb_get_intfdata(to_usb_interface(dev)); | |
705ececd MG |
310 | return sprintf(buf, "%d\n", variax->volume); |
311 | } | |
312 | ||
313 | /* | |
314 | "write" request on "volume" special file. | |
315 | */ | |
77491e52 GKH |
316 | static ssize_t variax_set_volume(struct device *dev, |
317 | struct device_attribute *attr, | |
318 | const char *buf, size_t count) | |
705ececd | 319 | { |
e1a164d7 MG |
320 | struct usb_line6_variax *variax = |
321 | usb_get_intfdata(to_usb_interface(dev)); | |
1383ec4d | 322 | u8 value; |
c0e6e7c1 SB |
323 | int ret; |
324 | ||
1383ec4d | 325 | ret = kstrtou8(buf, 10, &value); |
c0e6e7c1 SB |
326 | if (ret) |
327 | return ret; | |
705ececd | 328 | |
9cd57f77 GKH |
329 | if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume, |
330 | value) == 0) | |
705ececd MG |
331 | variax->volume = value; |
332 | ||
333 | return count; | |
334 | } | |
335 | ||
336 | /* | |
337 | "read" request on "model" special file. | |
338 | */ | |
77491e52 GKH |
339 | static ssize_t variax_get_model(struct device *dev, |
340 | struct device_attribute *attr, char *buf) | |
705ececd | 341 | { |
e1a164d7 MG |
342 | struct usb_line6_variax *variax = |
343 | usb_get_intfdata(to_usb_interface(dev)); | |
705ececd MG |
344 | return sprintf(buf, "%d\n", variax->model); |
345 | } | |
346 | ||
347 | /* | |
348 | "write" request on "model" special file. | |
349 | */ | |
77491e52 GKH |
350 | static ssize_t variax_set_model(struct device *dev, |
351 | struct device_attribute *attr, | |
352 | const char *buf, size_t count) | |
705ececd | 353 | { |
e1a164d7 MG |
354 | struct usb_line6_variax *variax = |
355 | usb_get_intfdata(to_usb_interface(dev)); | |
9291975d | 356 | u8 value; |
c0e6e7c1 SB |
357 | int ret; |
358 | ||
9291975d | 359 | ret = kstrtou8(buf, 10, &value); |
c0e6e7c1 SB |
360 | if (ret) |
361 | return ret; | |
705ececd | 362 | |
9cd57f77 | 363 | if (line6_send_program(&variax->line6, value) == 0) |
705ececd MG |
364 | variax->model = value; |
365 | ||
366 | return count; | |
367 | } | |
368 | ||
369 | /* | |
370 | "read" request on "active" special file. | |
371 | */ | |
77491e52 GKH |
372 | static ssize_t variax_get_active(struct device *dev, |
373 | struct device_attribute *attr, char *buf) | |
705ececd | 374 | { |
e1a164d7 MG |
375 | struct usb_line6_variax *variax = |
376 | usb_get_intfdata(to_usb_interface(dev)); | |
377 | return sprintf(buf, "%d\n", | |
378 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]); | |
705ececd MG |
379 | } |
380 | ||
381 | /* | |
382 | "write" request on "active" special file. | |
383 | */ | |
77491e52 GKH |
384 | static ssize_t variax_set_active(struct device *dev, |
385 | struct device_attribute *attr, | |
386 | const char *buf, size_t count) | |
705ececd | 387 | { |
e1a164d7 MG |
388 | struct usb_line6_variax *variax = |
389 | usb_get_intfdata(to_usb_interface(dev)); | |
9291975d | 390 | u8 value; |
c0e6e7c1 SB |
391 | int ret; |
392 | ||
9291975d | 393 | ret = kstrtou8(buf, 10, &value); |
c0e6e7c1 SB |
394 | if (ret) |
395 | return ret; | |
396 | ||
1027f476 | 397 | variax_activate_async(variax, value ? 1 : 0); |
705ececd MG |
398 | return count; |
399 | } | |
400 | ||
401 | /* | |
402 | "read" request on "tone" special file. | |
403 | */ | |
77491e52 GKH |
404 | static ssize_t variax_get_tone(struct device *dev, |
405 | struct device_attribute *attr, char *buf) | |
705ececd | 406 | { |
e1a164d7 MG |
407 | struct usb_line6_variax *variax = |
408 | usb_get_intfdata(to_usb_interface(dev)); | |
705ececd MG |
409 | return sprintf(buf, "%d\n", variax->tone); |
410 | } | |
411 | ||
412 | /* | |
413 | "write" request on "tone" special file. | |
414 | */ | |
77491e52 GKH |
415 | static ssize_t variax_set_tone(struct device *dev, |
416 | struct device_attribute *attr, | |
417 | const char *buf, size_t count) | |
705ececd | 418 | { |
e1a164d7 MG |
419 | struct usb_line6_variax *variax = |
420 | usb_get_intfdata(to_usb_interface(dev)); | |
1383ec4d | 421 | u8 value; |
c0e6e7c1 SB |
422 | int ret; |
423 | ||
1383ec4d | 424 | ret = kstrtou8(buf, 10, &value); |
c0e6e7c1 SB |
425 | if (ret) |
426 | return ret; | |
705ececd | 427 | |
9cd57f77 GKH |
428 | if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone, |
429 | value) == 0) | |
705ececd MG |
430 | variax->tone = value; |
431 | ||
432 | return count; | |
433 | } | |
434 | ||
435 | static ssize_t get_string(char *buf, const char *data, int length) | |
436 | { | |
437 | int i; | |
438 | memcpy(buf, data, length); | |
439 | ||
9cd57f77 | 440 | for (i = length; i--;) { |
705ececd MG |
441 | char c = buf[i]; |
442 | ||
9cd57f77 | 443 | if ((c != 0) && (c != ' ')) |
705ececd MG |
444 | break; |
445 | } | |
446 | ||
447 | buf[i + 1] = '\n'; | |
448 | return i + 2; | |
449 | } | |
450 | ||
451 | /* | |
452 | "read" request on "name" special file. | |
453 | */ | |
77491e52 GKH |
454 | static ssize_t variax_get_name(struct device *dev, |
455 | struct device_attribute *attr, char *buf) | |
705ececd | 456 | { |
e1a164d7 MG |
457 | struct usb_line6_variax *variax = |
458 | usb_get_intfdata(to_usb_interface(dev)); | |
1027f476 | 459 | line6_dump_wait_interruptible(&variax->dumpreq); |
9cd57f77 GKH |
460 | return get_string(buf, variax->model_data.name, |
461 | sizeof(variax->model_data.name)); | |
705ececd MG |
462 | } |
463 | ||
464 | /* | |
465 | "read" request on "bank" special file. | |
466 | */ | |
77491e52 GKH |
467 | static ssize_t variax_get_bank(struct device *dev, |
468 | struct device_attribute *attr, char *buf) | |
705ececd | 469 | { |
e1a164d7 MG |
470 | struct usb_line6_variax *variax = |
471 | usb_get_intfdata(to_usb_interface(dev)); | |
1027f476 | 472 | line6_dump_wait_interruptible(&variax->dumpreq); |
705ececd MG |
473 | return get_string(buf, variax->bank, sizeof(variax->bank)); |
474 | } | |
475 | ||
476 | /* | |
477 | "read" request on "dump" special file. | |
478 | */ | |
77491e52 GKH |
479 | static ssize_t variax_get_dump(struct device *dev, |
480 | struct device_attribute *attr, char *buf) | |
705ececd | 481 | { |
e1a164d7 MG |
482 | struct usb_line6_variax *variax = |
483 | usb_get_intfdata(to_usb_interface(dev)); | |
705ececd | 484 | int retval; |
1027f476 | 485 | retval = line6_dump_wait_interruptible(&variax->dumpreq); |
9cd57f77 GKH |
486 | if (retval < 0) |
487 | return retval; | |
488 | memcpy(buf, &variax->model_data.control, | |
489 | sizeof(variax->model_data.control)); | |
705ececd MG |
490 | return sizeof(variax->model_data.control); |
491 | } | |
492 | ||
1027f476 MG |
493 | /* |
494 | "read" request on "guitar" special file. | |
495 | */ | |
496 | static ssize_t variax_get_guitar(struct device *dev, | |
497 | struct device_attribute *attr, char *buf) | |
498 | { | |
e1a164d7 MG |
499 | struct usb_line6_variax *variax = |
500 | usb_get_intfdata(to_usb_interface(dev)); | |
1027f476 MG |
501 | return sprintf(buf, "%s\n", variax->guitar); |
502 | } | |
503 | ||
504 | #ifdef CONFIG_LINE6_USB_RAW | |
505 | ||
e1a164d7 MG |
506 | static char *variax_alloc_sysex_buffer(struct usb_line6_variax *variax, |
507 | int code, int size) | |
1027f476 | 508 | { |
e1a164d7 MG |
509 | return line6_alloc_sysex_buffer(&variax->line6, VARIAX_SYSEX_CODE, code, |
510 | size); | |
1027f476 | 511 | } |
705ececd MG |
512 | |
513 | /* | |
514 | "write" request on "raw" special file. | |
515 | */ | |
77491e52 GKH |
516 | static ssize_t variax_set_raw2(struct device *dev, |
517 | struct device_attribute *attr, | |
518 | const char *buf, size_t count) | |
705ececd | 519 | { |
e1a164d7 MG |
520 | struct usb_line6_variax *variax = |
521 | usb_get_intfdata(to_usb_interface(dev)); | |
705ececd MG |
522 | int size; |
523 | int i; | |
524 | char *sysex; | |
525 | ||
526 | count -= count % 3; | |
527 | size = count * 2; | |
528 | sysex = variax_alloc_sysex_buffer(variax, VARIAX_SYSEX_PARAM, size); | |
529 | ||
9cd57f77 | 530 | if (!sysex) |
705ececd MG |
531 | return 0; |
532 | ||
9cd57f77 | 533 | for (i = 0; i < count; i += 3) { |
705ececd MG |
534 | const unsigned char *p1 = buf + i; |
535 | char *p2 = sysex + SYSEX_DATA_OFS + i * 2; | |
536 | p2[0] = p1[2] & 0x0f; | |
537 | p2[1] = p1[2] >> 4; | |
538 | p2[2] = p1[1] & 0x0f; | |
539 | p2[3] = p1[1] >> 4; | |
540 | p2[4] = p1[0] & 0x0f; | |
541 | p2[5] = p1[0] >> 4; | |
542 | } | |
543 | ||
544 | line6_send_sysex_message(&variax->line6, sysex, size); | |
545 | kfree(sysex); | |
546 | return count; | |
547 | } | |
548 | ||
549 | #endif | |
550 | ||
551 | /* Variax workbench special files: */ | |
a3a972a0 | 552 | static DEVICE_ATTR(model, S_IWUSR | S_IRUGO, variax_get_model, |
e1a164d7 | 553 | variax_set_model); |
a3a972a0 | 554 | static DEVICE_ATTR(volume, S_IWUSR | S_IRUGO, variax_get_volume, |
e1a164d7 | 555 | variax_set_volume); |
a3a972a0 | 556 | static DEVICE_ATTR(tone, S_IWUSR | S_IRUGO, variax_get_tone, variax_set_tone); |
705ececd MG |
557 | static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write); |
558 | static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write); | |
559 | static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write); | |
a3a972a0 | 560 | static DEVICE_ATTR(active, S_IWUSR | S_IRUGO, variax_get_active, |
e1a164d7 | 561 | variax_set_active); |
1027f476 | 562 | static DEVICE_ATTR(guitar, S_IRUGO, variax_get_guitar, line6_nop_write); |
705ececd | 563 | |
1027f476 | 564 | #ifdef CONFIG_LINE6_USB_RAW |
a3a972a0 GKH |
565 | static DEVICE_ATTR(raw, S_IWUSR, line6_nop_read, line6_set_raw); |
566 | static DEVICE_ATTR(raw2, S_IWUSR, line6_nop_read, variax_set_raw2); | |
705ececd MG |
567 | #endif |
568 | ||
705ececd MG |
569 | /* |
570 | Variax destructor. | |
571 | */ | |
572 | static void variax_destruct(struct usb_interface *interface) | |
573 | { | |
574 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | |
705ececd | 575 | |
9cd57f77 GKH |
576 | if (variax == NULL) |
577 | return; | |
188e6645 | 578 | line6_cleanup_audio(&variax->line6); |
705ececd | 579 | |
e1a164d7 MG |
580 | del_timer(&variax->startup_timer1); |
581 | del_timer(&variax->startup_timer2); | |
582 | cancel_work_sync(&variax->startup_work); | |
583 | ||
705ececd MG |
584 | /* free dump request data: */ |
585 | line6_dumpreq_destructbuf(&variax->dumpreq, 2); | |
586 | line6_dumpreq_destructbuf(&variax->dumpreq, 1); | |
587 | line6_dumpreq_destruct(&variax->dumpreq); | |
588 | ||
9cd57f77 | 589 | kfree(variax->buffer_activate); |
705ececd MG |
590 | } |
591 | ||
592 | /* | |
593 | Create sysfs entries. | |
594 | */ | |
b702ed25 | 595 | static int variax_create_files2(struct device *dev) |
705ececd MG |
596 | { |
597 | int err; | |
598 | CHECK_RETURN(device_create_file(dev, &dev_attr_model)); | |
599 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume)); | |
600 | CHECK_RETURN(device_create_file(dev, &dev_attr_tone)); | |
601 | CHECK_RETURN(device_create_file(dev, &dev_attr_name)); | |
602 | CHECK_RETURN(device_create_file(dev, &dev_attr_bank)); | |
603 | CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); | |
604 | CHECK_RETURN(device_create_file(dev, &dev_attr_active)); | |
1027f476 MG |
605 | CHECK_RETURN(device_create_file(dev, &dev_attr_guitar)); |
606 | #ifdef CONFIG_LINE6_USB_RAW | |
705ececd MG |
607 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); |
608 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw2)); | |
609 | #endif | |
610 | return 0; | |
611 | } | |
612 | ||
613 | /* | |
1027f476 | 614 | Try to init workbench device. |
705ececd | 615 | */ |
1027f476 MG |
616 | static int variax_try_init(struct usb_interface *interface, |
617 | struct usb_line6_variax *variax) | |
705ececd MG |
618 | { |
619 | int err; | |
620 | ||
e1a164d7 MG |
621 | init_timer(&variax->startup_timer1); |
622 | init_timer(&variax->startup_timer2); | |
623 | INIT_WORK(&variax->startup_work, variax_startup7); | |
624 | ||
9cd57f77 GKH |
625 | if ((interface == NULL) || (variax == NULL)) |
626 | return -ENODEV; | |
705ececd MG |
627 | |
628 | /* initialize USB buffers: */ | |
9cd57f77 GKH |
629 | err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1, |
630 | sizeof(variax_request_model1)); | |
705ececd | 631 | |
9cd57f77 | 632 | if (err < 0) { |
705ececd | 633 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
634 | return err; |
635 | } | |
636 | ||
9cd57f77 GKH |
637 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2, |
638 | sizeof(variax_request_model2), 1); | |
705ececd | 639 | |
9cd57f77 | 640 | if (err < 0) { |
705ececd | 641 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
642 | return err; |
643 | } | |
644 | ||
9cd57f77 GKH |
645 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank, |
646 | sizeof(variax_request_bank), 2); | |
705ececd | 647 | |
9cd57f77 | 648 | if (err < 0) { |
705ececd | 649 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
650 | return err; |
651 | } | |
652 | ||
94002c07 JL |
653 | variax->buffer_activate = kmemdup(variax_activate, |
654 | sizeof(variax_activate), GFP_KERNEL); | |
705ececd | 655 | |
9cd57f77 | 656 | if (variax->buffer_activate == NULL) { |
705ececd | 657 | dev_err(&interface->dev, "Out of memory\n"); |
705ececd MG |
658 | return -ENOMEM; |
659 | } | |
660 | ||
705ececd | 661 | /* initialize audio system: */ |
9cd57f77 | 662 | err = line6_init_audio(&variax->line6); |
027360c5 | 663 | if (err < 0) |
705ececd | 664 | return err; |
705ececd MG |
665 | |
666 | /* initialize MIDI subsystem: */ | |
9cd57f77 | 667 | err = line6_init_midi(&variax->line6); |
027360c5 | 668 | if (err < 0) |
705ececd | 669 | return err; |
705ececd | 670 | |
1027f476 MG |
671 | /* initiate startup procedure: */ |
672 | variax_startup1(variax); | |
673 | return 0; | |
674 | } | |
675 | ||
676 | /* | |
677 | Init workbench device (and clean up in case of failure). | |
678 | */ | |
679 | int line6_variax_init(struct usb_interface *interface, | |
680 | struct usb_line6_variax *variax) | |
681 | { | |
682 | int err = variax_try_init(interface, variax); | |
683 | ||
027360c5 | 684 | if (err < 0) |
705ececd | 685 | variax_destruct(interface); |
705ececd | 686 | |
1027f476 | 687 | return err; |
705ececd MG |
688 | } |
689 | ||
690 | /* | |
691 | Workbench device disconnected. | |
692 | */ | |
1027f476 | 693 | void line6_variax_disconnect(struct usb_interface *interface) |
705ececd MG |
694 | { |
695 | struct device *dev; | |
696 | ||
9cd57f77 GKH |
697 | if (interface == NULL) |
698 | return; | |
705ececd MG |
699 | dev = &interface->dev; |
700 | ||
9cd57f77 | 701 | if (dev != NULL) { |
705ececd | 702 | /* remove sysfs entries: */ |
1027f476 | 703 | line6_variax_remove_files(0, 0, dev); |
705ececd MG |
704 | device_remove_file(dev, &dev_attr_model); |
705 | device_remove_file(dev, &dev_attr_volume); | |
706 | device_remove_file(dev, &dev_attr_tone); | |
707 | device_remove_file(dev, &dev_attr_name); | |
708 | device_remove_file(dev, &dev_attr_bank); | |
709 | device_remove_file(dev, &dev_attr_dump); | |
710 | device_remove_file(dev, &dev_attr_active); | |
1027f476 MG |
711 | device_remove_file(dev, &dev_attr_guitar); |
712 | #ifdef CONFIG_LINE6_USB_RAW | |
705ececd MG |
713 | device_remove_file(dev, &dev_attr_raw); |
714 | device_remove_file(dev, &dev_attr_raw2); | |
715 | #endif | |
716 | } | |
717 | ||
718 | variax_destruct(interface); | |
719 | } |