Commit | Line | Data |
---|---|---|
705ececd MG |
1 | /* |
2 | * Line6 Linux USB driver - 0.8.0 | |
3 | * | |
4 | * Copyright (C) 2004-2009 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 "driver.h" | |
13 | ||
14 | #include "audio.h" | |
15 | #include "control.h" | |
16 | #include "variax.h" | |
17 | ||
18 | ||
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 | ||
26 | ||
27 | static const char variax_activate[] = { | |
28 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, | |
29 | 0xf7 | |
30 | }; | |
31 | static const char variax_request_bank[] = { | |
32 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7 | |
33 | }; | |
34 | static const char variax_request_model1[] = { | |
35 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | |
36 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03, | |
37 | 0x00, 0x00, 0x00, 0xf7 | |
38 | }; | |
39 | static const char variax_request_model2[] = { | |
40 | 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00, | |
41 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03, | |
42 | 0x00, 0x00, 0x00, 0xf7 | |
43 | }; | |
44 | ||
45 | ||
46 | /* | |
47 | Decode data transmitted by workbench. | |
48 | */ | |
9cd57f77 GKH |
49 | static void variax_decode(const unsigned char *raw_data, unsigned char *data, |
50 | int raw_size) | |
705ececd | 51 | { |
9cd57f77 | 52 | for (; raw_size > 0; raw_size -= 6) { |
705ececd MG |
53 | data[2] = raw_data[0] | (raw_data[1] << 4); |
54 | data[1] = raw_data[2] | (raw_data[3] << 4); | |
55 | data[0] = raw_data[4] | (raw_data[5] << 4); | |
56 | raw_data += 6; | |
57 | data += 3; | |
58 | } | |
59 | } | |
60 | ||
61 | static void variax_activate_timeout(unsigned long arg) | |
62 | { | |
63 | struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; | |
64 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = 1; | |
9cd57f77 GKH |
65 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, |
66 | sizeof(variax_activate)); | |
705ececd MG |
67 | } |
68 | ||
69 | /* | |
70 | Send an asynchronous activation request after a given interval. | |
71 | */ | |
9cd57f77 GKH |
72 | static void variax_activate_delayed(struct usb_line6_variax *variax, |
73 | int seconds) | |
705ececd MG |
74 | { |
75 | variax->activate_timer.expires = jiffies + seconds * HZ; | |
76 | variax->activate_timer.function = variax_activate_timeout; | |
77 | variax->activate_timer.data = (unsigned long)variax; | |
78 | add_timer(&variax->activate_timer); | |
79 | } | |
80 | ||
81 | static void variax_startup_timeout(unsigned long arg) | |
82 | { | |
83 | struct usb_line6_variax *variax = (struct usb_line6_variax *)arg; | |
84 | ||
9cd57f77 | 85 | if (variax->dumpreq.ok) |
705ececd MG |
86 | return; |
87 | ||
88 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); | |
9cd57f77 GKH |
89 | line6_startup_delayed(&variax->dumpreq, 1, variax_startup_timeout, |
90 | variax); | |
705ececd MG |
91 | } |
92 | ||
93 | /* | |
94 | Process a completely received message. | |
95 | */ | |
96 | void variax_process_message(struct usb_line6_variax *variax) | |
97 | { | |
98 | const unsigned char *buf = variax->line6.buffer_message; | |
99 | ||
9cd57f77 | 100 | switch (buf[0]) { |
705ececd | 101 | case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST: |
9cd57f77 | 102 | switch (buf[1]) { |
705ececd MG |
103 | case VARIAXMIDI_volume: |
104 | variax->volume = buf[2]; | |
105 | break; | |
106 | ||
107 | case VARIAXMIDI_tone: | |
108 | variax->tone = buf[2]; | |
109 | } | |
110 | ||
111 | break; | |
112 | ||
113 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE: | |
114 | case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST: | |
115 | variax->model = buf[1]; | |
116 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 0); | |
117 | break; | |
118 | ||
119 | case LINE6_RESET: | |
120 | dev_info(variax->line6.ifcdev, "VARIAX reset\n"); | |
121 | variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); | |
122 | break; | |
123 | ||
124 | case LINE6_SYSEX_BEGIN: | |
9cd57f77 GKH |
125 | if (memcmp(buf + 1, variax_request_model1 + 1, |
126 | VARIAX_MODEL_HEADER_LENGTH - 1) == 0) { | |
127 | if (variax->line6.message_length == | |
128 | VARIAX_MODEL_MESSAGE_LENGTH) { | |
129 | switch (variax->dumpreq.in_progress) { | |
705ececd MG |
130 | case VARIAX_DUMP_PASS1: |
131 | variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, (unsigned char *)&variax->model_data, | |
132 | (sizeof(variax->model_data.name) + sizeof(variax->model_data.control) / 2) * 2); | |
133 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 1); | |
134 | line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS2); | |
135 | break; | |
136 | ||
137 | case VARIAX_DUMP_PASS2: | |
138 | /* model name is transmitted twice, so skip it here: */ | |
139 | variax_decode(buf + VARIAX_MODEL_HEADER_LENGTH, | |
9cd57f77 GKH |
140 | (unsigned char *)&variax->model_data.control + sizeof(variax->model_data.control) / 2, |
141 | sizeof(variax->model_data.control) / 2 * 2); | |
705ececd MG |
142 | variax->dumpreq.ok = 1; |
143 | line6_dump_request_async(&variax->dumpreq, &variax->line6, 2); | |
144 | line6_dump_started(&variax->dumpreq, VARIAX_DUMP_PASS3); | |
145 | } | |
9cd57f77 | 146 | } else { |
705ececd MG |
147 | DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "illegal length %d of model data\n", variax->line6.message_length)); |
148 | line6_dump_finished(&variax->dumpreq); | |
149 | } | |
9cd57f77 GKH |
150 | } else if (memcmp(buf + 1, variax_request_bank + 1, |
151 | sizeof(variax_request_bank) - 2) == 0) { | |
152 | memcpy(variax->bank, | |
153 | buf + sizeof(variax_request_bank) - 1, | |
154 | sizeof(variax->bank)); | |
705ececd MG |
155 | variax->dumpreq.ok = 1; |
156 | line6_dump_finished(&variax->dumpreq); | |
157 | } | |
158 | ||
159 | break; | |
160 | ||
161 | case LINE6_SYSEX_END: | |
162 | break; | |
163 | ||
164 | default: | |
165 | DEBUG_MESSAGES(dev_err(variax->line6.ifcdev, "Variax: unknown message %02X\n", buf[0])); | |
166 | } | |
167 | } | |
168 | ||
169 | /* | |
170 | "read" request on "volume" special file. | |
171 | */ | |
77491e52 GKH |
172 | static ssize_t variax_get_volume(struct device *dev, |
173 | struct device_attribute *attr, char *buf) | |
705ececd MG |
174 | { |
175 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
176 | return sprintf(buf, "%d\n", variax->volume); | |
177 | } | |
178 | ||
179 | /* | |
180 | "write" request on "volume" special file. | |
181 | */ | |
77491e52 GKH |
182 | static ssize_t variax_set_volume(struct device *dev, |
183 | struct device_attribute *attr, | |
184 | const char *buf, size_t count) | |
705ececd MG |
185 | { |
186 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
187 | int value = simple_strtoul(buf, NULL, 10); | |
188 | ||
9cd57f77 GKH |
189 | if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_volume, |
190 | value) == 0) | |
705ececd MG |
191 | variax->volume = value; |
192 | ||
193 | return count; | |
194 | } | |
195 | ||
196 | /* | |
197 | "read" request on "model" special file. | |
198 | */ | |
77491e52 GKH |
199 | static ssize_t variax_get_model(struct device *dev, |
200 | struct device_attribute *attr, char *buf) | |
705ececd MG |
201 | { |
202 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
203 | return sprintf(buf, "%d\n", variax->model); | |
204 | } | |
205 | ||
206 | /* | |
207 | "write" request on "model" special file. | |
208 | */ | |
77491e52 GKH |
209 | static ssize_t variax_set_model(struct device *dev, |
210 | struct device_attribute *attr, | |
211 | const char *buf, size_t count) | |
705ececd | 212 | { |
9cd57f77 | 213 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); |
705ececd MG |
214 | int value = simple_strtoul(buf, NULL, 10); |
215 | ||
9cd57f77 | 216 | if (line6_send_program(&variax->line6, value) == 0) |
705ececd MG |
217 | variax->model = value; |
218 | ||
219 | return count; | |
220 | } | |
221 | ||
222 | /* | |
223 | "read" request on "active" special file. | |
224 | */ | |
77491e52 GKH |
225 | static ssize_t variax_get_active(struct device *dev, |
226 | struct device_attribute *attr, char *buf) | |
705ececd MG |
227 | { |
228 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
229 | return sprintf(buf, "%d\n", variax->buffer_activate[VARIAX_OFFSET_ACTIVATE]); | |
230 | } | |
231 | ||
232 | /* | |
233 | "write" request on "active" special file. | |
234 | */ | |
77491e52 GKH |
235 | static ssize_t variax_set_active(struct device *dev, |
236 | struct device_attribute *attr, | |
237 | const char *buf, size_t count) | |
705ececd MG |
238 | { |
239 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
240 | int value = simple_strtoul(buf, NULL, 10) ? 1 : 0; | |
241 | variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = value; | |
9cd57f77 GKH |
242 | line6_send_raw_message_async(&variax->line6, variax->buffer_activate, |
243 | sizeof(variax_activate)); | |
705ececd MG |
244 | return count; |
245 | } | |
246 | ||
247 | /* | |
248 | "read" request on "tone" special file. | |
249 | */ | |
77491e52 GKH |
250 | static ssize_t variax_get_tone(struct device *dev, |
251 | struct device_attribute *attr, char *buf) | |
705ececd MG |
252 | { |
253 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
254 | return sprintf(buf, "%d\n", variax->tone); | |
255 | } | |
256 | ||
257 | /* | |
258 | "write" request on "tone" special file. | |
259 | */ | |
77491e52 GKH |
260 | static ssize_t variax_set_tone(struct device *dev, |
261 | struct device_attribute *attr, | |
262 | const char *buf, size_t count) | |
705ececd MG |
263 | { |
264 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
265 | int value = simple_strtoul(buf, NULL, 10); | |
266 | ||
9cd57f77 GKH |
267 | if (line6_transmit_parameter(&variax->line6, VARIAXMIDI_tone, |
268 | value) == 0) | |
705ececd MG |
269 | variax->tone = value; |
270 | ||
271 | return count; | |
272 | } | |
273 | ||
274 | static ssize_t get_string(char *buf, const char *data, int length) | |
275 | { | |
276 | int i; | |
277 | memcpy(buf, data, length); | |
278 | ||
9cd57f77 | 279 | for (i = length; i--;) { |
705ececd MG |
280 | char c = buf[i]; |
281 | ||
9cd57f77 | 282 | if ((c != 0) && (c != ' ')) |
705ececd MG |
283 | break; |
284 | } | |
285 | ||
286 | buf[i + 1] = '\n'; | |
287 | return i + 2; | |
288 | } | |
289 | ||
290 | /* | |
291 | "read" request on "name" special file. | |
292 | */ | |
77491e52 GKH |
293 | static ssize_t variax_get_name(struct device *dev, |
294 | struct device_attribute *attr, char *buf) | |
705ececd MG |
295 | { |
296 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
297 | line6_wait_dump(&variax->dumpreq, 0); | |
9cd57f77 GKH |
298 | return get_string(buf, variax->model_data.name, |
299 | sizeof(variax->model_data.name)); | |
705ececd MG |
300 | } |
301 | ||
302 | /* | |
303 | "read" request on "bank" special file. | |
304 | */ | |
77491e52 GKH |
305 | static ssize_t variax_get_bank(struct device *dev, |
306 | struct device_attribute *attr, char *buf) | |
705ececd MG |
307 | { |
308 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
309 | line6_wait_dump(&variax->dumpreq, 0); | |
310 | return get_string(buf, variax->bank, sizeof(variax->bank)); | |
311 | } | |
312 | ||
313 | /* | |
314 | "read" request on "dump" special file. | |
315 | */ | |
77491e52 GKH |
316 | static ssize_t variax_get_dump(struct device *dev, |
317 | struct device_attribute *attr, char *buf) | |
705ececd MG |
318 | { |
319 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
320 | int retval; | |
321 | retval = line6_wait_dump(&variax->dumpreq, 0); | |
9cd57f77 GKH |
322 | if (retval < 0) |
323 | return retval; | |
324 | memcpy(buf, &variax->model_data.control, | |
325 | sizeof(variax->model_data.control)); | |
705ececd MG |
326 | return sizeof(variax->model_data.control); |
327 | } | |
328 | ||
329 | #if CREATE_RAW_FILE | |
330 | ||
331 | /* | |
332 | "write" request on "raw" special file. | |
333 | */ | |
77491e52 GKH |
334 | static ssize_t variax_set_raw2(struct device *dev, |
335 | struct device_attribute *attr, | |
336 | const char *buf, size_t count) | |
705ececd MG |
337 | { |
338 | struct usb_line6_variax *variax = usb_get_intfdata(to_usb_interface(dev)); | |
339 | int size; | |
340 | int i; | |
341 | char *sysex; | |
342 | ||
343 | count -= count % 3; | |
344 | size = count * 2; | |
345 | sysex = variax_alloc_sysex_buffer(variax, VARIAX_SYSEX_PARAM, size); | |
346 | ||
9cd57f77 | 347 | if (!sysex) |
705ececd MG |
348 | return 0; |
349 | ||
9cd57f77 | 350 | for (i = 0; i < count; i += 3) { |
705ececd MG |
351 | const unsigned char *p1 = buf + i; |
352 | char *p2 = sysex + SYSEX_DATA_OFS + i * 2; | |
353 | p2[0] = p1[2] & 0x0f; | |
354 | p2[1] = p1[2] >> 4; | |
355 | p2[2] = p1[1] & 0x0f; | |
356 | p2[3] = p1[1] >> 4; | |
357 | p2[4] = p1[0] & 0x0f; | |
358 | p2[5] = p1[0] >> 4; | |
359 | } | |
360 | ||
361 | line6_send_sysex_message(&variax->line6, sysex, size); | |
362 | kfree(sysex); | |
363 | return count; | |
364 | } | |
365 | ||
366 | #endif | |
367 | ||
368 | /* Variax workbench special files: */ | |
369 | static DEVICE_ATTR(model, S_IWUGO | S_IRUGO, variax_get_model, variax_set_model); | |
370 | static DEVICE_ATTR(volume, S_IWUGO | S_IRUGO, variax_get_volume, variax_set_volume); | |
371 | static DEVICE_ATTR(tone, S_IWUGO | S_IRUGO, variax_get_tone, variax_set_tone); | |
372 | static DEVICE_ATTR(name, S_IRUGO, variax_get_name, line6_nop_write); | |
373 | static DEVICE_ATTR(bank, S_IRUGO, variax_get_bank, line6_nop_write); | |
374 | static DEVICE_ATTR(dump, S_IRUGO, variax_get_dump, line6_nop_write); | |
375 | static DEVICE_ATTR(active, S_IWUGO | S_IRUGO, variax_get_active, variax_set_active); | |
376 | ||
377 | #if CREATE_RAW_FILE | |
378 | static DEVICE_ATTR(raw, S_IWUGO, line6_nop_read, line6_set_raw); | |
379 | static DEVICE_ATTR(raw2, S_IWUGO, line6_nop_read, variax_set_raw2); | |
380 | #endif | |
381 | ||
382 | ||
383 | /* | |
384 | Variax destructor. | |
385 | */ | |
386 | static void variax_destruct(struct usb_interface *interface) | |
387 | { | |
388 | struct usb_line6_variax *variax = usb_get_intfdata(interface); | |
389 | struct usb_line6 *line6; | |
390 | ||
9cd57f77 GKH |
391 | if (variax == NULL) |
392 | return; | |
705ececd | 393 | line6 = &variax->line6; |
9cd57f77 GKH |
394 | if (line6 == NULL) |
395 | return; | |
705ececd MG |
396 | line6_cleanup_audio(line6); |
397 | ||
398 | /* free dump request data: */ | |
399 | line6_dumpreq_destructbuf(&variax->dumpreq, 2); | |
400 | line6_dumpreq_destructbuf(&variax->dumpreq, 1); | |
401 | line6_dumpreq_destruct(&variax->dumpreq); | |
402 | ||
9cd57f77 | 403 | kfree(variax->buffer_activate); |
705ececd MG |
404 | del_timer_sync(&variax->activate_timer); |
405 | } | |
406 | ||
407 | /* | |
408 | Create sysfs entries. | |
409 | */ | |
b702ed25 | 410 | static int variax_create_files2(struct device *dev) |
705ececd MG |
411 | { |
412 | int err; | |
413 | CHECK_RETURN(device_create_file(dev, &dev_attr_model)); | |
414 | CHECK_RETURN(device_create_file(dev, &dev_attr_volume)); | |
415 | CHECK_RETURN(device_create_file(dev, &dev_attr_tone)); | |
416 | CHECK_RETURN(device_create_file(dev, &dev_attr_name)); | |
417 | CHECK_RETURN(device_create_file(dev, &dev_attr_bank)); | |
418 | CHECK_RETURN(device_create_file(dev, &dev_attr_dump)); | |
419 | CHECK_RETURN(device_create_file(dev, &dev_attr_active)); | |
420 | #if CREATE_RAW_FILE | |
421 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw)); | |
422 | CHECK_RETURN(device_create_file(dev, &dev_attr_raw2)); | |
423 | #endif | |
424 | return 0; | |
425 | } | |
426 | ||
427 | /* | |
428 | Init workbench device. | |
429 | */ | |
9cd57f77 GKH |
430 | int variax_init(struct usb_interface *interface, |
431 | struct usb_line6_variax *variax) | |
705ececd MG |
432 | { |
433 | int err; | |
434 | ||
9cd57f77 GKH |
435 | if ((interface == NULL) || (variax == NULL)) |
436 | return -ENODEV; | |
705ececd MG |
437 | |
438 | /* initialize USB buffers: */ | |
9cd57f77 GKH |
439 | err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1, |
440 | sizeof(variax_request_model1)); | |
705ececd | 441 | |
9cd57f77 | 442 | if (err < 0) { |
705ececd MG |
443 | dev_err(&interface->dev, "Out of memory\n"); |
444 | variax_destruct(interface); | |
445 | return err; | |
446 | } | |
447 | ||
9cd57f77 GKH |
448 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2, |
449 | sizeof(variax_request_model2), 1); | |
705ececd | 450 | |
9cd57f77 | 451 | if (err < 0) { |
705ececd MG |
452 | dev_err(&interface->dev, "Out of memory\n"); |
453 | variax_destruct(interface); | |
454 | return err; | |
455 | } | |
456 | ||
9cd57f77 GKH |
457 | err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank, |
458 | sizeof(variax_request_bank), 2); | |
705ececd | 459 | |
9cd57f77 | 460 | if (err < 0) { |
705ececd MG |
461 | dev_err(&interface->dev, "Out of memory\n"); |
462 | variax_destruct(interface); | |
463 | return err; | |
464 | } | |
465 | ||
466 | variax->buffer_activate = kmalloc(sizeof(variax_activate), GFP_KERNEL); | |
467 | ||
9cd57f77 | 468 | if (variax->buffer_activate == NULL) { |
705ececd MG |
469 | dev_err(&interface->dev, "Out of memory\n"); |
470 | variax_destruct(interface); | |
471 | return -ENOMEM; | |
472 | } | |
473 | ||
9cd57f77 GKH |
474 | memcpy(variax->buffer_activate, variax_activate, |
475 | sizeof(variax_activate)); | |
705ececd MG |
476 | init_timer(&variax->activate_timer); |
477 | ||
478 | /* create sysfs entries: */ | |
9cd57f77 GKH |
479 | err = variax_create_files(0, 0, &interface->dev); |
480 | if (err < 0) { | |
705ececd MG |
481 | variax_destruct(interface); |
482 | return err; | |
483 | } | |
484 | ||
9cd57f77 GKH |
485 | err = variax_create_files2(&interface->dev); |
486 | if (err < 0) { | |
705ececd MG |
487 | variax_destruct(interface); |
488 | return err; | |
489 | } | |
490 | ||
491 | /* initialize audio system: */ | |
9cd57f77 GKH |
492 | err = line6_init_audio(&variax->line6); |
493 | if (err < 0) { | |
705ececd MG |
494 | variax_destruct(interface); |
495 | return err; | |
496 | } | |
497 | ||
498 | /* initialize MIDI subsystem: */ | |
9cd57f77 GKH |
499 | err = line6_init_midi(&variax->line6); |
500 | if (err < 0) { | |
705ececd MG |
501 | variax_destruct(interface); |
502 | return err; | |
503 | } | |
504 | ||
505 | /* register audio system: */ | |
9cd57f77 GKH |
506 | err = line6_register_audio(&variax->line6); |
507 | if (err < 0) { | |
705ececd MG |
508 | variax_destruct(interface); |
509 | return err; | |
510 | } | |
511 | ||
512 | variax_activate_delayed(variax, VARIAX_ACTIVATE_DELAY); | |
9cd57f77 GKH |
513 | line6_startup_delayed(&variax->dumpreq, VARIAX_STARTUP_DELAY, |
514 | variax_startup_timeout, variax); | |
705ececd MG |
515 | return 0; |
516 | } | |
517 | ||
518 | /* | |
519 | Workbench device disconnected. | |
520 | */ | |
521 | void variax_disconnect(struct usb_interface *interface) | |
522 | { | |
523 | struct device *dev; | |
524 | ||
9cd57f77 GKH |
525 | if (interface == NULL) |
526 | return; | |
705ececd MG |
527 | dev = &interface->dev; |
528 | ||
9cd57f77 | 529 | if (dev != NULL) { |
705ececd MG |
530 | /* remove sysfs entries: */ |
531 | variax_remove_files(0, 0, dev); | |
532 | device_remove_file(dev, &dev_attr_model); | |
533 | device_remove_file(dev, &dev_attr_volume); | |
534 | device_remove_file(dev, &dev_attr_tone); | |
535 | device_remove_file(dev, &dev_attr_name); | |
536 | device_remove_file(dev, &dev_attr_bank); | |
537 | device_remove_file(dev, &dev_attr_dump); | |
538 | device_remove_file(dev, &dev_attr_active); | |
539 | #if CREATE_RAW_FILE | |
540 | device_remove_file(dev, &dev_attr_raw); | |
541 | device_remove_file(dev, &dev_attr_raw2); | |
542 | #endif | |
543 | } | |
544 | ||
545 | variax_destruct(interface); | |
546 | } |