variax.c 8.0 KB
Newer Older
M
Markus Grabner 已提交
1
/*
2
 * Line6 Linux USB driver - 0.9.1beta
M
Markus Grabner 已提交
3
 *
4
 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
M
Markus Grabner 已提交
5 6 7 8 9 10 11
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation, version 2.
 *
 */

12
#include <linux/slab.h>
T
Takashi Iwai 已提交
13 14 15 16 17
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <sound/core.h>
18

19
#include "driver.h"
T
Takashi Iwai 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#include "usbdefs.h"

#define VARIAX_STARTUP_DELAY1 1000
#define VARIAX_STARTUP_DELAY3 100
#define VARIAX_STARTUP_DELAY4 100

/*
	Stages of Variax startup procedure
*/
enum {
	VARIAX_STARTUP_INIT = 1,
	VARIAX_STARTUP_VERSIONREQ,
	VARIAX_STARTUP_WAIT,
	VARIAX_STARTUP_ACTIVATE,
	VARIAX_STARTUP_WORKQUEUE,
	VARIAX_STARTUP_SETUP,
	VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1
};

enum {
	LINE6_PODXTLIVE_VARIAX,
	LINE6_VARIAX
};

struct usb_line6_variax {
	/**
		Generic Line6 USB data.
	*/
	struct usb_line6 line6;

	/**
		Buffer for activation code.
	*/
	unsigned char *buffer_activate;

	/**
		Handler for device initializaton.
	*/
	struct work_struct startup_work;

	/**
		Timers for device initializaton.
	*/
	struct timer_list startup_timer1;
	struct timer_list startup_timer2;

	/**
		Current progress in startup procedure.
	*/
	int startup_progress;
};
M
Markus Grabner 已提交
71 72 73

#define VARIAX_OFFSET_ACTIVATE 7

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/*
	This message is sent by the device during initialization and identifies
	the connected guitar version.
*/
static const char variax_init_version[] = {
	0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
	0x07, 0x00, 0x00, 0x00
};

/*
	This message is the last one sent by the device during initialization.
*/
static const char variax_init_done[] = {
	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
};

M
Markus Grabner 已提交
90 91 92 93
static const char variax_activate[] = {
	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
	0xf7
};
94 95 96 97 98 99 100

/* forward declarations: */
static void variax_startup2(unsigned long data);
static void variax_startup4(unsigned long data);
static void variax_startup5(unsigned long data);

static void variax_activate_async(struct usb_line6_variax *variax, int a)
M
Markus Grabner 已提交
101
{
102
	variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
103 104
	line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
				     sizeof(variax_activate));
M
Markus Grabner 已提交
105 106 107
}

/*
108 109 110 111
	Variax startup procedure.
	This is a sequence of functions with special requirements (e.g., must
	not run immediately after initialization, must not run in interrupt
	context). After the last one has finished, the device is ready to use.
M
Markus Grabner 已提交
112
*/
113 114

static void variax_startup1(struct usb_line6_variax *variax)
M
Markus Grabner 已提交
115
{
116
	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
117 118

	/* delay startup procedure: */
119 120
	line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
			  variax_startup2, (unsigned long)variax);
M
Markus Grabner 已提交
121 122
}

123
static void variax_startup2(unsigned long data)
M
Markus Grabner 已提交
124
{
125 126
	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
	struct usb_line6 *line6 = &variax->line6;
127 128 129 130 131 132 133 134

	/* schedule another startup procedure until startup is complete: */
	if (variax->startup_progress >= VARIAX_STARTUP_LAST)
		return;

	variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
	line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
			  variax_startup2, (unsigned long)variax);
M
Markus Grabner 已提交
135

136 137 138
	/* request firmware version: */
	line6_version_request_async(line6);
}
M
Markus Grabner 已提交
139

140 141
static void variax_startup3(struct usb_line6_variax *variax)
{
142
	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
143 144

	/* delay startup procedure: */
145 146
	line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
			  variax_startup4, (unsigned long)variax);
147 148 149 150 151
}

static void variax_startup4(unsigned long data)
{
	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
152

153 154
	CHECK_STARTUP_PROGRESS(variax->startup_progress,
			       VARIAX_STARTUP_ACTIVATE);
155 156 157

	/* activate device: */
	variax_activate_async(variax, 1);
158 159
	line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
			  variax_startup5, (unsigned long)variax);
160 161 162 163 164
}

static void variax_startup5(unsigned long data)
{
	struct usb_line6_variax *variax = (struct usb_line6_variax *)data;
165

166 167
	CHECK_STARTUP_PROGRESS(variax->startup_progress,
			       VARIAX_STARTUP_WORKQUEUE);
168 169 170 171 172

	/* schedule work for global work queue: */
	schedule_work(&variax->startup_work);
}

173
static void variax_startup6(struct work_struct *work)
174
{
175 176
	struct usb_line6_variax *variax =
	    container_of(work, struct usb_line6_variax, startup_work);
177

178
	CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
179 180

	/* ALSA audio interface: */
181
	snd_card_register(variax->line6.card);
M
Markus Grabner 已提交
182 183 184 185 186
}

