xref: /openbmc/linux/sound/usb/line6/variax.c (revision 867a0e05)
1 /*
2  * Line6 Linux USB driver - 0.9.1beta
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 
14 #include "audio.h"
15 #include "driver.h"
16 #include "variax.h"
17 
18 #define VARIAX_OFFSET_ACTIVATE 7
19 
20 /*
21 	This message is sent by the device during initialization and identifies
22 	the connected guitar version.
23 */
24 static const char variax_init_version[] = {
25 	0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
26 	0x07, 0x00, 0x00, 0x00
27 };
28 
29 /*
30 	This message is the last one sent by the device during initialization.
31 */
32 static const char variax_init_done[] = {
33 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
34 };
35 
36 static const char variax_activate[] = {
37 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
38 	0xf7
39 };
40 
41 /* forward declarations: */
42 static void variax_startup2(unsigned long data);
43 static void variax_startup4(unsigned long data);
44 static void variax_startup5(unsigned long data);
45 
46 static void variax_activate_async(struct usb_line6_variax *variax, int a)
47 {
48 	variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
49 	line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
50 				     sizeof(variax_activate));
51 }
52 
53 /*
54 	Variax startup procedure.
55 	This is a sequence of functions with special requirements (e.g., must
56 	not run immediately after initialization, must not run in interrupt
57 	context). After the last one has finished, the device is ready to use.
58 */
59 
60 static void variax_startup1(struct usb_line6_variax *variax)
61 {
62 	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
63 
64 	/* delay startup procedure: */
65 	line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
66 			  variax_startup2, (unsigned long)variax);
67 }
68 
69 static void variax_startup2(unsigned long data)
70 {
71 	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
72 	struct usb_line6 *line6 = &variax->line6;
73 
74 	/* schedule another startup procedure until startup is complete: */
75 	if (variax->startup_progress >= VARIAX_STARTUP_LAST)
76 		return;
77 
78 	variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
79 	line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
80 			  variax_startup2, (unsigned long)variax);
81 
82 	/* request firmware version: */
83 	line6_version_request_async(line6);
84 }
85 
86 static void variax_startup3(struct usb_line6_variax *variax)
87 {
88 	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
89 
90 	/* delay startup procedure: */
91 	line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
92 			  variax_startup4, (unsigned long)variax);
93 }
94 
95 static void variax_startup4(unsigned long data)
96 {
97 	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
98 
99 	CHECK_STARTUP_PROGRESS(variax->startup_progress,
100 			       VARIAX_STARTUP_ACTIVATE);
101 
102 	/* activate device: */
103 	variax_activate_async(variax, 1);
104 	line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
105 			  variax_startup5, (unsigned long)variax);
106 }
107 
108 static void variax_startup5(unsigned long data)
109 {
110 	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
111 
112 	CHECK_STARTUP_PROGRESS(variax->startup_progress,
113 			       VARIAX_STARTUP_WORKQUEUE);
114 
115 	/* schedule work for global work queue: */
116 	schedule_work(&variax->startup_work);
117 }
118 
119 static void variax_startup6(struct work_struct *work)
120 {
121 	struct usb_line6_variax *variax =
122 	    container_of(work, struct usb_line6_variax, startup_work);
123 
124 	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
125 
126 	/* ALSA audio interface: */
127 	line6_register_audio(&variax->line6);
128 }
129 
130 /*
131 	Process a completely received message.
132 */
133 static void line6_variax_process_message(struct usb_line6 *line6)
134 {
135 	struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
136 	const unsigned char *buf = variax->line6.buffer_message;
137 
138 	switch (buf[0]) {
139 	case LINE6_RESET:
140 		dev_info(variax->line6.ifcdev, "VARIAX reset\n");
141 		break;
142 
143 	case LINE6_SYSEX_BEGIN:
144 		if (memcmp(buf + 1, variax_init_version + 1,
145 			   sizeof(variax_init_version) - 1) == 0) {
146 			variax_startup3(variax);
147 		} else if (memcmp(buf + 1, variax_init_done + 1,
148 				  sizeof(variax_init_done) - 1) == 0) {
149 			/* notify of complete initialization: */
150 			variax_startup4((unsigned long)variax);
151 		}
152 		break;
153 	}
154 }
155 
156 /*
157 	Variax destructor.
158 */
159 static void variax_destruct(struct usb_interface *interface)
160 {
161 	struct usb_line6_variax *variax = usb_get_intfdata(interface);
162 
163 	if (variax == NULL)
164 		return;
165 	line6_cleanup_audio(&variax->line6);
166 
167 	del_timer(&variax->startup_timer1);
168 	del_timer(&variax->startup_timer2);
169 	cancel_work_sync(&variax->startup_work);
170 
171 	kfree(variax->buffer_activate);
172 }
173 
174 /*
175 	Workbench device disconnected.
176 */
177 static void line6_variax_disconnect(struct usb_interface *interface)
178 {
179 	if (interface == NULL)
180 		return;
181 
182 	variax_destruct(interface);
183 }
184 
185 /*
186 	 Try to init workbench device.
187 */
188 static int variax_try_init(struct usb_interface *interface,
189 			   struct usb_line6 *line6)
190 {
191 	struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
192 	int err;
193 
194 	line6->process_message = line6_variax_process_message;
195 	line6->disconnect = line6_variax_disconnect;
196 
197 	init_timer(&variax->startup_timer1);
198 	init_timer(&variax->startup_timer2);
199 	INIT_WORK(&variax->startup_work, variax_startup6);
200 
201 	if ((interface == NULL) || (variax == NULL))
202 		return -ENODEV;
203 
204 	/* initialize USB buffers: */
205 	variax->buffer_activate = kmemdup(variax_activate,
206 					  sizeof(variax_activate), GFP_KERNEL);
207 
208 	if (variax->buffer_activate == NULL) {
209 		dev_err(&interface->dev, "Out of memory\n");
210 		return -ENOMEM;
211 	}
212 
213 	/* initialize audio system: */
214 	err = line6_init_audio(&variax->line6);
215 	if (err < 0)
216 		return err;
217 
218 	/* initialize MIDI subsystem: */
219 	err = line6_init_midi(&variax->line6);
220 	if (err < 0)
221 		return err;
222 
223 	/* initiate startup procedure: */
224 	variax_startup1(variax);
225 	return 0;
226 }
227 
228 /*
229 	 Init workbench device (and clean up in case of failure).
230 */
231 int line6_variax_init(struct usb_interface *interface, struct usb_line6 *line6)
232 {
233 	int err = variax_try_init(interface, line6);
234 
235 	if (err < 0)
236 		variax_destruct(interface);
237 
238 	return err;
239 }
240