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