xref: /linux/sound/usb/line6/variax.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2705ececdSMarkus Grabner /*
3c078a4aaSChris Rorvick  * Line 6 Linux USB driver
4705ececdSMarkus Grabner  *
51027f476SMarkus Grabner  * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at)
6705ececdSMarkus Grabner  */
7705ececdSMarkus Grabner 
85a0e3ad6STejun Heo #include <linux/slab.h>
9ccddbe4aSTakashi Iwai #include <linux/spinlock.h>
10ccddbe4aSTakashi Iwai #include <linux/usb.h>
11ccddbe4aSTakashi Iwai #include <linux/wait.h>
12ccddbe4aSTakashi Iwai #include <linux/module.h>
13ccddbe4aSTakashi Iwai #include <sound/core.h>
145a0e3ad6STejun Heo 
151027f476SMarkus Grabner #include "driver.h"
16ccddbe4aSTakashi Iwai 
17ccddbe4aSTakashi Iwai #define VARIAX_STARTUP_DELAY1 1000
18ccddbe4aSTakashi Iwai #define VARIAX_STARTUP_DELAY3 100
19ccddbe4aSTakashi Iwai #define VARIAX_STARTUP_DELAY4 100
20ccddbe4aSTakashi Iwai 
21ccddbe4aSTakashi Iwai /*
22ccddbe4aSTakashi Iwai 	Stages of Variax startup procedure
23ccddbe4aSTakashi Iwai */
24ccddbe4aSTakashi Iwai enum {
25ccddbe4aSTakashi Iwai 	VARIAX_STARTUP_VERSIONREQ,
26ccddbe4aSTakashi Iwai 	VARIAX_STARTUP_ACTIVATE,
27ccddbe4aSTakashi Iwai 	VARIAX_STARTUP_SETUP,
28ccddbe4aSTakashi Iwai };
29ccddbe4aSTakashi Iwai 
30ccddbe4aSTakashi Iwai enum {
31ccddbe4aSTakashi Iwai 	LINE6_PODXTLIVE_VARIAX,
32ccddbe4aSTakashi Iwai 	LINE6_VARIAX
33ccddbe4aSTakashi Iwai };
34ccddbe4aSTakashi Iwai 
35ccddbe4aSTakashi Iwai struct usb_line6_variax {
36cddbd4f1STakashi Iwai 	/* Generic Line 6 USB data */
37ccddbe4aSTakashi Iwai 	struct usb_line6 line6;
38ccddbe4aSTakashi Iwai 
39cddbd4f1STakashi Iwai 	/* Buffer for activation code */
40ccddbe4aSTakashi Iwai 	unsigned char *buffer_activate;
41ccddbe4aSTakashi Iwai 
42cddbd4f1STakashi Iwai 	/* Current progress in startup procedure */
43ccddbe4aSTakashi Iwai 	int startup_progress;
44ccddbe4aSTakashi Iwai };
45705ececdSMarkus Grabner 
46f23a09eeSTakashi Iwai #define line6_to_variax(x)	container_of(x, struct usb_line6_variax, line6)
47f23a09eeSTakashi Iwai 
48705ececdSMarkus Grabner #define VARIAX_OFFSET_ACTIVATE 7
49705ececdSMarkus Grabner 
501027f476SMarkus Grabner /*
511027f476SMarkus Grabner 	This message is sent by the device during initialization and identifies
521027f476SMarkus Grabner 	the connected guitar version.
531027f476SMarkus Grabner */
541027f476SMarkus Grabner static const char variax_init_version[] = {
551027f476SMarkus Grabner 	0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c,
561027f476SMarkus Grabner 	0x07, 0x00, 0x00, 0x00
571027f476SMarkus Grabner };
581027f476SMarkus Grabner 
591027f476SMarkus Grabner /*
601027f476SMarkus Grabner 	This message is the last one sent by the device during initialization.
611027f476SMarkus Grabner */
621027f476SMarkus Grabner static const char variax_init_done[] = {
631027f476SMarkus Grabner 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b
641027f476SMarkus Grabner };
651027f476SMarkus Grabner 
66705ececdSMarkus Grabner static const char variax_activate[] = {
67705ececdSMarkus Grabner 	0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01,
68705ececdSMarkus Grabner 	0xf7
69705ececdSMarkus Grabner };
701027f476SMarkus Grabner 
variax_activate_async(struct usb_line6_variax * variax,int a)711027f476SMarkus Grabner static void variax_activate_async(struct usb_line6_variax *variax, int a)
72705ececdSMarkus Grabner {
731027f476SMarkus Grabner 	variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
749cd57f77SGreg Kroah-Hartman 	line6_send_raw_message_async(&variax->line6, variax->buffer_activate,
759cd57f77SGreg Kroah-Hartman 				     sizeof(variax_activate));
76705ececdSMarkus Grabner }
77705ececdSMarkus Grabner 
78705ececdSMarkus Grabner /*
791027f476SMarkus Grabner 	Variax startup procedure.
801027f476SMarkus Grabner 	This is a sequence of functions with special requirements (e.g., must
811027f476SMarkus Grabner 	not run immediately after initialization, must not run in interrupt
821027f476SMarkus Grabner 	context). After the last one has finished, the device is ready to use.
83705ececdSMarkus Grabner */
841027f476SMarkus Grabner 
variax_startup(struct usb_line6 * line6)856ea53391STakashi Iwai static void variax_startup(struct usb_line6 *line6)
86705ececdSMarkus Grabner {
87f23a09eeSTakashi Iwai 	struct usb_line6_variax *variax = line6_to_variax(line6);
881027f476SMarkus Grabner 
896ea53391STakashi Iwai 	switch (variax->startup_progress) {
906ea53391STakashi Iwai 	case VARIAX_STARTUP_VERSIONREQ:
916ea53391STakashi Iwai 		/* repeat request until getting the response */
926ea53391STakashi Iwai 		schedule_delayed_work(&line6->startup_work,
936ea53391STakashi Iwai 				      msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
941027f476SMarkus Grabner 		/* request firmware version: */
951027f476SMarkus Grabner 		line6_version_request_async(line6);
966ea53391STakashi Iwai 		break;
976ea53391STakashi Iwai 	case VARIAX_STARTUP_ACTIVATE:
981027f476SMarkus Grabner 		/* activate device: */
991027f476SMarkus Grabner 		variax_activate_async(variax, 1);
1006ea53391STakashi Iwai 		variax->startup_progress = VARIAX_STARTUP_SETUP;
1016ea53391STakashi Iwai 		schedule_delayed_work(&line6->startup_work,
1026ea53391STakashi Iwai 				      msecs_to_jiffies(VARIAX_STARTUP_DELAY4));
1036ea53391STakashi Iwai 		break;
1046ea53391STakashi Iwai 	case VARIAX_STARTUP_SETUP:
1051027f476SMarkus Grabner 		/* ALSA audio interface: */
10685a9339bSTakashi Iwai 		snd_card_register(variax->line6.card);
1076ea53391STakashi Iwai 		break;
1086ea53391STakashi Iwai 	}
109705ececdSMarkus Grabner }
110705ececdSMarkus Grabner 
111705ececdSMarkus Grabner /*
112705ececdSMarkus Grabner 	Process a completely received message.
113705ececdSMarkus Grabner */
line6_variax_process_message(struct usb_line6 * line6)11401f6b2bcSChris Rorvick static void line6_variax_process_message(struct usb_line6 *line6)
115705ececdSMarkus Grabner {
116f23a09eeSTakashi Iwai 	struct usb_line6_variax *variax = line6_to_variax(line6);
117705ececdSMarkus Grabner 	const unsigned char *buf = variax->line6.buffer_message;
118705ececdSMarkus Grabner 
119705ececdSMarkus Grabner 	switch (buf[0]) {
120705ececdSMarkus Grabner 	case LINE6_RESET:
121705ececdSMarkus Grabner 		dev_info(variax->line6.ifcdev, "VARIAX reset\n");
122705ececdSMarkus Grabner 		break;
123705ececdSMarkus Grabner 
124705ececdSMarkus Grabner 	case LINE6_SYSEX_BEGIN:
125323246b2SStefan Hajnoczi 		if (memcmp(buf + 1, variax_init_version + 1,
1261027f476SMarkus Grabner 			   sizeof(variax_init_version) - 1) == 0) {
1276ea53391STakashi Iwai 			if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE)
1286ea53391STakashi Iwai 				break;
1296ea53391STakashi Iwai 			variax->startup_progress = VARIAX_STARTUP_ACTIVATE;
1306ea53391STakashi Iwai 			cancel_delayed_work(&line6->startup_work);
1316ea53391STakashi Iwai 			schedule_delayed_work(&line6->startup_work,
1326ea53391STakashi Iwai 					      msecs_to_jiffies(VARIAX_STARTUP_DELAY3));
1331027f476SMarkus Grabner 		} else if (memcmp(buf + 1, variax_init_done + 1,
1341027f476SMarkus Grabner 				  sizeof(variax_init_done) - 1) == 0) {
1351027f476SMarkus Grabner 			/* notify of complete initialization: */
1366ea53391STakashi Iwai 			if (variax->startup_progress >= VARIAX_STARTUP_SETUP)
1376ea53391STakashi Iwai 				break;
1386ea53391STakashi Iwai 			cancel_delayed_work(&line6->startup_work);
1396ea53391STakashi Iwai 			schedule_delayed_work(&line6->startup_work, 0);
140705ececdSMarkus Grabner 		}
141705ececdSMarkus Grabner 		break;
142705ececdSMarkus Grabner 	}
143705ececdSMarkus Grabner }
144705ececdSMarkus Grabner 
145705ececdSMarkus Grabner /*
146705ececdSMarkus Grabner 	Variax destructor.
147705ececdSMarkus Grabner */
line6_variax_disconnect(struct usb_line6 * line6)148f66fd990STakashi Iwai static void line6_variax_disconnect(struct usb_line6 *line6)
149705ececdSMarkus Grabner {
150f23a09eeSTakashi Iwai 	struct usb_line6_variax *variax = line6_to_variax(line6);
151705ececdSMarkus Grabner 
1529cd57f77SGreg Kroah-Hartman 	kfree(variax->buffer_activate);
153705ececdSMarkus Grabner }
154705ececdSMarkus Grabner 
155705ececdSMarkus Grabner /*
1561027f476SMarkus Grabner 	 Try to init workbench device.
157705ececdSMarkus Grabner */
variax_init(struct usb_line6 * line6,const struct usb_device_id * id)158f66fd990STakashi Iwai static int variax_init(struct usb_line6 *line6,
159f66fd990STakashi Iwai 		       const struct usb_device_id *id)
160705ececdSMarkus Grabner {
161f23a09eeSTakashi Iwai 	struct usb_line6_variax *variax = line6_to_variax(line6);
162705ececdSMarkus Grabner 
16301f6b2bcSChris Rorvick 	line6->process_message = line6_variax_process_message;
164a46c4672SChris Rorvick 	line6->disconnect = line6_variax_disconnect;
1656ea53391STakashi Iwai 	line6->startup = variax_startup;
166e1a164d7SMarkus Grabner 
167705ececdSMarkus Grabner 	/* initialize USB buffers: */
16894002c07SJulia Lawall 	variax->buffer_activate = kmemdup(variax_activate,
16994002c07SJulia Lawall 					  sizeof(variax_activate), GFP_KERNEL);
170705ececdSMarkus Grabner 
171a019f5e8STakashi Iwai 	if (variax->buffer_activate == NULL)
172705ececdSMarkus Grabner 		return -ENOMEM;
173705ececdSMarkus Grabner 
1741027f476SMarkus Grabner 	/* initiate startup procedure: */
1756ea53391STakashi Iwai 	schedule_delayed_work(&line6->startup_work,
1766ea53391STakashi Iwai 			      msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
1771027f476SMarkus Grabner 	return 0;
1781027f476SMarkus Grabner }
1791027f476SMarkus Grabner 
180ccddbe4aSTakashi Iwai #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
181ccddbe4aSTakashi Iwai #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
182ccddbe4aSTakashi Iwai 
183ccddbe4aSTakashi Iwai /* table of devices that work with this driver */
184ccddbe4aSTakashi Iwai static const struct usb_device_id variax_id_table[] = {
185ccddbe4aSTakashi Iwai 	{ LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX },
186ccddbe4aSTakashi Iwai 	{ LINE6_DEVICE(0x534d),    .driver_info = LINE6_VARIAX },
187ccddbe4aSTakashi Iwai 	{}
188ccddbe4aSTakashi Iwai };
189ccddbe4aSTakashi Iwai 
190ccddbe4aSTakashi Iwai MODULE_DEVICE_TABLE(usb, variax_id_table);
191ccddbe4aSTakashi Iwai 
192ccddbe4aSTakashi Iwai static const struct line6_properties variax_properties_table[] = {
193ccddbe4aSTakashi Iwai 	[LINE6_PODXTLIVE_VARIAX] = {
194ccddbe4aSTakashi Iwai 		.id = "PODxtLive",
195ccddbe4aSTakashi Iwai 		.name = "PODxt Live",
196174e1fc0SAndrej Krutak 		.capabilities	= LINE6_CAP_CONTROL
197174e1fc0SAndrej Krutak 				| LINE6_CAP_CONTROL_MIDI,
198ccddbe4aSTakashi Iwai 		.altsetting = 1,
199ccddbe4aSTakashi Iwai 		.ep_ctrl_r = 0x86,
200ccddbe4aSTakashi Iwai 		.ep_ctrl_w = 0x05,
201ccddbe4aSTakashi Iwai 		.ep_audio_r = 0x82,
202ccddbe4aSTakashi Iwai 		.ep_audio_w = 0x01,
203ccddbe4aSTakashi Iwai 	},
204ccddbe4aSTakashi Iwai 	[LINE6_VARIAX] = {
205ccddbe4aSTakashi Iwai 		.id = "Variax",
206ccddbe4aSTakashi Iwai 		.name = "Variax Workbench",
207174e1fc0SAndrej Krutak 		.capabilities	= LINE6_CAP_CONTROL
208174e1fc0SAndrej Krutak 				| LINE6_CAP_CONTROL_MIDI,
209ccddbe4aSTakashi Iwai 		.altsetting = 1,
210ccddbe4aSTakashi Iwai 		.ep_ctrl_r = 0x82,
211ccddbe4aSTakashi Iwai 		.ep_ctrl_w = 0x01,
212ccddbe4aSTakashi Iwai 		/* no audio channel */
213ccddbe4aSTakashi Iwai 	}
214ccddbe4aSTakashi Iwai };
215ccddbe4aSTakashi Iwai 
216ccddbe4aSTakashi Iwai /*
217ccddbe4aSTakashi Iwai 	Probe USB device.
218ccddbe4aSTakashi Iwai */
variax_probe(struct usb_interface * interface,const struct usb_device_id * id)219ccddbe4aSTakashi Iwai static int variax_probe(struct usb_interface *interface,
220ccddbe4aSTakashi Iwai 			const struct usb_device_id *id)
221ccddbe4aSTakashi Iwai {
22212865cacSChris Rorvick 	return line6_probe(interface, id, "Line6-Variax",
223ccddbe4aSTakashi Iwai 			   &variax_properties_table[id->driver_info],
224aca514b8STakashi Iwai 			   variax_init, sizeof(struct usb_line6_variax));
225ccddbe4aSTakashi Iwai }
226ccddbe4aSTakashi Iwai 
227ccddbe4aSTakashi Iwai static struct usb_driver variax_driver = {
228ccddbe4aSTakashi Iwai 	.name = KBUILD_MODNAME,
229ccddbe4aSTakashi Iwai 	.probe = variax_probe,
230ccddbe4aSTakashi Iwai 	.disconnect = line6_disconnect,
231ccddbe4aSTakashi Iwai #ifdef CONFIG_PM
232ccddbe4aSTakashi Iwai 	.suspend = line6_suspend,
233ccddbe4aSTakashi Iwai 	.resume = line6_resume,
234ccddbe4aSTakashi Iwai 	.reset_resume = line6_resume,
235ccddbe4aSTakashi Iwai #endif
236ccddbe4aSTakashi Iwai 	.id_table = variax_id_table,
237ccddbe4aSTakashi Iwai };
238ccddbe4aSTakashi Iwai 
239ccddbe4aSTakashi Iwai module_usb_driver(variax_driver);
240ccddbe4aSTakashi Iwai 
241*e4091bddSChristophe JAILLET MODULE_DESCRIPTION("Variax Workbench USB driver");
242ccddbe4aSTakashi Iwai MODULE_LICENSE("GPL");
243