xref: /openbmc/linux/sound/usb/line6/variax.c (revision 3fc41476)
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_VERSIONREQ,
30 	VARIAX_STARTUP_ACTIVATE,
31 	VARIAX_STARTUP_SETUP,
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 	/* Current progress in startup procedure */
47 	int startup_progress;
48 };
49 
50 #define line6_to_variax(x)	container_of(x, struct usb_line6_variax, line6)
51 
52 #define VARIAX_OFFSET_ACTIVATE 7
53 
54 /*
55 	This message is sent by the device during initialization and identifies
56 	the connected guitar version.
57 */
58 static const char variax_init_version[] = {
59 	0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
60 	0x07, 0x00, 0x00, 0x00
61 };
62 
63 /*
64 	This message is the last one sent by the device during initialization.
65 */
66 static const char variax_init_done[] = {
67 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
68 };
69 
70 static const char variax_activate[] = {
71 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
72 	0xf7
73 };
74 
75 static void variax_activate_async(struct usb_line6_variax *variax, int a)
76 {
77 	variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
78 	line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
79 				     sizeof(variax_activate));
80 }
81 
82 /*
83 	Variax startup procedure.
84 	This is a sequence of functions with special requirements (e.g., must
85 	not run immediately after initialization, must not run in interrupt
86 	context). After the last one has finished, the device is ready to use.
87 */
88 
89 static void variax_startup(struct usb_line6 *line6)
90 {
91 	struct usb_line6_variax *variax = line6_to_variax(line6);
92 
93 	switch (variax->startup_progress) {
94 	case VARIAX_STARTUP_VERSIONREQ:
95 		/* repeat request until getting the response */
96 		schedule_delayed_work(&line6->startup_work,
97 				      msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
98 		/* request firmware version: */
99 		line6_version_request_async(line6);
100 		break;
101 	case VARIAX_STARTUP_ACTIVATE:
102 		/* activate device: */
103 		variax_activate_async(variax, 1);
104 		variax->startup_progress = VARIAX_STARTUP_SETUP;
105 		schedule_delayed_work(&line6->startup_work,
106 				      msecs_to_jiffies(VARIAX_STARTUP_DELAY4));
107 		break;
108 	case VARIAX_STARTUP_SETUP:
109 		/* ALSA audio interface: */
110 		snd_card_register(variax->line6.card);
111 		break;
112 	}
113 }
114 
115 /*
116 	Process a completely received message.
117 */
118 static void line6_variax_process_message(struct usb_line6 *line6)
119 {
120 	struct usb_line6_variax *variax = line6_to_variax(line6);
121 	const unsigned char *buf = variax->line6.buffer_message;
122 
123 	switch (buf[0]) {
124 	case LINE6_RESET:
125 		dev_info(variax->line6.ifcdev, "VARIAX reset\n");
126 		break;
127 
128 	case LINE6_SYSEX_BEGIN:
129 		if (memcmp(buf + 1, variax_init_version + 1,
130 			   sizeof(variax_init_version) - 1) == 0) {
131 			if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE)
132 				break;
133 			variax->startup_progress = VARIAX_STARTUP_ACTIVATE;
134 			cancel_delayed_work(&line6->startup_work);
135 			schedule_delayed_work(&line6->startup_work,
136 					      msecs_to_jiffies(VARIAX_STARTUP_DELAY3));
137 		} else if (memcmp(buf + 1, variax_init_done + 1,
138 				  sizeof(variax_init_done) - 1) == 0) {
139 			/* notify of complete initialization: */
140 			if (variax->startup_progress >= VARIAX_STARTUP_SETUP)
141 				break;
142 			cancel_delayed_work(&line6->startup_work);
143 			schedule_delayed_work(&line6->startup_work, 0);
144 		}
145 		break;
146 	}
147 }
148 
149 /*
150 	Variax destructor.
151 */
152 static void line6_variax_disconnect(struct usb_line6 *line6)
153 {
154 	struct usb_line6_variax *variax = line6_to_variax(line6);
155 
156 	kfree(variax->buffer_activate);
157 }
158 
159 /*
160 	 Try to init workbench device.
161 */
162 static int variax_init(struct usb_line6 *line6,
163 		       const struct usb_device_id *id)
164 {
165 	struct usb_line6_variax *variax = line6_to_variax(line6);
166 	int err;
167 
168 	line6->process_message = line6_variax_process_message;
169 	line6->disconnect = line6_variax_disconnect;
170 	line6->startup = variax_startup;
171 
172 	/* initialize USB buffers: */
173 	variax->buffer_activate = kmemdup(variax_activate,
174 					  sizeof(variax_activate), GFP_KERNEL);
175 
176 	if (variax->buffer_activate == NULL)
177 		return -ENOMEM;
178 
179 	/* initialize MIDI subsystem: */
180 	err = line6_init_midi(&variax->line6);
181 	if (err < 0)
182 		return err;
183 
184 	/* initiate startup procedure: */
185 	schedule_delayed_work(&line6->startup_work,
186 			      msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
187 	return 0;
188 }
189 
190 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
191 #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
192 
193 /* table of devices that work with this driver */
194 static const struct usb_device_id variax_id_table[] = {
195 	{ LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
196 	{ LINE6_DEVICE(0x534d),    .driver_info = LINE6_VARIAX },
197 	{}
198 };
199 
200 MODULE_DEVICE_TABLE(usb, variax_id_table);
201 
202 static const struct line6_properties variax_properties_table[] = {
203 	[LINE6_PODXTLIVE_VARIAX] = {
204 		.id = "PODxtLive",
205 		.name = "PODxt Live",
206 		.capabilities	= LINE6_CAP_CONTROL
207 				| LINE6_CAP_CONTROL_MIDI,
208 		.altsetting = 1,
209 		.ep_ctrl_r = 0x86,
210 		.ep_ctrl_w = 0x05,
211 		.ep_audio_r = 0x82,
212 		.ep_audio_w = 0x01,
213 	},
214 	[LINE6_VARIAX] = {
215 		.id = "Variax",
216 		.name = "Variax Workbench",
217 		.capabilities	= LINE6_CAP_CONTROL
218 				| LINE6_CAP_CONTROL_MIDI,
219 		.altsetting = 1,
220 		.ep_ctrl_r = 0x82,
221 		.ep_ctrl_w = 0x01,
222 		/* no audio channel */
223 	}
224 };
225 
226 /*
227 	Probe USB device.
228 */
229 static int variax_probe(struct usb_interface *interface,
230 			const struct usb_device_id *id)
231 {
232 	return line6_probe(interface, id, "Line6-Variax",
233 			   &variax_properties_table[id->driver_info],
234 			   variax_init, sizeof(struct usb_line6_variax));
235 }
236 
237 static struct usb_driver variax_driver = {
238 	.name = KBUILD_MODNAME,
239 	.probe = variax_probe,
240 	.disconnect = line6_disconnect,
241 #ifdef CONFIG_PM
242 	.suspend = line6_suspend,
243 	.resume = line6_resume,
244 	.reset_resume = line6_resume,
245 #endif
246 	.id_table = variax_id_table,
247 };
248 
249 module_usb_driver(variax_driver);
250 
251 MODULE_DESCRIPTION("Vairax Workbench USB driver");
252 MODULE_LICENSE("GPL");
253