xref: /openbmc/linux/sound/usb/line6/variax.c (revision 22d55f02)
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");
305