linux/sound/usb/6fire/control.c
<<
>>
Prefs
   1/*
   2 * Linux driver for TerraTec DMX 6Fire USB
   3 *
   4 * Mixer control
   5 *
   6 * Author:      Torsten Schenk <torsten.schenk@zoho.com>
   7 * Created:     Jan 01, 2011
   8 * Copyright:   (C) Torsten Schenk
   9 *
  10 * Thanks to:
  11 * - Holger Ruckdeschel: he found out how to control individual channel
  12 *   volumes and introduced mute switch
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation; either version 2 of the License, or
  17 * (at your option) any later version.
  18 */
  19
  20#include <linux/interrupt.h>
  21#include <sound/control.h>
  22#include <sound/tlv.h>
  23
  24#include "control.h"
  25#include "comm.h"
  26#include "chip.h"
  27
  28static const char * const opt_coax_texts[2] = { "Optical", "Coax" };
  29static const char * const line_phono_texts[2] = { "Line", "Phono" };
  30
  31/*
  32 * data that needs to be sent to device. sets up card internal stuff.
  33 * values dumped from windows driver and filtered by trial'n'error.
  34 */
  35static const struct {
  36        u8 type;
  37        u8 reg;
  38        u8 value;
  39}
  40init_data[] = {
  41        { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },
  42        { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },
  43        { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
  44        { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
  45        { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
  46        { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
  47        { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
  48        { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
  49        { 0 } /* TERMINATING ENTRY */
  50};
  51
  52static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
  53/* values to write to soundcard register for all samplerates */
  54static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
  55static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
  56
  57static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
  58static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);
  59
  60enum {
  61        DIGITAL_THRU_ONLY_SAMPLERATE = 3
  62};
  63
  64static void usb6fire_control_output_vol_update(struct control_runtime *rt)
  65{
  66        struct comm_runtime *comm_rt = rt->chip->comm;
  67        int i;
  68
  69        if (comm_rt)
  70                for (i = 0; i < 6; i++)
  71                        if (!(rt->ovol_updated & (1 << i))) {
  72                                comm_rt->write8(comm_rt, 0x12, 0x0f + i,
  73                                        180 - rt->output_vol[i]);
  74                                rt->ovol_updated |= 1 << i;
  75                        }
  76}
  77
  78static void usb6fire_control_output_mute_update(struct control_runtime *rt)
  79{
  80        struct comm_runtime *comm_rt = rt->chip->comm;
  81
  82        if (comm_rt)
  83                comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);
  84}
  85
  86static void usb6fire_control_input_vol_update(struct control_runtime *rt)
  87{
  88        struct comm_runtime *comm_rt = rt->chip->comm;
  89        int i;
  90
  91        if (comm_rt)
  92                for (i = 0; i < 2; i++)
  93                        if (!(rt->ivol_updated & (1 << i))) {
  94                                comm_rt->write8(comm_rt, 0x12, 0x1c + i,
  95                                        rt->input_vol[i] & 0x3f);
  96                                rt->ivol_updated |= 1 << i;
  97                        }
  98}
  99
 100static void usb6fire_control_line_phono_update(struct control_runtime *rt)
 101{
 102        struct comm_runtime *comm_rt = rt->chip->comm;
 103        if (comm_rt) {
 104                comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
 105                comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
 106        }
 107}
 108
 109static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
 110{
 111        struct comm_runtime *comm_rt = rt->chip->comm;
 112        if (comm_rt) {
 113                comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
 114                comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
 115        }
 116}
 117
 118static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
 119{
 120        int ret;
 121        struct usb_device *device = rt->chip->dev;
 122        struct comm_runtime *comm_rt = rt->chip->comm;
 123
 124        if (rate < 0 || rate >= CONTROL_N_RATES)
 125                return -EINVAL;
 126
 127        ret = usb_set_interface(device, 1, rates_altsetting[rate]);
 128        if (ret < 0)
 129                return ret;
 130
 131        /* set soundcard clock */
 132        ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
 133                        rates_6fire_vh[rate]);
 134        if (ret < 0)
 135                return ret;
 136
 137        return 0;
 138}
 139
 140static int usb6fire_control_set_channels(
 141        struct control_runtime *rt, int n_analog_out,
 142        int n_analog_in, bool spdif_out, bool spdif_in)
 143{
 144        int ret;
 145        struct comm_runtime *comm_rt = rt->chip->comm;
 146
 147        /* enable analog inputs and outputs
 148         * (one bit per stereo-channel) */
 149        ret = comm_rt->write16(comm_rt, 0x02, 0x02,
 150                        (1 << (n_analog_out / 2)) - 1,
 151                        (1 << (n_analog_in / 2)) - 1);
 152        if (ret < 0)
 153                return ret;
 154
 155        /* disable digital inputs and outputs */
 156        /* TODO: use spdif_x to enable/disable digital channels */
 157        ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
 158        if (ret < 0)
 159                return ret;
 160
 161        return 0;
 162}
 163
 164static int usb6fire_control_streaming_update(struct control_runtime *rt)
 165{
 166        struct comm_runtime *comm_rt = rt->chip->comm;
 167
 168        if (comm_rt) {
 169                if (!rt->usb_streaming && rt->digital_thru_switch)
 170                        usb6fire_control_set_rate(rt,
 171                                DIGITAL_THRU_ONLY_SAMPLERATE);
 172                return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
 173                        (rt->usb_streaming ? 0x01 : 0x00) |
 174                        (rt->digital_thru_switch ? 0x08 : 0x00));
 175        }
 176        return -EINVAL;
 177}
 178
 179static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
 180                struct snd_ctl_elem_info *uinfo)
 181{
 182        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 183        uinfo->count = 2;
 184        uinfo->value.integer.min = 0;
 185        uinfo->value.integer.max = 180;
 186        return 0;
 187}
 188
 189static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
 190                struct snd_ctl_elem_value *ucontrol)
 191{
 192        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 193        unsigned int ch = kcontrol->private_value;
 194        int changed = 0;
 195
 196        if (ch > 4) {
 197                dev_err(&rt->chip->dev->dev,
 198                        "Invalid channel in volume control.");
 199                return -EINVAL;
 200        }
 201
 202        if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {
 203                rt->output_vol[ch] = ucontrol->value.integer.value[0];
 204                rt->ovol_updated &= ~(1 << ch);
 205                changed = 1;
 206        }
 207        if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {
 208                rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];
 209                rt->ovol_updated &= ~(2 << ch);
 210                changed = 1;
 211        }
 212
 213        if (changed)
 214                usb6fire_control_output_vol_update(rt);
 215
 216        return changed;
 217}
 218
 219static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
 220                struct snd_ctl_elem_value *ucontrol)
 221{
 222        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 223        unsigned int ch = kcontrol->private_value;
 224
 225        if (ch > 4) {
 226                dev_err(&rt->chip->dev->dev,
 227                        "Invalid channel in volume control.");
 228                return -EINVAL;
 229        }
 230
 231        ucontrol->value.integer.value[0] = rt->output_vol[ch];
 232        ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];
 233        return 0;
 234}
 235
 236static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,
 237        struct snd_ctl_elem_value *ucontrol)
 238{
 239        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 240        unsigned int ch = kcontrol->private_value;
 241        u8 old = rt->output_mute;
 242        u8 value = 0;
 243
 244        if (ch > 4) {
 245                dev_err(&rt->chip->dev->dev,
 246                        "Invalid channel in volume control.");
 247                return -EINVAL;
 248        }
 249
 250        rt->output_mute &= ~(3 << ch);
 251        if (ucontrol->value.integer.value[0])
 252                value |= 1;
 253        if (ucontrol->value.integer.value[1])
 254                value |= 2;
 255        rt->output_mute |= value << ch;
 256
 257        if (rt->output_mute != old)
 258                usb6fire_control_output_mute_update(rt);
 259
 260        return rt->output_mute != old;
 261}
 262
 263static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,
 264        struct snd_ctl_elem_value *ucontrol)
 265{
 266        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 267        unsigned int ch = kcontrol->private_value;
 268        u8 value = rt->output_mute >> ch;
 269
 270        if (ch > 4) {
 271                dev_err(&rt->chip->dev->dev,
 272                        "Invalid channel in volume control.");
 273                return -EINVAL;
 274        }
 275
 276        ucontrol->value.integer.value[0] = 1 & value;
 277        value >>= 1;
 278        ucontrol->value.integer.value[1] = 1 & value;
 279
 280        return 0;
 281}
 282
 283static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol,
 284                struct snd_ctl_elem_info *uinfo)
 285{
 286        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 287        uinfo->count = 2;
 288        uinfo->value.integer.min = 0;
 289        uinfo->value.integer.max = 30;
 290        return 0;
 291}
 292
 293static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,
 294                struct snd_ctl_elem_value *ucontrol)
 295{
 296        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 297        int changed = 0;
 298
 299        if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {
 300                rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;
 301                rt->ivol_updated &= ~(1 << 0);
 302                changed = 1;
 303        }
 304        if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {
 305                rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;
 306                rt->ivol_updated &= ~(1 << 1);
 307                changed = 1;
 308        }
 309
 310        if (changed)
 311                usb6fire_control_input_vol_update(rt);
 312
 313        return changed;
 314}
 315
 316static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,
 317                struct snd_ctl_elem_value *ucontrol)
 318{
 319        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 320
 321        ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;
 322        ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;
 323
 324        return 0;
 325}
 326
 327static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
 328                struct snd_ctl_elem_info *uinfo)
 329{
 330        return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts);
 331}
 332
 333static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
 334                struct snd_ctl_elem_value *ucontrol)
 335{
 336        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 337        int changed = 0;
 338        if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
 339                rt->line_phono_switch = ucontrol->value.integer.value[0];
 340                usb6fire_control_line_phono_update(rt);
 341                changed = 1;
 342        }
 343        return changed;
 344}
 345
 346static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,
 347                struct snd_ctl_elem_value *ucontrol)
 348{
 349        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 350        ucontrol->value.integer.value[0] = rt->line_phono_switch;
 351        return 0;
 352}
 353
 354static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
 355                struct snd_ctl_elem_info *uinfo)
 356{
 357        return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts);
 358}
 359
 360static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
 361                struct snd_ctl_elem_value *ucontrol)
 362{
 363        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 364        int changed = 0;
 365
 366        if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
 367                rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
 368                usb6fire_control_opt_coax_update(rt);
 369                changed = 1;
 370        }
 371        return changed;
 372}
 373
 374static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
 375                struct snd_ctl_elem_value *ucontrol)
 376{
 377        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 378        ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
 379        return 0;
 380}
 381
 382static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
 383                struct snd_ctl_elem_value *ucontrol)
 384{
 385        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 386        int changed = 0;
 387
 388        if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
 389                rt->digital_thru_switch = ucontrol->value.integer.value[0];
 390                usb6fire_control_streaming_update(rt);
 391                changed = 1;
 392        }
 393        return changed;
 394}
 395
 396static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
 397                struct snd_ctl_elem_value *ucontrol)
 398{
 399        struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
 400        ucontrol->value.integer.value[0] = rt->digital_thru_switch;
 401        return 0;
 402}
 403
 404static struct snd_kcontrol_new vol_elements[] = {
 405        {
 406                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 407                .name = "Analog Playback Volume",
 408                .index = 0,
 409                .private_value = 0,
 410                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 411                        SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 412                .info = usb6fire_control_output_vol_info,
 413                .get = usb6fire_control_output_vol_get,
 414                .put = usb6fire_control_output_vol_put,
 415                .tlv = { .p = tlv_output }
 416        },
 417        {
 418                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 419                .name = "Analog Playback Volume",
 420                .index = 1,
 421                .private_value = 2,
 422                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 423                        SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 424                .info = usb6fire_control_output_vol_info,
 425                .get = usb6fire_control_output_vol_get,
 426                .put = usb6fire_control_output_vol_put,
 427                .tlv = { .p = tlv_output }
 428        },
 429        {
 430                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 431                .name = "Analog Playback Volume",
 432                .index = 2,
 433                .private_value = 4,
 434                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 435                        SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 436                .info = usb6fire_control_output_vol_info,
 437                .get = usb6fire_control_output_vol_get,
 438                .put = usb6fire_control_output_vol_put,
 439                .tlv = { .p = tlv_output }
 440        },
 441        {}
 442};
 443
 444static struct snd_kcontrol_new mute_elements[] = {
 445        {
 446                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 447                .name = "Analog Playback Switch",
 448                .index = 0,
 449                .private_value = 0,
 450                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 451                .info = snd_ctl_boolean_stereo_info,
 452                .get = usb6fire_control_output_mute_get,
 453                .put = usb6fire_control_output_mute_put,
 454        },
 455        {
 456                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 457                .name = "Analog Playback Switch",
 458                .index = 1,
 459                .private_value = 2,
 460                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 461                .info = snd_ctl_boolean_stereo_info,
 462                .get = usb6fire_control_output_mute_get,
 463                .put = usb6fire_control_output_mute_put,
 464        },
 465        {
 466                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 467                .name = "Analog Playback Switch",
 468                .index = 2,
 469                .private_value = 4,
 470                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 471                .info = snd_ctl_boolean_stereo_info,
 472                .get = usb6fire_control_output_mute_get,
 473                .put = usb6fire_control_output_mute_put,
 474        },
 475        {}
 476};
 477
 478static struct snd_kcontrol_new elements[] = {
 479        {
 480                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 481                .name = "Line/Phono Capture Route",
 482                .index = 0,
 483                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 484                .info = usb6fire_control_line_phono_info,
 485                .get = usb6fire_control_line_phono_get,
 486                .put = usb6fire_control_line_phono_put
 487        },
 488        {
 489                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 490                .name = "Opt/Coax Capture Route",
 491                .index = 0,
 492                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 493                .info = usb6fire_control_opt_coax_info,
 494                .get = usb6fire_control_opt_coax_get,
 495                .put = usb6fire_control_opt_coax_put
 496        },
 497        {
 498                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 499                .name = "Digital Thru Playback Route",
 500                .index = 0,
 501                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 502                .info = snd_ctl_boolean_mono_info,
 503                .get = usb6fire_control_digital_thru_get,
 504                .put = usb6fire_control_digital_thru_put
 505        },
 506        {
 507                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 508                .name = "Analog Capture Volume",
 509                .index = 0,
 510                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 511                        SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 512                .info = usb6fire_control_input_vol_info,
 513                .get = usb6fire_control_input_vol_get,
 514                .put = usb6fire_control_input_vol_put,
 515                .tlv = { .p = tlv_input }
 516        },
 517        {}
 518};
 519
 520static int usb6fire_control_add_virtual(
 521        struct control_runtime *rt,
 522        struct snd_card *card,
 523        char *name,
 524        struct snd_kcontrol_new *elems)
 525{
 526        int ret;
 527        int i;
 528        struct snd_kcontrol *vmaster =
 529                snd_ctl_make_virtual_master(name, tlv_output);
 530        struct snd_kcontrol *control;
 531
 532        if (!vmaster)
 533                return -ENOMEM;
 534        ret = snd_ctl_add(card, vmaster);
 535        if (ret < 0)
 536                return ret;
 537
 538        i = 0;
 539        while (elems[i].name) {
 540                control = snd_ctl_new1(&elems[i], rt);
 541                if (!control)
 542                        return -ENOMEM;
 543                ret = snd_ctl_add(card, control);
 544                if (ret < 0)
 545                        return ret;
 546                ret = snd_ctl_add_slave(vmaster, control);
 547                if (ret < 0)
 548                        return ret;
 549                i++;
 550        }
 551        return 0;
 552}
 553
 554int usb6fire_control_init(struct sfire_chip *chip)
 555{
 556        int i;
 557        int ret;
 558        struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
 559                        GFP_KERNEL);
 560        struct comm_runtime *comm_rt = chip->comm;
 561
 562        if (!rt)
 563                return -ENOMEM;
 564
 565        rt->chip = chip;
 566        rt->update_streaming = usb6fire_control_streaming_update;
 567        rt->set_rate = usb6fire_control_set_rate;
 568        rt->set_channels = usb6fire_control_set_channels;
 569
 570        i = 0;
 571        while (init_data[i].type) {
 572                comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
 573                                init_data[i].value);
 574                i++;
 575        }
 576
 577        usb6fire_control_opt_coax_update(rt);
 578        usb6fire_control_line_phono_update(rt);
 579        usb6fire_control_output_vol_update(rt);
 580        usb6fire_control_output_mute_update(rt);
 581        usb6fire_control_input_vol_update(rt);
 582        usb6fire_control_streaming_update(rt);
 583
 584        ret = usb6fire_control_add_virtual(rt, chip->card,
 585                "Master Playback Volume", vol_elements);
 586        if (ret) {
 587                dev_err(&chip->dev->dev, "cannot add control.\n");
 588                kfree(rt);
 589                return ret;
 590        }
 591        ret = usb6fire_control_add_virtual(rt, chip->card,
 592                "Master Playback Switch", mute_elements);
 593        if (ret) {
 594                dev_err(&chip->dev->dev, "cannot add control.\n");
 595                kfree(rt);
 596                return ret;
 597        }
 598
 599        i = 0;
 600        while (elements[i].name) {
 601                ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
 602                if (ret < 0) {
 603                        kfree(rt);
 604                        dev_err(&chip->dev->dev, "cannot add control.\n");
 605                        return ret;
 606                }
 607                i++;
 608        }
 609
 610        chip->control = rt;
 611        return 0;
 612}
 613
 614void usb6fire_control_abort(struct sfire_chip *chip)
 615{}
 616
 617void usb6fire_control_destroy(struct sfire_chip *chip)
 618{
 619        kfree(chip->control);
 620        chip->control = NULL;
 621}
 622