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 */ 24 enum { 25 VARIAX_STARTUP_INIT = 1, 26 VARIAX_STARTUP_VERSIONREQ, 27 VARIAX_STARTUP_WAIT, 28 VARIAX_STARTUP_ACTIVATE, 29 VARIAX_STARTUP_WORKQUEUE, 30 VARIAX_STARTUP_SETUP, 31 VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 32 }; 33 34 enum { 35 LINE6_PODXTLIVE_VARIAX, 36 LINE6_VARIAX 37 }; 38 39 struct usb_line6_variax { 40 /* Generic Line 6 USB data */ 41 struct usb_line6 line6; 42 43 /* Buffer for activation code */ 44 unsigned char *buffer_activate; 45 46 /* Handler for device initialization */ 47 struct work_struct startup_work; 48 49 /* Timers for device initialization */ 50 struct timer_list startup_timer1; 51 struct timer_list startup_timer2; 52 53 /* Current progress in startup procedure */ 54 int startup_progress; 55 }; 56 57 #define VARIAX_OFFSET_ACTIVATE 7 58 59 /* 60 This message is sent by the device during initialization and identifies 61 the connected guitar version. 62 */ 63 static const char variax_init_version[] = { 64 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, 65 0x07, 0x00, 0x00, 0x00 66 }; 67 68 /* 69 This message is the last one sent by the device during initialization. 70 */ 71 static const char variax_init_done[] = { 72 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b 73 }; 74 75 static const char variax_activate[] = { 76 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, 77 0xf7 78 }; 79 80 /* forward declarations: */ 81 static void variax_startup2(struct timer_list *t); 82 static void variax_startup4(struct timer_list *t); 83 static void variax_startup5(struct timer_list *t); 84 85 static void variax_activate_async(struct usb_line6_variax *variax, int a) 86 { 87 variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; 88 line6_send_raw_message_async(&variax->line6, variax->buffer_activate, 89 sizeof(variax_activate)); 90 } 91 92 /* 93 Variax startup procedure. 94 This is a sequence of functions with special requirements (e.g., must 95 not run immediately after initialization, must not run in interrupt 96 context). After the last one has finished, the device is ready to use. 97 */ 98 99 static void variax_startup1(struct usb_line6_variax *variax) 100 { 101 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); 102 103 /* delay startup procedure: */ 104 line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, 105 variax_startup2); 106 } 107 108 static void variax_startup2(struct timer_list *t) 109 { 110 struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1); 111 struct usb_line6 *line6 = &variax->line6; 112 113 /* schedule another startup procedure until startup is complete: */ 114 if (variax->startup_progress >= VARIAX_STARTUP_LAST) 115 return; 116 117 variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; 118 line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, 119 variax_startup2); 120 121 /* request firmware version: */ 122 line6_version_request_async(line6); 123 } 124 125 static void variax_startup3(struct usb_line6_variax *variax) 126 { 127 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); 128 129 /* delay startup procedure: */ 130 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, 131 variax_startup4); 132 } 133 134 static void variax_startup4(struct timer_list *t) 135 { 136 struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); 137 138 CHECK_STARTUP_PROGRESS(variax->startup_progress, 139 VARIAX_STARTUP_ACTIVATE); 140 141 /* activate device: */ 142 variax_activate_async(variax, 1); 143 line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, 144 variax_startup5); 145 } 146 147 static void variax_startup5(struct timer_list *t) 148 { 149 struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); 150 151 CHECK_STARTUP_PROGRESS(variax->startup_progress, 152 VARIAX_STARTUP_WORKQUEUE); 153 154 /* schedule work for global work queue: */ 155 schedule_work(&variax->startup_work); 156 } 157 158 static void variax_startup6(struct work_struct *work) 159 { 160 struct usb_line6_variax *variax = 161 container_of(work, struct usb_line6_variax, startup_work); 162 163 CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); 164 165 /* ALSA audio interface: */ 166 snd_card_register(variax->line6.card); 167 } 168 169 /* 170 Process a completely received message. 171 */ 172 static void line6_variax_process_message(struct usb_line6 *line6) 173 { 174 struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; 175 const unsigned char *buf = variax->line6.buffer_message; 176 177 switch (buf[0]) { 178 case LINE6_RESET: 179 dev_info(variax->line6.ifcdev, "VARIAX reset\n"); 180 break; 181 182 case LINE6_SYSEX_BEGIN: 183 if (memcmp(buf + 1, variax_init_version + 1, 184 sizeof(variax_init_version) - 1) == 0) { 185 variax_startup3(variax); 186 } else if (memcmp(buf + 1, variax_init_done + 1, 187 sizeof(variax_init_done) - 1) == 0) { 188 /* notify of complete initialization: */ 189 variax_startup4(&variax->startup_timer2); 190 } 191 break; 192 } 193 } 194 195 /* 196 Variax destructor. 197 */ 198 static void line6_variax_disconnect(struct usb_line6 *line6) 199 { 200 struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; 201 202 del_timer(&variax->startup_timer1); 203 del_timer(&variax->startup_timer2); 204 cancel_work_sync(&variax->startup_work); 205 206 kfree(variax->buffer_activate); 207 } 208 209 /* 210 Try to init workbench device. 211 */ 212 static int variax_init(struct usb_line6 *line6, 213 const struct usb_device_id *id) 214 { 215 struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; 216 int err; 217 218 line6->process_message = line6_variax_process_message; 219 line6->disconnect = line6_variax_disconnect; 220 221 timer_setup(&variax->startup_timer1, NULL, 0); 222 timer_setup(&variax->startup_timer2, NULL, 0); 223 INIT_WORK(&variax->startup_work, variax_startup6); 224 225 /* initialize USB buffers: */ 226 variax->buffer_activate = kmemdup(variax_activate, 227 sizeof(variax_activate), GFP_KERNEL); 228 229 if (variax->buffer_activate == NULL) 230 return -ENOMEM; 231 232 /* initialize MIDI subsystem: */ 233 err = line6_init_midi(&variax->line6); 234 if (err < 0) 235 return err; 236 237 /* initiate startup procedure: */ 238 variax_startup1(variax); 239 return 0; 240 } 241 242 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) 243 #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) 244 245 /* table of devices that work with this driver */ 246 static const struct usb_device_id variax_id_table[] = { 247 { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, 248 { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, 249 {} 250 }; 251 252 MODULE_DEVICE_TABLE(usb, variax_id_table); 253 254 static const struct line6_properties variax_properties_table[] = { 255 [LINE6_PODXTLIVE_VARIAX] = { 256 .id = "PODxtLive", 257 .name = "PODxt Live", 258 .capabilities = LINE6_CAP_CONTROL 259 | LINE6_CAP_CONTROL_MIDI, 260 .altsetting = 1, 261 .ep_ctrl_r = 0x86, 262 .ep_ctrl_w = 0x05, 263 .ep_audio_r = 0x82, 264 .ep_audio_w = 0x01, 265 }, 266 [LINE6_VARIAX] = { 267 .id = "Variax", 268 .name = "Variax Workbench", 269 .capabilities = LINE6_CAP_CONTROL 270 | LINE6_CAP_CONTROL_MIDI, 271 .altsetting = 1, 272 .ep_ctrl_r = 0x82, 273 .ep_ctrl_w = 0x01, 274 /* no audio channel */ 275 } 276 }; 277 278 /* 279 Probe USB device. 280 */ 281 static int variax_probe(struct usb_interface *interface, 282 const struct usb_device_id *id) 283 { 284 return line6_probe(interface, id, "Line6-Variax", 285 &variax_properties_table[id->driver_info], 286 variax_init, sizeof(struct usb_line6_variax)); 287 } 288 289 static struct usb_driver variax_driver = { 290 .name = KBUILD_MODNAME, 291 .probe = variax_probe, 292 .disconnect = line6_disconnect, 293 #ifdef CONFIG_PM 294 .suspend = line6_suspend, 295 .resume = line6_resume, 296 .reset_resume = line6_resume, 297 #endif 298 .id_table = variax_id_table, 299 }; 300 301 module_usb_driver(variax_driver); 302 303 MODULE_DESCRIPTION("Vairax Workbench USB driver"); 304 MODULE_LICENSE("GPL"); 305