xref: /openbmc/linux/sound/usb/line6/variax.c (revision 6d99a79c)
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(struct timer_list *t);
86 static void variax_startup4(struct timer_list *t);
87 static void variax_startup5(struct timer_list *t);
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);
110 }
111 
112 static void variax_startup2(struct timer_list *t)
113 {
114 	struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1);
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);
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);
136 }
137 
138 static void variax_startup4(struct timer_list *t)
139 {
140 	struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
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);
149 }
150 
151 static void variax_startup5(struct timer_list *t)
152 {
153 	struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
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(&variax->startup_timer2);
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 	timer_setup(&variax->startup_timer1, NULL, 0);
226 	timer_setup(&variax->startup_timer2, NULL, 0);
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 				| LINE6_CAP_CONTROL_MIDI,
264 		.altsetting = 1,
265 		.ep_ctrl_r = 0x86,
266 		.ep_ctrl_w = 0x05,
267 		.ep_audio_r = 0x82,
268 		.ep_audio_w = 0x01,
269 	},
270 	[LINE6_VARIAX] = {
271 		.id = "Variax",
272 		.name = "Variax Workbench",
273 		.capabilities	= LINE6_CAP_CONTROL
274 				| LINE6_CAP_CONTROL_MIDI,
275 		.altsetting = 1,
276 		.ep_ctrl_r = 0x82,
277 		.ep_ctrl_w = 0x01,
278 		/* no audio channel */
279 	}
280 };
281 
282 /*
283 	Probe USB device.
284 */
285 static int variax_probe(struct usb_interface *interface,
286 			const struct usb_device_id *id)
287 {
288 	return line6_probe(interface, id, "Line6-Variax",
289 			   &variax_properties_table[id->driver_info],
290 			   variax_init, sizeof(struct usb_line6_variax));
291 }
292 
293 static struct usb_driver variax_driver = {
294 	.name = KBUILD_MODNAME,
295 	.probe = variax_probe,
296 	.disconnect = line6_disconnect,
297 #ifdef CONFIG_PM
298 	.suspend = line6_suspend,
299 	.resume = line6_resume,
300 	.reset_resume = line6_resume,
301 #endif
302 	.id_table = variax_id_table,
303 };
304 
305 module_usb_driver(variax_driver);
306 
307 MODULE_DESCRIPTION("Vairax Workbench USB driver");
308 MODULE_LICENSE("GPL");
309