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