1 /* 2 * Line 6 Linux USB driver 3 * 4 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 5 * 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 12 #include <linux/slab.h> 13 #include <linux/spinlock.h> 14 #include <linux/usb.h> 15 #include <linux/wait.h> 16 #include <linux/module.h> 17 #include <sound/core.h> 18 19 #include "driver.h" 20 21 #define VARIAX_STARTUP_DELAY1 1000 22 #define VARIAX_STARTUP_DELAY3 100 23 #define VARIAX_STARTUP_DELAY4 100 24 25 /* 26 Stages of Variax startup procedure 27 */ 28 enum { 29 VARIAX_STARTUP_VERSIONREQ, 30 VARIAX_STARTUP_ACTIVATE, 31 VARIAX_STARTUP_SETUP, 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 /* Current progress in startup procedure */ 47 int startup_progress; 48 }; 49 50 #define line6_to_variax(x) container_of(x, struct usb_line6_variax, line6) 51 52 #define VARIAX_OFFSET_ACTIVATE 7 53 54 /* 55 This message is sent by the device during initialization and identifies 56 the connected guitar version. 57 */ 58 static const char variax_init_version[] = { 59 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, 60 0x07, 0x00, 0x00, 0x00 61 }; 62 63 /* 64 This message is the last one sent by the device during initialization. 65 */ 66 static const char variax_init_done[] = { 67 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b 68 }; 69 70 static const char variax_activate[] = { 71 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, 72 0xf7 73 }; 74 75 static void variax_activate_async(struct usb_line6_variax *variax, int a) 76 { 77 variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; 78 line6_send_raw_message_async(&variax->line6, variax->buffer_activate, 79 sizeof(variax_activate)); 80 } 81 82 /* 83 Variax startup procedure. 84 This is a sequence of functions with special requirements (e.g., must 85 not run immediately after initialization, must not run in interrupt 86 context). After the last one has finished, the device is ready to use. 87 */ 88 89 static void variax_startup(struct usb_line6 *line6) 90 { 91 struct usb_line6_variax *variax = line6_to_variax(line6); 92 93 switch (variax->startup_progress) { 94 case VARIAX_STARTUP_VERSIONREQ: 95 /* repeat request until getting the response */ 96 schedule_delayed_work(&line6->startup_work, 97 msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); 98 /* request firmware version: */ 99 line6_version_request_async(line6); 100 break; 101 case VARIAX_STARTUP_ACTIVATE: 102 /* activate device: */ 103 variax_activate_async(variax, 1); 104 variax->startup_progress = VARIAX_STARTUP_SETUP; 105 schedule_delayed_work(&line6->startup_work, 106 msecs_to_jiffies(VARIAX_STARTUP_DELAY4)); 107 break; 108 case VARIAX_STARTUP_SETUP: 109 /* ALSA audio interface: */ 110 snd_card_register(variax->line6.card); 111 break; 112 } 113 } 114 115 /* 116 Process a completely received message. 117 */ 118 static void line6_variax_process_message(struct usb_line6 *line6) 119 { 120 struct usb_line6_variax *variax = line6_to_variax(line6); 121 const unsigned char *buf = variax->line6.buffer_message; 122 123 switch (buf[0]) { 124 case LINE6_RESET: 125 dev_info(variax->line6.ifcdev, "VARIAX reset\n"); 126 break; 127 128 case LINE6_SYSEX_BEGIN: 129 if (memcmp(buf + 1, variax_init_version + 1, 130 sizeof(variax_init_version) - 1) == 0) { 131 if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE) 132 break; 133 variax->startup_progress = VARIAX_STARTUP_ACTIVATE; 134 cancel_delayed_work(&line6->startup_work); 135 schedule_delayed_work(&line6->startup_work, 136 msecs_to_jiffies(VARIAX_STARTUP_DELAY3)); 137 } else if (memcmp(buf + 1, variax_init_done + 1, 138 sizeof(variax_init_done) - 1) == 0) { 139 /* notify of complete initialization: */ 140 if (variax->startup_progress >= VARIAX_STARTUP_SETUP) 141 break; 142 cancel_delayed_work(&line6->startup_work); 143 schedule_delayed_work(&line6->startup_work, 0); 144 } 145 break; 146 } 147 } 148 149 /* 150 Variax destructor. 151 */ 152 static void line6_variax_disconnect(struct usb_line6 *line6) 153 { 154 struct usb_line6_variax *variax = line6_to_variax(line6); 155 156 kfree(variax->buffer_activate); 157 } 158 159 /* 160 Try to init workbench device. 161 */ 162 static int variax_init(struct usb_line6 *line6, 163 const struct usb_device_id *id) 164 { 165 struct usb_line6_variax *variax = line6_to_variax(line6); 166 int err; 167 168 line6->process_message = line6_variax_process_message; 169 line6->disconnect = line6_variax_disconnect; 170 line6->startup = variax_startup; 171 172 /* initialize USB buffers: */ 173 variax->buffer_activate = kmemdup(variax_activate, 174 sizeof(variax_activate), GFP_KERNEL); 175 176 if (variax->buffer_activate == NULL) 177 return -ENOMEM; 178 179 /* initialize MIDI subsystem: */ 180 err = line6_init_midi(&variax->line6); 181 if (err < 0) 182 return err; 183 184 /* initiate startup procedure: */ 185 schedule_delayed_work(&line6->startup_work, 186 msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); 187 return 0; 188 } 189 190 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) 191 #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) 192 193 /* table of devices that work with this driver */ 194 static const struct usb_device_id variax_id_table[] = { 195 { LINE6_IF_NUM(0x4650, 1), .driver_info = LINE6_PODXTLIVE_VARIAX }, 196 { LINE6_DEVICE(0x534d), .driver_info = LINE6_VARIAX }, 197 {} 198 }; 199 200 MODULE_DEVICE_TABLE(usb, variax_id_table); 201 202 static const struct line6_properties variax_properties_table[] = { 203 [LINE6_PODXTLIVE_VARIAX] = { 204 .id = "PODxtLive", 205 .name = "PODxt Live", 206 .capabilities = LINE6_CAP_CONTROL 207 | LINE6_CAP_CONTROL_MIDI, 208 .altsetting = 1, 209 .ep_ctrl_r = 0x86, 210 .ep_ctrl_w = 0x05, 211 .ep_audio_r = 0x82, 212 .ep_audio_w = 0x01, 213 }, 214 [LINE6_VARIAX] = { 215 .id = "Variax", 216 .name = "Variax Workbench", 217 .capabilities = LINE6_CAP_CONTROL 218 | LINE6_CAP_CONTROL_MIDI, 219 .altsetting = 1, 220 .ep_ctrl_r = 0x82, 221 .ep_ctrl_w = 0x01, 222 /* no audio channel */ 223 } 224 }; 225 226 /* 227 Probe USB device. 228 */ 229 static int variax_probe(struct usb_interface *interface, 230 const struct usb_device_id *id) 231 { 232 return line6_probe(interface, id, "Line6-Variax", 233 &variax_properties_table[id->driver_info], 234 variax_init, sizeof(struct usb_line6_variax)); 235 } 236 237 static struct usb_driver variax_driver = { 238 .name = KBUILD_MODNAME, 239 .probe = variax_probe, 240 .disconnect = line6_disconnect, 241 #ifdef CONFIG_PM 242 .suspend = line6_suspend, 243 .resume = line6_resume, 244 .reset_resume = line6_resume, 245 #endif 246 .id_table = variax_id_table, 247 }; 248 249 module_usb_driver(variax_driver); 250 251 MODULE_DESCRIPTION("Vairax Workbench USB driver"); 252 MODULE_LICENSE("GPL"); 253