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