Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Line 6 Linux USB driver
4 *
5 * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.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*/
24enum {
25 VARIAX_STARTUP_VERSIONREQ,
26 VARIAX_STARTUP_ACTIVATE,
27 VARIAX_STARTUP_SETUP,
28};
29
30enum {
31 LINE6_PODXTLIVE_VARIAX,
32 LINE6_VARIAX
33};
34
35struct 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*/
54static 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*/
62static const char variax_init_done[] = {
63 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
64};
65
66static const char variax_activate[] = {
67 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
68 0xf7
69};
70
71static 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
85static 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*/
114static 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*/
148static 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*/
158static 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
163 line6->process_message = line6_variax_process_message;
164 line6->disconnect = line6_variax_disconnect;
165 line6->startup = variax_startup;
166
167 /* initialize USB buffers: */
168 variax->buffer_activate = kmemdup(variax_activate,
169 sizeof(variax_activate), GFP_KERNEL);
170
171 if (variax->buffer_activate == NULL)
172 return -ENOMEM;
173
174 /* initiate startup procedure: */
175 schedule_delayed_work(&line6->startup_work,
176 msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
177 return 0;
178}
179
180#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
181#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
182
183/* table of devices that work with this driver */
184static const struct usb_device_id variax_id_table[] = {
185 { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
186 { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX },
187 {}
188};
189
190MODULE_DEVICE_TABLE(usb, variax_id_table);
191
192static const struct line6_properties variax_properties_table[] = {
193 [LINE6_PODXTLIVE_VARIAX] = {
194 .id = "PODxtLive",
195 .name = "PODxt Live",
196 .capabilities = LINE6_CAP_CONTROL
197 | LINE6_CAP_CONTROL_MIDI,
198 .altsetting = 1,
199 .ep_ctrl_r = 0x86,
200 .ep_ctrl_w = 0x05,
201 .ep_audio_r = 0x82,
202 .ep_audio_w = 0x01,
203 },
204 [LINE6_VARIAX] = {
205 .id = "Variax",
206 .name = "Variax Workbench",
207 .capabilities = LINE6_CAP_CONTROL
208 | LINE6_CAP_CONTROL_MIDI,
209 .altsetting = 1,
210 .ep_ctrl_r = 0x82,
211 .ep_ctrl_w = 0x01,
212 /* no audio channel */
213 }
214};
215
216/*
217 Probe USB device.
218*/
219static int variax_probe(struct usb_interface *interface,
220 const struct usb_device_id *id)
221{
222 return line6_probe(interface, id, "Line6-Variax",
223 &variax_properties_table[id->driver_info],
224 variax_init, sizeof(struct usb_line6_variax));
225}
226
227static struct usb_driver variax_driver = {
228 .name = KBUILD_MODNAME,
229 .probe = variax_probe,
230 .disconnect = line6_disconnect,
231#ifdef CONFIG_PM
232 .suspend = line6_suspend,
233 .resume = line6_resume,
234 .reset_resume = line6_resume,
235#endif
236 .id_table = variax_id_table,
237};
238
239module_usb_driver(variax_driver);
240
241MODULE_DESCRIPTION("Variax Workbench USB driver");
242MODULE_LICENSE("GPL");
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*/
24enum {
25 VARIAX_STARTUP_VERSIONREQ,
26 VARIAX_STARTUP_ACTIVATE,
27 VARIAX_STARTUP_SETUP,
28};
29
30enum {
31 LINE6_PODXTLIVE_VARIAX,
32 LINE6_VARIAX
33};
34
35struct 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*/
54static 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*/
62static const char variax_init_done[] = {
63 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
64};
65
66static const char variax_activate[] = {
67 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
68 0xf7
69};
70
71static 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
85static 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*/
114static 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*/
148static 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*/
158static 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 */
190static 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
196MODULE_DEVICE_TABLE(usb, variax_id_table);
197
198static 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*/
225static 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
233static 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
245module_usb_driver(variax_driver);
246
247MODULE_DESCRIPTION("Variax Workbench USB driver");
248MODULE_LICENSE("GPL");