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*/
28enum {
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
38enum {
39	LINE6_PODXTLIVE_VARIAX,
40	LINE6_VARIAX
41};
42
43struct 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*/
67static 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*/
75static const char variax_init_done[] = {
76	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
77};
78
79static const char variax_activate[] = {
80	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
81	0xf7
82};
83
84/* forward declarations: */
85static void variax_startup2(unsigned long data);
86static void variax_startup4(unsigned long data);
87static void variax_startup5(unsigned long data);
88
89static 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
103static 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, (unsigned long)variax);
110}
111
112static void variax_startup2(unsigned long data)
113{
114	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
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, (unsigned long)variax);
124
125	/* request firmware version: */
126	line6_version_request_async(line6);
127}
128
129static 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, (unsigned long)variax);
136}
137
138static void variax_startup4(unsigned long data)
139{
140	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
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, (unsigned long)variax);
149}
150
151static void variax_startup5(unsigned long data)
152{
153	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
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
162static 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*/
176static 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((unsigned long)variax);
194		}
195		break;
196	}
197}
198
199/*
200	Variax destructor.
201*/
202static 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*/
216static 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	init_timer(&variax->startup_timer1);
226	init_timer(&variax->startup_timer2);
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 */
250static 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
256MODULE_DEVICE_TABLE(usb, variax_id_table);
257
258static const struct line6_properties variax_properties_table[] = {
259	[LINE6_PODXTLIVE_VARIAX] = {
260		.id = "PODxtLive",
261		.name = "PODxt Live",
262		.capabilities	= LINE6_CAP_CONTROL,
263		.altsetting = 1,
264		.ep_ctrl_r = 0x86,
265		.ep_ctrl_w = 0x05,
266		.ep_audio_r = 0x82,
267		.ep_audio_w = 0x01,
268	},
269	[LINE6_VARIAX] = {
270		.id = "Variax",
271		.name = "Variax Workbench",
272		.capabilities	= LINE6_CAP_CONTROL,
273		.altsetting = 1,
274		.ep_ctrl_r = 0x82,
275		.ep_ctrl_w = 0x01,
276		/* no audio channel */
277	}
278};
279
280/*
281	Probe USB device.
282*/
283static int variax_probe(struct usb_interface *interface,
284			const struct usb_device_id *id)
285{
286	return line6_probe(interface, id, "Line6-Variax",
287			   &variax_properties_table[id->driver_info],
288			   variax_init, sizeof(struct usb_line6_variax));
289}
290
291static struct usb_driver variax_driver = {
292	.name = KBUILD_MODNAME,
293	.probe = variax_probe,
294	.disconnect = line6_disconnect,
295#ifdef CONFIG_PM
296	.suspend = line6_suspend,
297	.resume = line6_resume,
298	.reset_resume = line6_resume,
299#endif
300	.id_table = variax_id_table,
301};
302
303module_usb_driver(variax_driver);
304
305MODULE_DESCRIPTION("Vairax Workbench USB driver");
306MODULE_LICENSE("GPL");
307