blob: 0c4512d0382eab1cfb9e4b5aaf329c462f68c922 [file] [log] [blame]
Markus Grabner705ecec2009-02-27 19:43:04 -08001/*
Chris Rorvickc078a4a2015-01-20 02:20:50 -06002 * Line 6 Linux USB driver
Markus Grabner705ecec2009-02-27 19:43:04 -08003 *
Markus Grabner1027f4762010-08-12 01:35:30 +02004 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
Markus Grabner705ecec2009-02-27 19:43:04 -08005 *
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
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090012#include <linux/slab.h>
Takashi Iwaiccddbe42015-01-15 08:22:31 +010013#include <linux/spinlock.h>
14#include <linux/usb.h>
15#include <linux/wait.h>
16#include <linux/module.h>
17#include <sound/core.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018
Markus Grabner1027f4762010-08-12 01:35:30 +020019#include "driver.h"
Takashi Iwaiccddbe42015-01-15 08:22:31 +010020
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 {
Takashi Iwaicddbd4f2015-01-28 14:43:11 +010044 /* Generic Line 6 USB data */
Takashi Iwaiccddbe42015-01-15 08:22:31 +010045 struct usb_line6 line6;
46
Takashi Iwaicddbd4f2015-01-28 14:43:11 +010047 /* Buffer for activation code */
Takashi Iwaiccddbe42015-01-15 08:22:31 +010048 unsigned char *buffer_activate;
49
Takashi Iwaicddbd4f2015-01-28 14:43:11 +010050 /* Handler for device initialization */
Takashi Iwaiccddbe42015-01-15 08:22:31 +010051 struct work_struct startup_work;
52
Takashi Iwaicddbd4f2015-01-28 14:43:11 +010053 /* Timers for device initialization */
Takashi Iwaiccddbe42015-01-15 08:22:31 +010054 struct timer_list startup_timer1;
55 struct timer_list startup_timer2;
56
Takashi Iwaicddbd4f2015-01-28 14:43:11 +010057 /* Current progress in startup procedure */
Takashi Iwaiccddbe42015-01-15 08:22:31 +010058 int startup_progress;
59};
Markus Grabner705ecec2009-02-27 19:43:04 -080060
Markus Grabner705ecec2009-02-27 19:43:04 -080061#define VARIAX_OFFSET_ACTIVATE 7
62
Markus Grabner1027f4762010-08-12 01:35:30 +020063/*
64 This message is sent by the device during initialization and identifies
Markus Grabner1027f4762010-08-12 01:35:30 +020065 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
Markus Grabner705ecec2009-02-27 19:43:04 -080079static const char variax_activate[] = {
80 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
81 0xf7
82};
Markus Grabner1027f4762010-08-12 01:35:30 +020083
Markus Grabner1027f4762010-08-12 01:35:30 +020084/* forward declarations: */
Markus Grabner1027f4762010-08-12 01:35:30 +020085static void variax_startup2(unsigned long data);
86static void variax_startup4(unsigned long data);
87static void variax_startup5(unsigned long data);
88
Markus Grabner1027f4762010-08-12 01:35:30 +020089static void variax_activate_async(struct usb_line6_variax *variax, int a)
Markus Grabner705ecec2009-02-27 19:43:04 -080090{
Markus Grabner1027f4762010-08-12 01:35:30 +020091 variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -080092 line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
93 sizeof(variax_activate));
Markus Grabner705ecec2009-02-27 19:43:04 -080094}
95
96/*
Markus Grabner1027f4762010-08-12 01:35:30 +020097 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.
Markus Grabner705ecec2009-02-27 19:43:04 -0800101*/
Markus Grabner1027f4762010-08-12 01:35:30 +0200102
103static void variax_startup1(struct usb_line6_variax *variax)
Markus Grabner705ecec2009-02-27 19:43:04 -0800104{
Markus Grabnere1a164d2010-08-23 01:08:25 +0200105 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
Markus Grabner1027f4762010-08-12 01:35:30 +0200106
107 /* delay startup procedure: */
Markus Grabnere1a164d2010-08-23 01:08:25 +0200108 line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
109 variax_startup2, (unsigned long)variax);
Markus Grabner705ecec2009-02-27 19:43:04 -0800110}
111
Markus Grabner1027f4762010-08-12 01:35:30 +0200112static void variax_startup2(unsigned long data)
Markus Grabner705ecec2009-02-27 19:43:04 -0800113{
Markus Grabner1027f4762010-08-12 01:35:30 +0200114 struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
115 struct usb_line6 *line6 = &variax->line6;
Markus Grabnere1a164d2010-08-23 01:08:25 +0200116
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);
Markus Grabner705ecec2009-02-27 19:43:04 -0800124
Markus Grabner1027f4762010-08-12 01:35:30 +0200125 /* request firmware version: */
126 line6_version_request_async(line6);
127}
Markus Grabner705ecec2009-02-27 19:43:04 -0800128
Markus Grabner1027f4762010-08-12 01:35:30 +0200129static void variax_startup3(struct usb_line6_variax *variax)
130{
Markus Grabnere1a164d2010-08-23 01:08:25 +0200131 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
Markus Grabner1027f4762010-08-12 01:35:30 +0200132
133 /* delay startup procedure: */
Markus Grabnere1a164d2010-08-23 01:08:25 +0200134 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
135 variax_startup4, (unsigned long)variax);
Markus Grabner1027f4762010-08-12 01:35:30 +0200136}
137
138static void variax_startup4(unsigned long data)
139{
140 struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
Jerry Snitselaarf3c52612014-04-24 00:31:48 -0700141
Markus Grabnere1a164d2010-08-23 01:08:25 +0200142 CHECK_STARTUP_PROGRESS(variax->startup_progress,
143 VARIAX_STARTUP_ACTIVATE);
Markus Grabner1027f4762010-08-12 01:35:30 +0200144
145 /* activate device: */
146 variax_activate_async(variax, 1);
Markus Grabnere1a164d2010-08-23 01:08:25 +0200147 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
148 variax_startup5, (unsigned long)variax);
Markus Grabner1027f4762010-08-12 01:35:30 +0200149}
150
151static void variax_startup5(unsigned long data)
152{
153 struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
Jerry Snitselaarf3c52612014-04-24 00:31:48 -0700154
Markus Grabnere1a164d2010-08-23 01:08:25 +0200155 CHECK_STARTUP_PROGRESS(variax->startup_progress,
Markus Grabnere1a164d2010-08-23 01:08:25 +0200156 VARIAX_STARTUP_WORKQUEUE);
Markus Grabner1027f4762010-08-12 01:35:30 +0200157
158 /* schedule work for global work queue: */
159 schedule_work(&variax->startup_work);
160}
161
Stefan Hajnoczi323246b2012-11-22 20:49:23 +0100162static void variax_startup6(struct work_struct *work)
Markus Grabner1027f4762010-08-12 01:35:30 +0200163{
Markus Grabnere1a164d2010-08-23 01:08:25 +0200164 struct usb_line6_variax *variax =
165 container_of(work, struct usb_line6_variax, startup_work);
Markus Grabner1027f4762010-08-12 01:35:30 +0200166
Markus Grabnere1a164d2010-08-23 01:08:25 +0200167 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
Markus Grabner1027f4762010-08-12 01:35:30 +0200168
169 /* ALSA audio interface: */
Takashi Iwai85a93392015-01-19 15:54:00 +0100170 snd_card_register(variax->line6.card);
Markus Grabner705ecec2009-02-27 19:43:04 -0800171}
172
173/*
174 Process a completely received message.
175*/
Chris Rorvick01f6b2b2015-01-12 12:42:58 -0800176static void line6_variax_process_message(struct usb_line6 *line6)
Markus Grabner705ecec2009-02-27 19:43:04 -0800177{
Chris Rorvick1cad3e82015-01-12 12:42:57 -0800178 struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
Markus Grabner705ecec2009-02-27 19:43:04 -0800179 const unsigned char *buf = variax->line6.buffer_message;
180
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800181 switch (buf[0]) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800182 case LINE6_RESET:
183 dev_info(variax->line6.ifcdev, "VARIAX reset\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800184 break;
185
186 case LINE6_SYSEX_BEGIN:
Stefan Hajnoczi323246b2012-11-22 20:49:23 +0100187 if (memcmp(buf + 1, variax_init_version + 1,
188 sizeof(variax_init_version) - 1) == 0) {
Markus Grabner1027f4762010-08-12 01:35:30 +0200189 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);
Markus Grabner705ecec2009-02-27 19:43:04 -0800194 }
Markus Grabner705ecec2009-02-27 19:43:04 -0800195 break;
Markus Grabner705ecec2009-02-27 19:43:04 -0800196 }
197}
198
Markus Grabner705ecec2009-02-27 19:43:04 -0800199/*
200 Variax destructor.
201*/
Takashi Iwaif66fd992015-01-25 18:22:58 +0100202static void line6_variax_disconnect(struct usb_line6 *line6)
Markus Grabner705ecec2009-02-27 19:43:04 -0800203{
Takashi Iwaif66fd992015-01-25 18:22:58 +0100204 struct usb_line6_variax *variax = (struct usb_line6_variax *)line6;
Markus Grabner705ecec2009-02-27 19:43:04 -0800205
Markus Grabnere1a164d2010-08-23 01:08:25 +0200206 del_timer(&variax->startup_timer1);
207 del_timer(&variax->startup_timer2);
208 cancel_work_sync(&variax->startup_work);
209
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800210 kfree(variax->buffer_activate);
Markus Grabner705ecec2009-02-27 19:43:04 -0800211}
212
213/*
Markus Grabner1027f4762010-08-12 01:35:30 +0200214 Try to init workbench device.
Markus Grabner705ecec2009-02-27 19:43:04 -0800215*/
Takashi Iwaif66fd992015-01-25 18:22:58 +0100216static int variax_init(struct usb_line6 *line6,
217 const struct usb_device_id *id)
Markus Grabner705ecec2009-02-27 19:43:04 -0800218{
Chris Rorvicka221dd42015-01-12 12:42:56 -0800219 struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
Markus Grabner705ecec2009-02-27 19:43:04 -0800220 int err;
221
Chris Rorvick01f6b2b2015-01-12 12:42:58 -0800222 line6->process_message = line6_variax_process_message;
Chris Rorvicka46c4672015-01-12 12:42:59 -0800223 line6->disconnect = line6_variax_disconnect;
Chris Rorvick01f6b2b2015-01-12 12:42:58 -0800224
Markus Grabnere1a164d2010-08-23 01:08:25 +0200225 init_timer(&variax->startup_timer1);
226 init_timer(&variax->startup_timer2);
Stefan Hajnoczi323246b2012-11-22 20:49:23 +0100227 INIT_WORK(&variax->startup_work, variax_startup6);
Markus Grabnere1a164d2010-08-23 01:08:25 +0200228
Markus Grabner705ecec2009-02-27 19:43:04 -0800229 /* initialize USB buffers: */
Julia Lawall94002c02010-05-15 23:21:43 +0200230 variax->buffer_activate = kmemdup(variax_activate,
231 sizeof(variax_activate), GFP_KERNEL);
Markus Grabner705ecec2009-02-27 19:43:04 -0800232
Takashi Iwaia019f5e2015-01-19 15:05:10 +0100233 if (variax->buffer_activate == NULL)
Markus Grabner705ecec2009-02-27 19:43:04 -0800234 return -ENOMEM;
Markus Grabner705ecec2009-02-27 19:43:04 -0800235
Markus Grabner705ecec2009-02-27 19:43:04 -0800236 /* initialize MIDI subsystem: */
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800237 err = line6_init_midi(&variax->line6);
Greg Kroah-Hartman027360c2010-09-21 16:58:00 -0700238 if (err < 0)
Markus Grabner705ecec2009-02-27 19:43:04 -0800239 return err;
Markus Grabner705ecec2009-02-27 19:43:04 -0800240
Markus Grabner1027f4762010-08-12 01:35:30 +0200241 /* initiate startup procedure: */
242 variax_startup1(variax);
243 return 0;
244}
245
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100246#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",
Andrej Krutak174e1fc2016-09-18 20:59:26 +0200262 .capabilities = LINE6_CAP_CONTROL
263 | LINE6_CAP_CONTROL_MIDI,
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100264 .altsetting = 1,
265 .ep_ctrl_r = 0x86,
266 .ep_ctrl_w = 0x05,
267 .ep_audio_r = 0x82,
268 .ep_audio_w = 0x01,
269 },
270 [LINE6_VARIAX] = {
271 .id = "Variax",
272 .name = "Variax Workbench",
Andrej Krutak174e1fc2016-09-18 20:59:26 +0200273 .capabilities = LINE6_CAP_CONTROL
274 | LINE6_CAP_CONTROL_MIDI,
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100275 .altsetting = 1,
276 .ep_ctrl_r = 0x82,
277 .ep_ctrl_w = 0x01,
278 /* no audio channel */
279 }
280};
281
282/*
283 Probe USB device.
284*/
285static int variax_probe(struct usb_interface *interface,
286 const struct usb_device_id *id)
287{
Chris Rorvick12865ca2015-02-07 10:43:19 -0600288 return line6_probe(interface, id, "Line6-Variax",
Takashi Iwai85a93392015-01-19 15:54:00 +0100289 &variax_properties_table[id->driver_info],
Takashi Iwaiaca514b2015-01-25 18:36:29 +0100290 variax_init, sizeof(struct usb_line6_variax));
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100291}
292
293static struct usb_driver variax_driver = {
294 .name = KBUILD_MODNAME,
295 .probe = variax_probe,
296 .disconnect = line6_disconnect,
297#ifdef CONFIG_PM
298 .suspend = line6_suspend,
299 .resume = line6_resume,
300 .reset_resume = line6_resume,
301#endif
302 .id_table = variax_id_table,
303};
304
305module_usb_driver(variax_driver);
306
307MODULE_DESCRIPTION("Vairax Workbench USB driver");
308MODULE_LICENSE("GPL");