treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 372
[linux-2.6-block.git] / sound / usb / line6 / variax.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Line 6 Linux USB driver
4  *
5  * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
6  */
7
8 #include <linux/slab.h>
9 #include <linux/spinlock.h>
10 #include <linux/usb.h>
11 #include <linux/wait.h>
12 #include <linux/module.h>
13 #include <sound/core.h>
14
15 #include "driver.h"
16
17 #define VARIAX_STARTUP_DELAY1 1000
18 #define VARIAX_STARTUP_DELAY3 100
19 #define VARIAX_STARTUP_DELAY4 100
20
21 /*
22         Stages of Variax startup procedure
23 */
24 enum {
25         VARIAX_STARTUP_INIT = 1,
26         VARIAX_STARTUP_VERSIONREQ,
27         VARIAX_STARTUP_WAIT,
28         VARIAX_STARTUP_ACTIVATE,
29         VARIAX_STARTUP_WORKQUEUE,
30         VARIAX_STARTUP_SETUP,
31         VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1
32 };
33
34 enum {
35         LINE6_PODXTLIVE_VARIAX,
36         LINE6_VARIAX
37 };
38
39 struct usb_line6_variax {
40         /* Generic Line 6 USB data */
41         struct usb_line6 line6;
42
43         /* Buffer for activation code */
44         unsigned char *buffer_activate;
45
46         /* Handler for device initialization */
47         struct work_struct startup_work;
48
49         /* Timers for device initialization */
50         struct timer_list startup_timer1;
51         struct timer_list startup_timer2;
52
53         /* Current progress in startup procedure */
54         int startup_progress;
55 };
56
57 #define VARIAX_OFFSET_ACTIVATE 7
58
59 /*
60         This message is sent by the device during initialization and identifies
61         the connected guitar version.
62 */
63 static const char variax_init_version[] = {
64         0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
65         0x07, 0x00, 0x00, 0x00
66 };
67
68 /*
69         This message is the last one sent by the device during initialization.
70 */
71 static const char variax_init_done[] = {
72         0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
73 };
74
75 static const char variax_activate[] = {
76         0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
77         0xf7
78 };
79
80 /* forward declarations: */
81 static void variax_startup2(struct timer_list *t);
82 static void variax_startup4(struct timer_list *t);
83 static void variax_startup5(struct timer_list *t);
84
85 static void variax_activate_async(struct usb_line6_variax *variax, int a)
86 {
87         variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
88         line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
89                                      sizeof(variax_activate));
90 }
91
92 /*
93         Variax startup procedure.
94         This is a sequence of functions with special requirements (e.g., must
95         not run immediately after initialization, must not run in interrupt
96         context). After the last one has finished, the device is ready to use.
97 */
98
99 static void variax_startup1(struct usb_line6_variax *variax)
100 {
101         CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
102
103         /* delay startup procedure: */
104         line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
105                           variax_startup2);
106 }
107
108 static void variax_startup2(struct timer_list *t)
109 {
110         struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1);
111         struct usb_line6 *line6 = &variax->line6;
112
113         /* schedule another startup procedure until startup is complete: */
114         if (variax->startup_progress >= VARIAX_STARTUP_LAST)
115                 return;
116
117         variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
118         line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
119                           variax_startup2);
120
121         /* request firmware version: */
122         line6_version_request_async(line6);
123 }
124
125 static void variax_startup3(struct usb_line6_variax *variax)
126 {
127         CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
128
129         /* delay startup procedure: */
130         line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
131                           variax_startup4);
132 }
133
134 static void variax_startup4(struct timer_list *t)
135 {
136         struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
137
138         CHECK_STARTUP_PROGRESS(variax->startup_progress,
139                                VARIAX_STARTUP_ACTIVATE);
140
141         /* activate device: */
142         variax_activate_async(variax, 1);
143         line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
144                           variax_startup5);
145 }
146
147 static void variax_startup5(struct timer_list *t)
148 {
149         struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
150
151         CHECK_STARTUP_PROGRESS(variax->startup_progress,
152                                VARIAX_STARTUP_WORKQUEUE);
153
154         /* schedule work for global work queue: */
155         schedule_work(&variax->startup_work);
156 }
157
158 static void variax_startup6(struct work_struct *work)
159 {
160         struct usb_line6_variax *variax =
161             container_of(work, struct usb_line6_variax, startup_work);
162
163         CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
164
165         /* ALSA audio interface: */
166         snd_card_register(variax->line6.card);
167 }
168
169 /*
170         Process a completely received message.
171 */
172 static void line6_variax_process_message(struct usb_line6 *line6)
173 {
174         struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
175         const unsigned char *buf = variax->line6.buffer_message;
176
177         switch (buf[0]) {
178         case LINE6_RESET:
179                 dev_info(variax->line6.ifcdev, "VARIAX reset\n");
180                 break;
181
182         case LINE6_SYSEX_BEGIN:
183                 if (memcmp(buf + 1, variax_init_version + 1,
184                            sizeof(variax_init_version) - 1) == 0) {
185                         variax_startup3(variax);
186                 } else if (memcmp(buf + 1, variax_init_done + 1,
187                                   sizeof(variax_init_done) - 1) == 0) {
188                         /* notify of complete initialization: */
189                         variax_startup4(&variax->startup_timer2);
190                 }
191                 break;
192         }
193 }
194
195 /*
196         Variax destructor.
197 */
198 static void line6_variax_disconnect(struct usb_line6 *line6)
199 {
200         struct usb_line6_variax *variax = (struct usb_line6_variax *)line6;
201
202         del_timer(&variax->startup_timer1);
203         del_timer(&variax->startup_timer2);
204         cancel_work_sync(&variax->startup_work);
205
206         kfree(variax->buffer_activate);
207 }
208
209 /*
210          Try to init workbench device.
211 */
212 static int variax_init(struct usb_line6 *line6,
213                        const struct usb_device_id *id)
214 {
215         struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
216         int err;
217
218         line6->process_message = line6_variax_process_message;
219         line6->disconnect = line6_variax_disconnect;
220
221         timer_setup(&variax->startup_timer1, NULL, 0);
222         timer_setup(&variax->startup_timer2, NULL, 0);
223         INIT_WORK(&variax->startup_work, variax_startup6);
224
225         /* initialize USB buffers: */
226         variax->buffer_activate = kmemdup(variax_activate,
227                                           sizeof(variax_activate), GFP_KERNEL);
228
229         if (variax->buffer_activate == NULL)
230                 return -ENOMEM;
231
232         /* initialize MIDI subsystem: */
233         err = line6_init_midi(&variax->line6);
234         if (err < 0)
235                 return err;
236
237         /* initiate startup procedure: */
238         variax_startup1(variax);
239         return 0;
240 }
241
242 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
243 #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
244
245 /* table of devices that work with this driver */
246 static const struct usb_device_id variax_id_table[] = {
247         { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
248         { LINE6_DEVICE(0x534d),    .driver_info = LINE6_VARIAX },
249         {}
250 };
251
252 MODULE_DEVICE_TABLE(usb, variax_id_table);
253
254 static const struct line6_properties variax_properties_table[] = {
255         [LINE6_PODXTLIVE_VARIAX] = {
256                 .id = "PODxtLive",
257                 .name = "PODxt Live",
258                 .capabilities   = LINE6_CAP_CONTROL
259                                 | LINE6_CAP_CONTROL_MIDI,
260                 .altsetting = 1,
261                 .ep_ctrl_r = 0x86,
262                 .ep_ctrl_w = 0x05,
263                 .ep_audio_r = 0x82,
264                 .ep_audio_w = 0x01,
265         },
266         [LINE6_VARIAX] = {
267                 .id = "Variax",
268                 .name = "Variax Workbench",
269                 .capabilities   = LINE6_CAP_CONTROL
270                                 | LINE6_CAP_CONTROL_MIDI,
271                 .altsetting = 1,
272                 .ep_ctrl_r = 0x82,
273                 .ep_ctrl_w = 0x01,
274                 /* no audio channel */
275         }
276 };
277
278 /*
279         Probe USB device.
280 */
281 static int variax_probe(struct usb_interface *interface,
282                         const struct usb_device_id *id)
283 {
284         return line6_probe(interface, id, "Line6-Variax",
285                            &variax_properties_table[id->driver_info],
286                            variax_init, sizeof(struct usb_line6_variax));
287 }
288
289 static struct usb_driver variax_driver = {
290         .name = KBUILD_MODNAME,
291         .probe = variax_probe,
292         .disconnect = line6_disconnect,
293 #ifdef CONFIG_PM
294         .suspend = line6_suspend,
295         .resume = line6_resume,
296         .reset_resume = line6_resume,
297 #endif
298         .id_table = variax_id_table,
299 };
300
301 module_usb_driver(variax_driver);
302
303 MODULE_DESCRIPTION("Vairax Workbench USB driver");
304 MODULE_LICENSE("GPL");