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