blob: ff55ad37a6c5dd6a6dee051da8e75979b8f12c83 [file] [log] [blame]
Markus Grabner705ecec2009-02-27 19:43:04 -08001/*
Markus Grabnere1a164d2010-08-23 01:08:25 +02002 * Line6 Linux USB driver - 0.9.1beta
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>
13
Markus Grabner705ecec2009-02-27 19:43:04 -080014#include "audio.h"
15#include "control.h"
Markus Grabner1027f4762010-08-12 01:35:30 +020016#include "driver.h"
Markus Grabner705ecec2009-02-27 19:43:04 -080017#include "variax.h"
18
Markus Grabner705ecec2009-02-27 19:43:04 -080019#define VARIAX_MODEL_HEADER_LENGTH 7
20#define VARIAX_MODEL_MESSAGE_LENGTH 199
21#define VARIAX_OFFSET_ACTIVATE 7
22
Markus Grabner1027f4762010-08-12 01:35:30 +020023/*
24 This message is sent by the device during initialization and identifies
25 the connected guitar model.
26*/
27static const char variax_init_model[] = {
28 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x69, 0x02,
29 0x00
30};
31
32/*
33 This message is sent by the device during initialization and identifies
34 the connected guitar version.
35*/
36static const char variax_init_version[] = {
37 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
38 0x07, 0x00, 0x00, 0x00
39};
40
41/*
42 This message is the last one sent by the device during initialization.
43*/
44static const char variax_init_done[] = {
45 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
46};
47
Markus Grabner705ecec2009-02-27 19:43:04 -080048static const char variax_activate[] = {
49 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
50 0xf7
51};
Markus Grabner1027f4762010-08-12 01:35:30 +020052
Markus Grabner705ecec2009-02-27 19:43:04 -080053static const char variax_request_bank[] = {
54 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6d, 0xf7
55};
Markus Grabner1027f4762010-08-12 01:35:30 +020056
Markus Grabner705ecec2009-02-27 19:43:04 -080057static const char variax_request_model1[] = {
58 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
59 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x03,
60 0x00, 0x00, 0x00, 0xf7
61};
Markus Grabner1027f4762010-08-12 01:35:30 +020062
Markus Grabner705ecec2009-02-27 19:43:04 -080063static const char variax_request_model2[] = {
64 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x3c, 0x00,
65 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x03,
66 0x00, 0x00, 0x00, 0xf7
67};
68
Markus Grabner1027f4762010-08-12 01:35:30 +020069/* forward declarations: */
Markus Grabner1027f4762010-08-12 01:35:30 +020070static void variax_startup2(unsigned long data);
71static void variax_startup4(unsigned long data);
72static void variax_startup5(unsigned long data);
73
Markus Grabner705ecec2009-02-27 19:43:04 -080074/*
75 Decode data transmitted by workbench.
76*/
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -080077static void variax_decode(const unsigned char *raw_data, unsigned char *data,
78 int raw_size)
Markus Grabner705ecec2009-02-27 19:43:04 -080079{
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -080080 for (; raw_size > 0; raw_size -= 6) {
Markus Grabner705ecec2009-02-27 19:43:04 -080081 data[2] = raw_data[0] | (raw_data[1] << 4);
82 data[1] = raw_data[2] | (raw_data[3] << 4);
83 data[0] = raw_data[4] | (raw_data[5] << 4);
84 raw_data += 6;
85 data += 3;
86 }
87}
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;
Markus Grabnere1a164d2010-08-23 01:08:25 +0200141 CHECK_STARTUP_PROGRESS(variax->startup_progress,
142 VARIAX_STARTUP_ACTIVATE);
Markus Grabner1027f4762010-08-12 01:35:30 +0200143
144 /* activate device: */
145 variax_activate_async(variax, 1);
Markus Grabnere1a164d2010-08-23 01:08:25 +0200146 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
147 variax_startup5, (unsigned long)variax);
Markus Grabner1027f4762010-08-12 01:35:30 +0200148}
149
150static void variax_startup5(unsigned long data)
151{
152 struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
Markus Grabnere1a164d2010-08-23 01:08:25 +0200153 CHECK_STARTUP_PROGRESS(variax->startup_progress,
154 VARIAX_STARTUP_DUMPREQ);
Markus Grabner1027f4762010-08-12 01:35:30 +0200155
156 /* current model dump: */
Markus Grabnere1a164d2010-08-23 01:08:25 +0200157 line6_dump_request_async(&variax->dumpreq, &variax->line6, 0,
158 VARIAX_DUMP_PASS1);
Stefan Hajnoczie5603cb2012-11-11 13:24:46 +0100159 /* passes 2 and 3 are performed implicitly before entering
160 * variax_startup6.
161 */
Markus Grabner1027f4762010-08-12 01:35:30 +0200162}
163
164static void variax_startup6(struct usb_line6_variax *variax)
165{
Markus Grabnere1a164d2010-08-23 01:08:25 +0200166 CHECK_STARTUP_PROGRESS(variax->startup_progress,
167 VARIAX_STARTUP_WORKQUEUE);
Markus Grabner1027f4762010-08-12 01:35:30 +0200168
169 /* schedule work for global work queue: */
170 schedule_work(&variax->startup_work);
171}
172
173static void variax_startup7(struct work_struct *work)
174{
Markus Grabnere1a164d2010-08-23 01:08:25 +0200175 struct usb_line6_variax *variax =
176 container_of(work, struct usb_line6_variax, startup_work);
Markus Grabner1027f4762010-08-12 01:35:30 +0200177 struct usb_line6 *line6 = &variax->line6;
178
Markus Grabnere1a164d2010-08-23 01:08:25 +0200179 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
Markus Grabner1027f4762010-08-12 01:35:30 +0200180
181 /* ALSA audio interface: */
182 line6_register_audio(&variax->line6);
183
184 /* device files: */
185 line6_variax_create_files(0, 0, line6->ifcdev);
Markus Grabner705ecec2009-02-27 19:43:04 -0800186}
187
188/*
189 Process a completely received message.
190*/
Markus Grabner1027f4762010-08-12 01:35:30 +0200191void line6_variax_process_message(struct usb_line6_variax *variax)
Markus Grabner705ecec2009-02-27 19:43:04 -0800192{
193 const unsigned char *buf = variax->line6.buffer_message;
194
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800195 switch (buf[0]) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800196 case LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST:
Markus Grabner705ecec2009-02-27 19:43:04 -0800197 break;
198
199 case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_DEVICE:
200 case LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST:
Markus Grabnere1a164d2010-08-23 01:08:25 +0200201 line6_dump_request_async(&variax->dumpreq, &variax->line6, 0,
202 VARIAX_DUMP_PASS1);
Markus Grabner705ecec2009-02-27 19:43:04 -0800203 break;
204
205 case LINE6_RESET:
206 dev_info(variax->line6.ifcdev, "VARIAX reset\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800207 break;
208
209 case LINE6_SYSEX_BEGIN:
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800210 if (memcmp(buf + 1, variax_request_model1 + 1,
211 VARIAX_MODEL_HEADER_LENGTH - 1) == 0) {
212 if (variax->line6.message_length ==
213 VARIAX_MODEL_MESSAGE_LENGTH) {
214 switch (variax->dumpreq.in_progress) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800215 case VARIAX_DUMP_PASS1:
Markus Grabnere1a164d2010-08-23 01:08:25 +0200216 line6_dump_request_async
217 (&variax->dumpreq, &variax->line6,
218 1, VARIAX_DUMP_PASS2);
Markus Grabner705ecec2009-02-27 19:43:04 -0800219 break;
220
221 case VARIAX_DUMP_PASS2:
222 /* model name is transmitted twice, so skip it here: */
Markus Grabnere1a164d2010-08-23 01:08:25 +0200223 variax_decode(buf +
224 VARIAX_MODEL_HEADER_LENGTH,
225 (unsigned char *)
226 &variax->
227 model_data.control +
228 sizeof(variax->model_data.
229 control)
230 / 2,
231 sizeof(variax->model_data.
232 control)
233 / 2 * 2);
234 line6_dump_request_async
235 (&variax->dumpreq, &variax->line6,
236 2, VARIAX_DUMP_PASS3);
Markus Grabner705ecec2009-02-27 19:43:04 -0800237 }
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800238 } else {
Stefan Hajnoczie00d33c2012-11-11 13:52:24 +0100239 dev_dbg(variax->line6.ifcdev,
240 "illegal length %d of model data\n",
241 variax->line6.message_length);
Markus Grabner705ecec2009-02-27 19:43:04 -0800242 line6_dump_finished(&variax->dumpreq);
243 }
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800244 } else if (memcmp(buf + 1, variax_request_bank + 1,
Markus Grabner1027f4762010-08-12 01:35:30 +0200245 sizeof(variax_request_bank) - 2) == 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800246 line6_dump_finished(&variax->dumpreq);
Markus Grabner1027f4762010-08-12 01:35:30 +0200247 variax_startup6(variax);
Markus Grabner1027f4762010-08-12 01:35:30 +0200248 } else if (memcmp(buf + 1, variax_init_version + 1,
249 sizeof(variax_init_version) - 1) == 0) {
250 variax_startup3(variax);
251 } else if (memcmp(buf + 1, variax_init_done + 1,
252 sizeof(variax_init_done) - 1) == 0) {
253 /* notify of complete initialization: */
254 variax_startup4((unsigned long)variax);
Markus Grabner705ecec2009-02-27 19:43:04 -0800255 }
256
257 break;
258
259 case LINE6_SYSEX_END:
260 break;
261
262 default:
Stefan Hajnoczie00d33c2012-11-11 13:52:24 +0100263 dev_dbg(variax->line6.ifcdev,
264 "Variax: unknown message %02X\n", buf[0]);
Markus Grabner705ecec2009-02-27 19:43:04 -0800265 }
266}
267
Markus Grabner705ecec2009-02-27 19:43:04 -0800268/*
269 Variax destructor.
270*/
271static void variax_destruct(struct usb_interface *interface)
272{
273 struct usb_line6_variax *variax = usb_get_intfdata(interface);
Markus Grabner705ecec2009-02-27 19:43:04 -0800274
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800275 if (variax == NULL)
276 return;
Stefan Hajnoczi188e6642011-12-10 02:12:30 +0100277 line6_cleanup_audio(&variax->line6);
Markus Grabner705ecec2009-02-27 19:43:04 -0800278
Markus Grabnere1a164d2010-08-23 01:08:25 +0200279 del_timer(&variax->startup_timer1);
280 del_timer(&variax->startup_timer2);
281 cancel_work_sync(&variax->startup_work);
282
Markus Grabner705ecec2009-02-27 19:43:04 -0800283 /* free dump request data: */
284 line6_dumpreq_destructbuf(&variax->dumpreq, 2);
285 line6_dumpreq_destructbuf(&variax->dumpreq, 1);
286 line6_dumpreq_destruct(&variax->dumpreq);
287
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800288 kfree(variax->buffer_activate);
Markus Grabner705ecec2009-02-27 19:43:04 -0800289}
290
291/*
Markus Grabner1027f4762010-08-12 01:35:30 +0200292 Try to init workbench device.
Markus Grabner705ecec2009-02-27 19:43:04 -0800293*/
Markus Grabner1027f4762010-08-12 01:35:30 +0200294static int variax_try_init(struct usb_interface *interface,
295 struct usb_line6_variax *variax)
Markus Grabner705ecec2009-02-27 19:43:04 -0800296{
297 int err;
298
Markus Grabnere1a164d2010-08-23 01:08:25 +0200299 init_timer(&variax->startup_timer1);
300 init_timer(&variax->startup_timer2);
301 INIT_WORK(&variax->startup_work, variax_startup7);
302
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800303 if ((interface == NULL) || (variax == NULL))
304 return -ENODEV;
Markus Grabner705ecec2009-02-27 19:43:04 -0800305
306 /* initialize USB buffers: */
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800307 err = line6_dumpreq_init(&variax->dumpreq, variax_request_model1,
308 sizeof(variax_request_model1));
Markus Grabner705ecec2009-02-27 19:43:04 -0800309
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800310 if (err < 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800311 dev_err(&interface->dev, "Out of memory\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800312 return err;
313 }
314
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800315 err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_model2,
316 sizeof(variax_request_model2), 1);
Markus Grabner705ecec2009-02-27 19:43:04 -0800317
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800318 if (err < 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800319 dev_err(&interface->dev, "Out of memory\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800320 return err;
321 }
322
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800323 err = line6_dumpreq_initbuf(&variax->dumpreq, variax_request_bank,
324 sizeof(variax_request_bank), 2);
Markus Grabner705ecec2009-02-27 19:43:04 -0800325
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800326 if (err < 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800327 dev_err(&interface->dev, "Out of memory\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800328 return err;
329 }
330
Julia Lawall94002c02010-05-15 23:21:43 +0200331 variax->buffer_activate = kmemdup(variax_activate,
332 sizeof(variax_activate), GFP_KERNEL);
Markus Grabner705ecec2009-02-27 19:43:04 -0800333
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800334 if (variax->buffer_activate == NULL) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800335 dev_err(&interface->dev, "Out of memory\n");
Markus Grabner705ecec2009-02-27 19:43:04 -0800336 return -ENOMEM;
337 }
338
Markus Grabner705ecec2009-02-27 19:43:04 -0800339 /* initialize audio system: */
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800340 err = line6_init_audio(&variax->line6);
Greg Kroah-Hartman027360c2010-09-21 16:58:00 -0700341 if (err < 0)
Markus Grabner705ecec2009-02-27 19:43:04 -0800342 return err;
Markus Grabner705ecec2009-02-27 19:43:04 -0800343
344 /* initialize MIDI subsystem: */
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800345 err = line6_init_midi(&variax->line6);
Greg Kroah-Hartman027360c2010-09-21 16:58:00 -0700346 if (err < 0)
Markus Grabner705ecec2009-02-27 19:43:04 -0800347 return err;
Markus Grabner705ecec2009-02-27 19:43:04 -0800348
Markus Grabner1027f4762010-08-12 01:35:30 +0200349 /* initiate startup procedure: */
350 variax_startup1(variax);
351 return 0;
352}
353
354/*
355 Init workbench device (and clean up in case of failure).
356*/
357int line6_variax_init(struct usb_interface *interface,
358 struct usb_line6_variax *variax)
359{
360 int err = variax_try_init(interface, variax);
361
Greg Kroah-Hartman027360c2010-09-21 16:58:00 -0700362 if (err < 0)
Markus Grabner705ecec2009-02-27 19:43:04 -0800363 variax_destruct(interface);
Markus Grabner705ecec2009-02-27 19:43:04 -0800364
Markus Grabner1027f4762010-08-12 01:35:30 +0200365 return err;
Markus Grabner705ecec2009-02-27 19:43:04 -0800366}
367
368/*
369 Workbench device disconnected.
370*/
Markus Grabner1027f4762010-08-12 01:35:30 +0200371void line6_variax_disconnect(struct usb_interface *interface)
Markus Grabner705ecec2009-02-27 19:43:04 -0800372{
373 struct device *dev;
374
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800375 if (interface == NULL)
376 return;
Markus Grabner705ecec2009-02-27 19:43:04 -0800377 dev = &interface->dev;
378
Greg Kroah-Hartman9cd57f72009-02-27 22:43:57 -0800379 if (dev != NULL) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800380 /* remove sysfs entries: */
Markus Grabner1027f4762010-08-12 01:35:30 +0200381 line6_variax_remove_files(0, 0, dev);
Markus Grabner705ecec2009-02-27 19:43:04 -0800382 }
383
384 variax_destruct(interface);
385}