GNU Linux-libre 4.4-gnu1
[releases.git] / sound / usb / line6 / variax.c
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                 .altsetting = 1,
264                 .ep_ctrl_r = 0x86,
265                 .ep_ctrl_w = 0x05,
266                 .ep_audio_r = 0x82,
267                 .ep_audio_w = 0x01,
268         },
269         [LINE6_VARIAX] = {
270                 .id = "Variax",
271                 .name = "Variax Workbench",
272                 .capabilities   = LINE6_CAP_CONTROL,
273                 .altsetting = 1,
274                 .ep_ctrl_r = 0x82,
275                 .ep_ctrl_w = 0x01,
276                 /* no audio channel */
277         }
278 };
279
280 /*
281         Probe USB device.
282 */
283 static int variax_probe(struct usb_interface *interface,
284                         const struct usb_device_id *id)
285 {
286         return line6_probe(interface, id, "Line6-Variax",
287                            &variax_properties_table[id->driver_info],
288                            variax_init, sizeof(struct usb_line6_variax));
289 }
290
291 static struct usb_driver variax_driver = {
292         .name = KBUILD_MODNAME,
293         .probe = variax_probe,
294         .disconnect = line6_disconnect,
295 #ifdef CONFIG_PM
296         .suspend = line6_suspend,
297         .resume = line6_resume,
298         .reset_resume = line6_resume,
299 #endif
300         .id_table = variax_id_table,
301 };
302
303 module_usb_driver(variax_driver);
304
305 MODULE_DESCRIPTION("Vairax Workbench USB driver");
306 MODULE_LICENSE("GPL");