/*
	Process a completely received message.
*/
187
static void line6_variax_process_message(struct usb_line6 *line6)
M
Markus Grabner 已提交
188
{
189
	struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
M
Markus Grabner 已提交
190 191
	const unsigned char *buf = variax->line6.buffer_message;

192
	switch (buf[0]) {
M
Markus Grabner 已提交
193 194 195 196 197
	case LINE6_RESET:
		dev_info(variax->line6.ifcdev, "VARIAX reset\n");
		break;

	case LINE6_SYSEX_BEGIN:
198 199
		if (memcmp(buf + 1, variax_init_version + 1,
			   sizeof(variax_init_version) - 1) == 0) {
200 201 202 203 204
			variax_startup3(variax);
		} else if (memcmp(buf + 1, variax_init_done + 1,
				  sizeof(variax_init_done) - 1) == 0) {
			/* notify of complete initialization: */
			variax_startup4((unsigned long)variax);
M
Markus Grabner 已提交
205 206 207 208 209 210 211 212
		}
		break;
	}
}

/*
	Variax destructor.
*/
213
static void line6_variax_disconnect(struct usb_interface *interface)
M
Markus Grabner 已提交
214
{
215 216 217 218
	struct usb_line6_variax *variax;

	if (!interface)
		return;
M
Markus Grabner 已提交
219

220 221
	variax = usb_get_intfdata(interface);
	if (!variax)
222
		return;
M
Markus Grabner 已提交
223

224 225 226 227
	del_timer(&variax->startup_timer1);
	del_timer(&variax->startup_timer2);
	cancel_work_sync(&variax->startup_work);

228
	kfree(variax->buffer_activate);
M
Markus Grabner 已提交
229 230 231
}

/*
232
	 Try to init workbench device.
M
Markus Grabner 已提交
233
*/
234 235
static int variax_init(struct usb_interface *interface,
		       struct usb_line6 *line6)
M
Markus Grabner 已提交
236
{
237
	struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
M
Markus Grabner 已提交
238 239
	int err;

240
	line6->process_message = line6_variax_process_message;
241
	line6->disconnect = line6_variax_disconnect;
242

243 244
	init_timer(&variax->startup_timer1);
	init_timer(&variax->startup_timer2);
245
	INIT_WORK(&variax->startup_work, variax_startup6);
246

247 248
	if ((interface == NULL) || (variax == NULL))
		return -ENODEV;
M
Markus Grabner 已提交
249 250

	/* initialize USB buffers: */
J
Julia Lawall 已提交
251 252
	variax->buffer_activate = kmemdup(variax_activate,
					  sizeof(variax_activate), GFP_KERNEL);
M
Markus Grabner 已提交
253

254
	if (variax->buffer_activate == NULL)
M
Markus Grabner 已提交
255 256 257
		return -ENOMEM;

	/* initialize MIDI subsystem: */
258
	err = line6_init_midi(&variax->line6);
259
	if (err < 0)
M
Markus Grabner 已提交
260 261
		return err;

262 263 264 265 266
	/* initiate startup procedure: */
	variax_startup1(variax);
	return 0;
}

T
Takashi Iwai 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)

/* table of devices that work with this driver */
static const struct usb_device_id variax_id_table[] = {
	{ LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
	{ LINE6_DEVICE(0x534d),    .driver_info = LINE6_VARIAX },
	{}
};

MODULE_DEVICE_TABLE(usb, variax_id_table);

static const struct line6_properties variax_properties_table[] = {
	[LINE6_PODXTLIVE_VARIAX] = {
		.id = "PODxtLive",
		.name = "PODxt Live",
		.capabilities	= LINE6_CAP_CONTROL
				| LINE6_CAP_PCM
				| LINE6_CAP_HWMON,
		.altsetting = 1,
		.ep_ctrl_r = 0x86,
		.ep_ctrl_w = 0x05,
		.ep_audio_r = 0x82,
		.ep_audio_w = 0x01,
	},
	[LINE6_VARIAX] = {
		.id = "Variax",
		.name = "Variax Workbench",
		.capabilities	= LINE6_CAP_CONTROL,
		.altsetting = 1,
		.ep_ctrl_r = 0x82,
		.ep_ctrl_w = 0x01,
		/* no audio channel */
	}
};

/*
	Probe USB device.
*/
static int variax_probe(struct usb_interface *interface,
			const struct usb_device_id *id)
{
	struct usb_line6_variax *variax;

	variax = kzalloc(sizeof(*variax), GFP_KERNEL);
	if (!variax)
		return -ENODEV;
314 315 316
	return line6_probe(interface, &variax->line6,
			   &variax_properties_table[id->driver_info],
			   variax_init);
T
Takashi Iwai 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
}

static struct usb_driver variax_driver = {
	.name = KBUILD_MODNAME,
	.probe = variax_probe,
	.disconnect = line6_disconnect,
#ifdef CONFIG_PM
	.suspend = line6_suspend,
	.resume = line6_resume,
	.reset_resume = line6_resume,
#endif
	.id_table = variax_id_table,
};

module_usb_driver(variax_driver);

MODULE_DESCRIPTION("Vairax Workbench USB driver");
MODULE_LICENSE("GPL");