aboutsummaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/6fire/Makefile3
-rw-r--r--sound/usb/6fire/chip.c219
-rw-r--r--sound/usb/6fire/chip.h31
-rw-r--r--sound/usb/6fire/comm.c204
-rw-r--r--sound/usb/6fire/comm.h43
-rw-r--r--sound/usb/6fire/common.h29
-rw-r--r--sound/usb/6fire/control.c635
-rw-r--r--sound/usb/6fire/control.h57
-rw-r--r--sound/usb/6fire/firmware.c421
-rw-r--r--sound/usb/6fire/firmware.h27
-rw-r--r--sound/usb/6fire/midi.c218
-rw-r--r--sound/usb/6fire/midi.h41
-rw-r--r--sound/usb/6fire/pcm.c717
-rw-r--r--sound/usb/6fire/pcm.h75
-rw-r--r--sound/usb/Kconfig67
-rw-r--r--sound/usb/Makefile14
-rw-r--r--sound/usb/bcd2000/Makefile3
-rw-r--r--sound/usb/bcd2000/bcd2000.c461
-rw-r--r--sound/usb/caiaq/audio.c590
-rw-r--r--sound/usb/caiaq/audio.h4
-rw-r--r--sound/usb/caiaq/control.c351
-rw-r--r--sound/usb/caiaq/control.h2
-rw-r--r--sound/usb/caiaq/device.c343
-rw-r--r--sound/usb/caiaq/device.h32
-rw-r--r--sound/usb/caiaq/input.c553
-rw-r--r--sound/usb/caiaq/input.h6
-rw-r--r--sound/usb/caiaq/midi.c65
-rw-r--r--sound/usb/caiaq/midi.h5
-rw-r--r--sound/usb/card.c340
-rw-r--r--sound/usb/card.h119
-rw-r--r--sound/usb/clock.c285
-rw-r--r--sound/usb/clock.h5
-rw-r--r--sound/usb/debug.h2
-rw-r--r--sound/usb/endpoint.c1380
-rw-r--r--sound/usb/endpoint.h35
-rw-r--r--sound/usb/format.c173
-rw-r--r--sound/usb/format.h4
-rw-r--r--sound/usb/helper.c33
-rw-r--r--sound/usb/helper.h2
-rw-r--r--sound/usb/hiface/Makefile2
-rw-r--r--sound/usb/hiface/chip.c297
-rw-r--r--sound/usb/hiface/chip.h30
-rw-r--r--sound/usb/hiface/pcm.c621
-rw-r--r--sound/usb/hiface/pcm.h24
-rw-r--r--sound/usb/midi.c281
-rw-r--r--sound/usb/misc/ua101.c72
-rw-r--r--sound/usb/mixer.c1020
-rw-r--r--sound/usb/mixer.h25
-rw-r--r--sound/usb/mixer_maps.c57
-rw-r--r--sound/usb/mixer_quirks.c1332
-rw-r--r--sound/usb/pcm.c1105
-rw-r--r--sound/usb/pcm.h6
-rw-r--r--sound/usb/power.h17
-rw-r--r--sound/usb/proc.c44
-rw-r--r--sound/usb/quirks-table.h1199
-rw-r--r--sound/usb/quirks.c713
-rw-r--r--sound/usb/quirks.h15
-rw-r--r--sound/usb/stream.c731
-rw-r--r--sound/usb/stream.h12
-rw-r--r--sound/usb/urb.c995
-rw-r--r--sound/usb/urb.h21
-rw-r--r--sound/usb/usbaudio.h28
-rw-r--r--sound/usb/usx2y/us122l.c73
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c2
-rw-r--r--sound/usb/usx2y/usb_stream.c7
-rw-r--r--sound/usb/usx2y/usbusx2y.c38
-rw-r--r--sound/usb/usx2y/usbusx2y.h2
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c93
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c96
69 files changed, 13286 insertions, 3266 deletions
diff --git a/sound/usb/6fire/Makefile b/sound/usb/6fire/Makefile
new file mode 100644
index 00000000000..dfce6ec5351
--- /dev/null
+++ b/sound/usb/6fire/Makefile
@@ -0,0 +1,3 @@
+snd-usb-6fire-objs += chip.o comm.o midi.o control.o firmware.o pcm.o
+obj-$(CONFIG_SND_USB_6FIRE) += snd-usb-6fire.o
+
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
new file mode 100644
index 00000000000..dcddfc354ba
--- /dev/null
+++ b/sound/usb/6fire/chip.c
@@ -0,0 +1,219 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Main routines and module definitions.
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "chip.h"
+#include "firmware.h"
+#include "pcm.h"
+#include "control.h"
+#include "comm.h"
+#include "midi.h"
+
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
+MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{TerraTec,DMX 6Fire USB}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */
+static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the 6fire sound device");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the 6fire sound device.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the 6fire sound device.");
+
+static DEFINE_MUTEX(register_mutex);
+
+static void usb6fire_chip_abort(struct sfire_chip *chip)
+{
+ if (chip) {
+ if (chip->pcm)
+ usb6fire_pcm_abort(chip);
+ if (chip->midi)
+ usb6fire_midi_abort(chip);
+ if (chip->comm)
+ usb6fire_comm_abort(chip);
+ if (chip->control)
+ usb6fire_control_abort(chip);
+ if (chip->card) {
+ snd_card_disconnect(chip->card);
+ snd_card_free_when_closed(chip->card);
+ chip->card = NULL;
+ }
+ }
+}
+
+static void usb6fire_chip_destroy(struct sfire_chip *chip)
+{
+ if (chip) {
+ if (chip->pcm)
+ usb6fire_pcm_destroy(chip);
+ if (chip->midi)
+ usb6fire_midi_destroy(chip);
+ if (chip->comm)
+ usb6fire_comm_destroy(chip);
+ if (chip->control)
+ usb6fire_control_destroy(chip);
+ if (chip->card)
+ snd_card_free(chip->card);
+ }
+}
+
+static int usb6fire_chip_probe(struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
+{
+ int ret;
+ int i;
+ struct sfire_chip *chip = NULL;
+ struct usb_device *device = interface_to_usbdev(intf);
+ int regidx = -1; /* index in module parameter array */
+ struct snd_card *card = NULL;
+
+ /* look if we already serve this card and return if so */
+ mutex_lock(&register_mutex);
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (devices[i] == device) {
+ if (chips[i])
+ chips[i]->intf_count++;
+ usb_set_intfdata(intf, chips[i]);
+ mutex_unlock(&register_mutex);
+ return 0;
+ } else if (!devices[i] && regidx < 0)
+ regidx = i;
+ }
+ if (regidx < 0) {
+ mutex_unlock(&register_mutex);
+ dev_err(&intf->dev, "too many cards registered.\n");
+ return -ENODEV;
+ }
+ devices[regidx] = device;
+ mutex_unlock(&register_mutex);
+
+ /* check, if firmware is present on device, upload it if not */
+ ret = usb6fire_fw_init(intf);
+ if (ret < 0)
+ return ret;
+ else if (ret == FW_NOT_READY) /* firmware update performed */
+ return 0;
+
+ /* if we are here, card can be registered in alsa. */
+ if (usb_set_interface(device, 0, 0) != 0) {
+ dev_err(&intf->dev, "can't set first interface.\n");
+ return -EIO;
+ }
+ ret = snd_card_new(&intf->dev, index[regidx], id[regidx],
+ THIS_MODULE, sizeof(struct sfire_chip), &card);
+ if (ret < 0) {
+ dev_err(&intf->dev, "cannot create alsa card.\n");
+ return ret;
+ }
+ strcpy(card->driver, "6FireUSB");
+ strcpy(card->shortname, "TerraTec DMX6FireUSB");
+ sprintf(card->longname, "%s at %d:%d", card->shortname,
+ device->bus->busnum, device->devnum);
+
+ chip = card->private_data;
+ chips[regidx] = chip;
+ chip->dev = device;
+ chip->regidx = regidx;
+ chip->intf_count = 1;
+ chip->card = card;
+
+ ret = usb6fire_comm_init(chip);
+ if (ret < 0) {
+ usb6fire_chip_destroy(chip);
+ return ret;
+ }
+
+ ret = usb6fire_midi_init(chip);
+ if (ret < 0) {
+ usb6fire_chip_destroy(chip);
+ return ret;
+ }
+
+ ret = usb6fire_pcm_init(chip);
+ if (ret < 0) {
+ usb6fire_chip_destroy(chip);
+ return ret;
+ }
+
+ ret = usb6fire_control_init(chip);
+ if (ret < 0) {
+ usb6fire_chip_destroy(chip);
+ return ret;
+ }
+
+ ret = snd_card_register(card);
+ if (ret < 0) {
+ dev_err(&intf->dev, "cannot register card.");
+ usb6fire_chip_destroy(chip);
+ return ret;
+ }
+ usb_set_intfdata(intf, chip);
+ return 0;
+}
+
+static void usb6fire_chip_disconnect(struct usb_interface *intf)
+{
+ struct sfire_chip *chip;
+ struct snd_card *card;
+
+ chip = usb_get_intfdata(intf);
+ if (chip) { /* if !chip, fw upload has been performed */
+ card = chip->card;
+ chip->intf_count--;
+ if (!chip->intf_count) {
+ mutex_lock(&register_mutex);
+ devices[chip->regidx] = NULL;
+ chips[chip->regidx] = NULL;
+ mutex_unlock(&register_mutex);
+
+ chip->shutdown = true;
+ usb6fire_chip_abort(chip);
+ usb6fire_chip_destroy(chip);
+ }
+ }
+}
+
+static struct usb_device_id device_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0ccd,
+ .idProduct = 0x0080
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static struct usb_driver usb_driver = {
+ .name = "snd-usb-6fire",
+ .probe = usb6fire_chip_probe,
+ .disconnect = usb6fire_chip_disconnect,
+ .id_table = device_table,
+};
+
+module_usb_driver(usb_driver);
diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h
new file mode 100644
index 00000000000..bde02d105a5
--- /dev/null
+++ b/sound/usb/6fire/chip.h
@@ -0,0 +1,31 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef USB6FIRE_CHIP_H
+#define USB6FIRE_CHIP_H
+
+#include "common.h"
+
+struct sfire_chip {
+ struct usb_device *dev;
+ struct snd_card *card;
+ int intf_count; /* number of registered interfaces */
+ int regidx; /* index in module parameter arrays */
+ bool shutdown;
+
+ struct midi_runtime *midi;
+ struct pcm_runtime *pcm;
+ struct control_runtime *control;
+ struct comm_runtime *comm;
+};
+#endif /* USB6FIRE_CHIP_H */
+
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
new file mode 100644
index 00000000000..161215d78d9
--- /dev/null
+++ b/sound/usb/6fire/comm.c
@@ -0,0 +1,204 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Device communications
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "comm.h"
+#include "chip.h"
+#include "midi.h"
+
+enum {
+ COMM_EP = 1,
+ COMM_FPGA_EP = 2
+};
+
+static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
+ u8 *buffer, void *context, void(*handler)(struct urb *urb))
+{
+ usb_init_urb(urb);
+ urb->transfer_buffer = buffer;
+ urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
+ urb->complete = handler;
+ urb->context = context;
+ urb->interval = 1;
+ urb->dev = rt->chip->dev;
+}
+
+static void usb6fire_comm_receiver_handler(struct urb *urb)
+{
+ struct comm_runtime *rt = urb->context;
+ struct midi_runtime *midi_rt = rt->chip->midi;
+
+ if (!urb->status) {
+ if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+ if (midi_rt)
+ midi_rt->in_received(midi_rt,
+ rt->receiver_buffer + 2,
+ rt->receiver_buffer[1]);
+ }
+
+ if (!rt->chip->shutdown) {
+ urb->status = 0;
+ urb->actual_length = 0;
+ if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
+ dev_warn(&urb->dev->dev,
+ "comm data receiver aborted.\n");
+ }
+}
+
+static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
+ u8 reg, u8 vl, u8 vh)
+{
+ buffer[0] = 0x01;
+ buffer[2] = request;
+ buffer[3] = id;
+ switch (request) {
+ case 0x02:
+ buffer[1] = 0x05; /* length (starting at buffer[2]) */
+ buffer[4] = reg;
+ buffer[5] = vl;
+ buffer[6] = vh;
+ break;
+
+ case 0x12:
+ buffer[1] = 0x0b; /* length (starting at buffer[2]) */
+ buffer[4] = 0x00;
+ buffer[5] = 0x18;
+ buffer[6] = 0x05;
+ buffer[7] = 0x00;
+ buffer[8] = 0x01;
+ buffer[9] = 0x00;
+ buffer[10] = 0x9e;
+ buffer[11] = reg;
+ buffer[12] = vl;
+ break;
+
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ buffer[1] = 0x04;
+ buffer[4] = reg;
+ buffer[5] = vl;
+ break;
+ }
+}
+
+static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
+{
+ int ret;
+ int actual_len;
+
+ ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
+ buffer, buffer[1] + 2, &actual_len, HZ);
+ if (ret < 0)
+ return ret;
+ else if (actual_len != buffer[1] + 2)
+ return -EIO;
+ return 0;
+}
+
+static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
+ u8 reg, u8 value)
+{
+ u8 *buffer;
+ int ret;
+
+ /* 13: maximum length of message */
+ buffer = kmalloc(13, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
+ ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
+ u8 reg, u8 vl, u8 vh)
+{
+ u8 *buffer;
+ int ret;
+
+ /* 13: maximum length of message */
+ buffer = kmalloc(13, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
+ ret = usb6fire_comm_send_buffer(buffer, rt->chip->dev);
+
+ kfree(buffer);
+ return ret;
+}
+
+int usb6fire_comm_init(struct sfire_chip *chip)
+{
+ struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
+ GFP_KERNEL);
+ struct urb *urb;
+ int ret;
+
+ if (!rt)
+ return -ENOMEM;
+
+ rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL);
+ if (!rt->receiver_buffer) {
+ kfree(rt);
+ return -ENOMEM;
+ }
+
+ urb = &rt->receiver;
+ rt->serial = 1;
+ rt->chip = chip;
+ usb_init_urb(urb);
+ rt->init_urb = usb6fire_comm_init_urb;
+ rt->write8 = usb6fire_comm_write8;
+ rt->write16 = usb6fire_comm_write16;
+
+ /* submit an urb that receives communication data from device */
+ urb->transfer_buffer = rt->receiver_buffer;
+ urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE;
+ urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP);
+ urb->dev = chip->dev;
+ urb->complete = usb6fire_comm_receiver_handler;
+ urb->context = rt;
+ urb->interval = 1;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ kfree(rt->receiver_buffer);
+ kfree(rt);
+ dev_err(&chip->dev->dev, "cannot create comm data receiver.");
+ return ret;
+ }
+ chip->comm = rt;
+ return 0;
+}
+
+void usb6fire_comm_abort(struct sfire_chip *chip)
+{
+ struct comm_runtime *rt = chip->comm;
+
+ if (rt)
+ usb_poison_urb(&rt->receiver);
+}
+
+void usb6fire_comm_destroy(struct sfire_chip *chip)
+{
+ struct comm_runtime *rt = chip->comm;
+
+ kfree(rt->receiver_buffer);
+ kfree(rt);
+ chip->comm = NULL;
+}
diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h
new file mode 100644
index 00000000000..780d5ed8e5d
--- /dev/null
+++ b/sound/usb/6fire/comm.h
@@ -0,0 +1,43 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef USB6FIRE_COMM_H
+#define USB6FIRE_COMM_H
+
+#include "common.h"
+
+enum /* settings for comm */
+{
+ COMM_RECEIVER_BUFSIZE = 64,
+};
+
+struct comm_runtime {
+ struct sfire_chip *chip;
+
+ struct urb receiver;
+ u8 *receiver_buffer;
+
+ u8 serial; /* urb serial */
+
+ void (*init_urb)(struct comm_runtime *rt, struct urb *urb, u8 *buffer,
+ void *context, void(*handler)(struct urb *urb));
+ /* writes control data to the device */
+ int (*write8)(struct comm_runtime *rt, u8 request, u8 reg, u8 value);
+ int (*write16)(struct comm_runtime *rt, u8 request, u8 reg,
+ u8 vh, u8 vl);
+};
+
+int usb6fire_comm_init(struct sfire_chip *chip);
+void usb6fire_comm_abort(struct sfire_chip *chip);
+void usb6fire_comm_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_COMM_H */
+
diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h
new file mode 100644
index 00000000000..b6eb03ed1c2
--- /dev/null
+++ b/sound/usb/6fire/common.h
@@ -0,0 +1,29 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef USB6FIRE_COMMON_H
+#define USB6FIRE_COMMON_H
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+
+#define PREFIX "6fire: "
+
+struct sfire_chip;
+struct midi_runtime;
+struct pcm_runtime;
+struct control_runtime;
+struct comm_runtime;
+#endif /* USB6FIRE_COMMON_H */
+
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
new file mode 100644
index 00000000000..184e3987ac2
--- /dev/null
+++ b/sound/usb/6fire/control.c
@@ -0,0 +1,635 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Mixer control
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * Thanks to:
+ * - Holger Ruckdeschel: he found out how to control individual channel
+ * volumes and introduced mute switch
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/interrupt.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "control.h"
+#include "comm.h"
+#include "chip.h"
+
+static char *opt_coax_texts[2] = { "Optical", "Coax" };
+static char *line_phono_texts[2] = { "Line", "Phono" };
+
+/*
+ * data that needs to be sent to device. sets up card internal stuff.
+ * values dumped from windows driver and filtered by trial'n'error.
+ */
+static const struct {
+ u8 type;
+ u8 reg;
+ u8 value;
+}
+init_data[] = {
+ { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },
+ { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },
+ { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
+ { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
+ { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
+ { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
+ { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
+ { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
+ { 0 } /* TERMINATING ENTRY */
+};
+
+static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
+/* values to write to soundcard register for all samplerates */
+static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
+
+static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
+static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);
+
+enum {
+ DIGITAL_THRU_ONLY_SAMPLERATE = 3
+};
+
+static void usb6fire_control_output_vol_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+ int i;
+
+ if (comm_rt)
+ for (i = 0; i < 6; i++)
+ if (!(rt->ovol_updated & (1 << i))) {
+ comm_rt->write8(comm_rt, 0x12, 0x0f + i,
+ 180 - rt->output_vol[i]);
+ rt->ovol_updated |= 1 << i;
+ }
+}
+
+static void usb6fire_control_output_mute_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (comm_rt)
+ comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);
+}
+
+static void usb6fire_control_input_vol_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+ int i;
+
+ if (comm_rt)
+ for (i = 0; i < 2; i++)
+ if (!(rt->ivol_updated & (1 << i))) {
+ comm_rt->write8(comm_rt, 0x12, 0x1c + i,
+ rt->input_vol[i] & 0x3f);
+ rt->ivol_updated |= 1 << i;
+ }
+}
+
+static void usb6fire_control_line_phono_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+ if (comm_rt) {
+ comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
+ comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
+ }
+}
+
+static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+ if (comm_rt) {
+ comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
+ comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
+ }
+}
+
+static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
+{
+ int ret;
+ struct usb_device *device = rt->chip->dev;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (rate < 0 || rate >= CONTROL_N_RATES)
+ return -EINVAL;
+
+ ret = usb_set_interface(device, 1, rates_altsetting[rate]);
+ if (ret < 0)
+ return ret;
+
+ /* set soundcard clock */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
+ rates_6fire_vh[rate]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_set_channels(
+ struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in)
+{
+ int ret;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ /* enable analog inputs and outputs
+ * (one bit per stereo-channel) */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x02,
+ (1 << (n_analog_out / 2)) - 1,
+ (1 << (n_analog_in / 2)) - 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable digital inputs and outputs */
+ /* TODO: use spdif_x to enable/disable digital channels */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_streaming_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (comm_rt) {
+ if (!rt->usb_streaming && rt->digital_thru_switch)
+ usb6fire_control_set_rate(rt,
+ DIGITAL_THRU_ONLY_SAMPLERATE);
+ return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
+ (rt->usb_streaming ? 0x01 : 0x00) |
+ (rt->digital_thru_switch ? 0x08 : 0x00));
+ }
+ return -EINVAL;
+}
+
+static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 180;
+ return 0;
+}
+
+static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ unsigned int ch = kcontrol->private_value;
+ int changed = 0;
+
+ if (ch > 4) {
+ dev_err(&rt->chip->dev->dev,
+ "Invalid channel in volume control.");
+ return -EINVAL;
+ }
+
+ if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {
+ rt->output_vol[ch] = ucontrol->value.integer.value[0];
+ rt->ovol_updated &= ~(1 << ch);
+ changed = 1;
+ }
+ if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {
+ rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];
+ rt->ovol_updated &= ~(2 << ch);
+ changed = 1;
+ }
+
+ if (changed)
+ usb6fire_control_output_vol_update(rt);
+
+ return changed;
+}
+
+static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ unsigned int ch = kcontrol->private_value;
+
+ if (ch > 4) {
+ dev_err(&rt->chip->dev->dev,
+ "Invalid channel in volume control.");
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = rt->output_vol[ch];
+ ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];
+ return 0;
+}
+
+static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ unsigned int ch = kcontrol->private_value;
+ u8 old = rt->output_mute;
+ u8 value = 0;
+
+ if (ch > 4) {
+ dev_err(&rt->chip->dev->dev,
+ "Invalid channel in volume control.");
+ return -EINVAL;
+ }
+
+ rt->output_mute &= ~(3 << ch);
+ if (ucontrol->value.integer.value[0])
+ value |= 1;
+ if (ucontrol->value.integer.value[1])
+ value |= 2;
+ rt->output_mute |= value << ch;
+
+ if (rt->output_mute != old)
+ usb6fire_control_output_mute_update(rt);
+
+ return rt->output_mute != old;
+}
+
+static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ unsigned int ch = kcontrol->private_value;
+ u8 value = rt->output_mute >> ch;
+
+ if (ch > 4) {
+ dev_err(&rt->chip->dev->dev,
+ "Invalid channel in volume control.");
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = 1 & value;
+ value >>= 1;
+ ucontrol->value.integer.value[1] = 1 & value;
+
+ return 0;
+}
+
+static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 30;
+ return 0;
+}
+
+static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {
+ rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;
+ rt->ivol_updated &= ~(1 << 0);
+ changed = 1;
+ }
+ if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {
+ rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;
+ rt->ivol_updated &= ~(1 << 1);
+ changed = 1;
+ }
+
+ if (changed)
+ usb6fire_control_input_vol_update(rt);
+
+ return changed;
+}
+
+static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;
+ ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;
+
+ return 0;
+}
+
+static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ line_phono_texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
+ rt->line_phono_switch = ucontrol->value.integer.value[0];
+ usb6fire_control_line_phono_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = rt->line_phono_switch;
+ return 0;
+}
+
+static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ opt_coax_texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
+ rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
+ usb6fire_control_opt_coax_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
+ return 0;
+}
+
+static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
+ rt->digital_thru_switch = ucontrol->value.integer.value[0];
+ usb6fire_control_streaming_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = rt->digital_thru_switch;
+ return 0;
+}
+
+static struct snd_kcontrol_new vol_elements[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Volume",
+ .index = 0,
+ .private_value = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = usb6fire_control_output_vol_info,
+ .get = usb6fire_control_output_vol_get,
+ .put = usb6fire_control_output_vol_put,
+ .tlv = { .p = tlv_output }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Volume",
+ .index = 1,
+ .private_value = 2,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = usb6fire_control_output_vol_info,
+ .get = usb6fire_control_output_vol_get,
+ .put = usb6fire_control_output_vol_put,
+ .tlv = { .p = tlv_output }
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Volume",
+ .index = 2,
+ .private_value = 4,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = usb6fire_control_output_vol_info,
+ .get = usb6fire_control_output_vol_get,
+ .put = usb6fire_control_output_vol_put,
+ .tlv = { .p = tlv_output }
+ },
+ {}
+};
+
+static struct snd_kcontrol_new mute_elements[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Switch",
+ .index = 0,
+ .private_value = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_stereo_info,
+ .get = usb6fire_control_output_mute_get,
+ .put = usb6fire_control_output_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Switch",
+ .index = 1,
+ .private_value = 2,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_stereo_info,
+ .get = usb6fire_control_output_mute_get,
+ .put = usb6fire_control_output_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Switch",
+ .index = 2,
+ .private_value = 4,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_stereo_info,
+ .get = usb6fire_control_output_mute_get,
+ .put = usb6fire_control_output_mute_put,
+ },
+ {}
+};
+
+static struct snd_kcontrol_new elements[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line/Phono Capture Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = usb6fire_control_line_phono_info,
+ .get = usb6fire_control_line_phono_get,
+ .put = usb6fire_control_line_phono_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Opt/Coax Capture Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = usb6fire_control_opt_coax_info,
+ .get = usb6fire_control_opt_coax_get,
+ .put = usb6fire_control_opt_coax_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Thru Playback Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = usb6fire_control_digital_thru_get,
+ .put = usb6fire_control_digital_thru_put
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Capture Volume",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = usb6fire_control_input_vol_info,
+ .get = usb6fire_control_input_vol_get,
+ .put = usb6fire_control_input_vol_put,
+ .tlv = { .p = tlv_input }
+ },
+ {}
+};
+
+static int usb6fire_control_add_virtual(
+ struct control_runtime *rt,
+ struct snd_card *card,
+ char *name,
+ struct snd_kcontrol_new *elems)
+{
+ int ret;
+ int i;
+ struct snd_kcontrol *vmaster =
+ snd_ctl_make_virtual_master(name, tlv_output);
+ struct snd_kcontrol *control;
+
+ if (!vmaster)
+ return -ENOMEM;
+ ret = snd_ctl_add(card, vmaster);
+ if (ret < 0)
+ return ret;
+
+ i = 0;
+ while (elems[i].name) {
+ control = snd_ctl_new1(&elems[i], rt);
+ if (!control)
+ return -ENOMEM;
+ ret = snd_ctl_add(card, control);
+ if (ret < 0)
+ return ret;
+ ret = snd_ctl_add_slave(vmaster, control);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+ return 0;
+}
+
+int usb6fire_control_init(struct sfire_chip *chip)
+{
+ int i;
+ int ret;
+ struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
+ GFP_KERNEL);
+ struct comm_runtime *comm_rt = chip->comm;
+
+ if (!rt)
+ return -ENOMEM;
+
+ rt->chip = chip;
+ rt->update_streaming = usb6fire_control_streaming_update;
+ rt->set_rate = usb6fire_control_set_rate;
+ rt->set_channels = usb6fire_control_set_channels;
+
+ i = 0;
+ while (init_data[i].type) {
+ comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
+ init_data[i].value);
+ i++;
+ }
+
+ usb6fire_control_opt_coax_update(rt);
+ usb6fire_control_line_phono_update(rt);
+ usb6fire_control_output_vol_update(rt);
+ usb6fire_control_output_mute_update(rt);
+ usb6fire_control_input_vol_update(rt);
+ usb6fire_control_streaming_update(rt);
+
+ ret = usb6fire_control_add_virtual(rt, chip->card,
+ "Master Playback Volume", vol_elements);
+ if (ret) {
+ dev_err(&chip->dev->dev, "cannot add control.\n");
+ kfree(rt);
+ return ret;
+ }
+ ret = usb6fire_control_add_virtual(rt, chip->card,
+ "Master Playback Switch", mute_elements);
+ if (ret) {
+ dev_err(&chip->dev->dev, "cannot add control.\n");
+ kfree(rt);
+ return ret;
+ }
+
+ i = 0;
+ while (elements[i].name) {
+ ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
+ if (ret < 0) {
+ kfree(rt);
+ dev_err(&chip->dev->dev, "cannot add control.\n");
+ return ret;
+ }
+ i++;
+ }
+
+ chip->control = rt;
+ return 0;
+}
+
+void usb6fire_control_abort(struct sfire_chip *chip)
+{}
+
+void usb6fire_control_destroy(struct sfire_chip *chip)
+{
+ kfree(chip->control);
+ chip->control = NULL;
+}
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
new file mode 100644
index 00000000000..5a40ba14348
--- /dev/null
+++ b/sound/usb/6fire/control.h
@@ -0,0 +1,57 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef USB6FIRE_CONTROL_H
+#define USB6FIRE_CONTROL_H
+
+#include "common.h"
+
+enum {
+ CONTROL_MAX_ELEMENTS = 32
+};
+
+enum {
+ CONTROL_RATE_44KHZ,
+ CONTROL_RATE_48KHZ,
+ CONTROL_RATE_88KHZ,
+ CONTROL_RATE_96KHZ,
+ CONTROL_RATE_176KHZ,
+ CONTROL_RATE_192KHZ,
+ CONTROL_N_RATES
+};
+
+struct control_runtime {
+ int (*update_streaming)(struct control_runtime *rt);
+ int (*set_rate)(struct control_runtime *rt, int rate);
+ int (*set_channels)(struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in);
+
+ struct sfire_chip *chip;
+
+ struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
+ bool opt_coax_switch;
+ bool line_phono_switch;
+ bool digital_thru_switch;
+ bool usb_streaming;
+ u8 output_vol[6];
+ u8 ovol_updated;
+ u8 output_mute;
+ s8 input_vol[2];
+ u8 ivol_updated;
+};
+
+int usb6fire_control_init(struct sfire_chip *chip);
+void usb6fire_control_abort(struct sfire_chip *chip);
+void usb6fire_control_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_CONTROL_H */
+
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
new file mode 100644
index 00000000000..3b02e54b8f6
--- /dev/null
+++ b/sound/usb/6fire/firmware.c
@@ -0,0 +1,421 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Firmware loader
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/bitrev.h>
+#include <linux/kernel.h>
+
+#include "firmware.h"
+#include "chip.h"
+
+MODULE_FIRMWARE("6fire/dmx6firel2.ihx");
+MODULE_FIRMWARE("6fire/dmx6fireap.ihx");
+MODULE_FIRMWARE("6fire/dmx6firecf.bin");
+
+enum {
+ FPGA_BUFSIZE = 512, FPGA_EP = 2
+};
+
+/*
+ * wMaxPacketSize of pcm endpoints.
+ * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
+ * fpp: frames per isopacket
+ *
+ * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init
+ */
+static const u8 ep_w_max_packet_size[] = {
+ 0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */
+ 0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/
+ 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
+};
+
+static const u8 known_fw_versions[][2] = {
+ { 0x03, 0x01 }
+};
+
+struct ihex_record {
+ u16 address;
+ u8 len;
+ u8 data[256];
+ char error; /* true if an error occurred parsing this record */
+
+ u8 max_len; /* maximum record length in whole ihex */
+
+ /* private */
+ const char *txt_data;
+ unsigned int txt_length;
+ unsigned int txt_offset; /* current position in txt_data */
+};
+
+static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc)
+{
+ u8 val = 0;
+ int hval;
+
+ hval = hex_to_bin(data[0]);
+ if (hval >= 0)
+ val |= (hval << 4);
+
+ hval = hex_to_bin(data[1]);
+ if (hval >= 0)
+ val |= hval;
+
+ *crc += val;
+ return val;
+}
+
+/*
+ * returns true if record is available, false otherwise.
+ * iff an error occurred, false will be returned and record->error will be true.
+ */
+static bool usb6fire_fw_ihex_next_record(struct ihex_record *record)
+{
+ u8 crc = 0;
+ u8 type;
+ int i;
+
+ record->error = false;
+
+ /* find begin of record (marked by a colon) */
+ while (record->txt_offset < record->txt_length
+ && record->txt_data[record->txt_offset] != ':')
+ record->txt_offset++;
+ if (record->txt_offset == record->txt_length)
+ return false;
+
+ /* number of characters needed for len, addr and type entries */
+ record->txt_offset++;
+ if (record->txt_offset + 8 > record->txt_length) {
+ record->error = true;
+ return false;
+ }
+
+ record->len = usb6fire_fw_ihex_hex(record->txt_data +
+ record->txt_offset, &crc);
+ record->txt_offset += 2;
+ record->address = usb6fire_fw_ihex_hex(record->txt_data +
+ record->txt_offset, &crc) << 8;
+ record->txt_offset += 2;
+ record->address |= usb6fire_fw_ihex_hex(record->txt_data +
+ record->txt_offset, &crc);
+ record->txt_offset += 2;
+ type = usb6fire_fw_ihex_hex(record->txt_data +
+ record->txt_offset, &crc);
+ record->txt_offset += 2;
+
+ /* number of characters needed for data and crc entries */
+ if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) {
+ record->error = true;
+ return false;
+ }
+ for (i = 0; i < record->len; i++) {
+ record->data[i] = usb6fire_fw_ihex_hex(record->txt_data
+ + record->txt_offset, &crc);
+ record->txt_offset += 2;
+ }
+ usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc);
+ if (crc) {
+ record->error = true;
+ return false;
+ }
+
+ if (type == 1 || !record->len) /* eof */
+ return false;
+ else if (type == 0)
+ return true;
+ else {
+ record->error = true;
+ return false;
+ }
+}
+
+static int usb6fire_fw_ihex_init(const struct firmware *fw,
+ struct ihex_record *record)
+{
+ record->txt_data = fw->data;
+ record->txt_length = fw->size;
+ record->txt_offset = 0;
+ record->max_len = 0;
+ /* read all records, if loop ends, record->error indicates,
+ * whether ihex is valid. */
+ while (usb6fire_fw_ihex_next_record(record))
+ record->max_len = max(record->len, record->max_len);
+ if (record->error)
+ return -EINVAL;
+ record->txt_offset = 0;
+ return 0;
+}
+
+static int usb6fire_fw_ezusb_write(struct usb_device *device,
+ int type, int value, char *data, int len)
+{
+ int ret;
+
+ ret = usb_control_msg(device, usb_sndctrlpipe(device, 0), type,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, data, len, HZ);
+ if (ret < 0)
+ return ret;
+ else if (ret != len)
+ return -EIO;
+ return 0;
+}
+
+static int usb6fire_fw_ezusb_read(struct usb_device *device,
+ int type, int value, char *data, int len)
+{
+ int ret = usb_control_msg(device, usb_rcvctrlpipe(device, 0), type,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value,
+ 0, data, len, HZ);
+ if (ret < 0)
+ return ret;
+ else if (ret != len)
+ return -EIO;
+ return 0;
+}
+
+static int usb6fire_fw_fpga_write(struct usb_device *device,
+ char *data, int len)
+{
+ int actual_len;
+ int ret;
+
+ ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,
+ &actual_len, HZ);
+ if (ret < 0)
+ return ret;
+ else if (actual_len != len)
+ return -EIO;
+ return 0;
+}
+
+static int usb6fire_fw_ezusb_upload(
+ struct usb_interface *intf, const char *fwname,
+ unsigned int postaddr, u8 *postdata, unsigned int postlen)
+{
+ int ret;
+ u8 data;
+ struct usb_device *device = interface_to_usbdev(intf);
+ const struct firmware *fw = NULL;
+ struct ihex_record *rec = kmalloc(sizeof(struct ihex_record),
+ GFP_KERNEL);
+
+ if (!rec)
+ return -ENOMEM;
+
+ ret = request_firmware(&fw, fwname, &device->dev);
+ if (ret < 0) {
+ kfree(rec);
+ dev_err(&intf->dev,
+ "error requesting ezusb firmware %s.\n", fwname);
+ return ret;
+ }
+ ret = usb6fire_fw_ihex_init(fw, rec);
+ if (ret < 0) {
+ kfree(rec);
+ release_firmware(fw);
+ dev_err(&intf->dev,
+ "error validating ezusb firmware %s.\n", fwname);
+ return ret;
+ }
+ /* upload firmware image */
+ data = 0x01; /* stop ezusb cpu */
+ ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
+ if (ret < 0) {
+ kfree(rec);
+ release_firmware(fw);
+ dev_err(&intf->dev,
+ "unable to upload ezusb firmware %s: begin message.\n",
+ fwname);
+ return ret;
+ }
+
+ while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */
+ ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address,
+ rec->data, rec->len);
+ if (ret < 0) {
+ kfree(rec);
+ release_firmware(fw);
+ dev_err(&intf->dev,
+ "unable to upload ezusb firmware %s: data urb.\n",
+ fwname);
+ return ret;
+ }
+ }
+
+ release_firmware(fw);
+ kfree(rec);
+ if (postdata) { /* write data after firmware has been uploaded */
+ ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr,
+ postdata, postlen);
+ if (ret < 0) {
+ dev_err(&intf->dev,
+ "unable to upload ezusb firmware %s: post urb.\n",
+ fwname);
+ return ret;
+ }
+ }
+
+ data = 0x00; /* resume ezusb cpu */
+ ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
+ if (ret < 0) {
+ dev_err(&intf->dev,
+ "unable to upload ezusb firmware %s: end message.\n",
+ fwname);
+ return ret;
+ }
+ return 0;
+}
+
+static int usb6fire_fw_fpga_upload(
+ struct usb_interface *intf, const char *fwname)
+{
+ int ret;
+ int i;
+ struct usb_device *device = interface_to_usbdev(intf);
+ u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL);
+ const char *c;
+ const char *end;
+ const struct firmware *fw;
+
+ if (!buffer)
+ return -ENOMEM;
+
+ ret = request_firmware(&fw, fwname, &device->dev);
+ if (ret < 0) {
+ dev_err(&intf->dev, "unable to get fpga firmware %s.\n",
+ fwname);
+ kfree(buffer);
+ return -EIO;
+ }
+
+ c = fw->data;
+ end = fw->data + fw->size;
+
+ ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0);
+ if (ret < 0) {
+ kfree(buffer);
+ release_firmware(fw);
+ dev_err(&intf->dev,
+ "unable to upload fpga firmware: begin urb.\n");
+ return ret;
+ }
+
+ while (c != end) {
+ for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
+ buffer[i] = byte_rev_table[(u8) *c];
+
+ ret = usb6fire_fw_fpga_write(device, buffer, i);
+ if (ret < 0) {
+ release_firmware(fw);
+ kfree(buffer);
+ dev_err(&intf->dev,
+ "unable to upload fpga firmware: fw urb.\n");
+ return ret;
+ }
+ }
+ release_firmware(fw);
+ kfree(buffer);
+
+ ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(&intf->dev,
+ "unable to upload fpga firmware: end urb.\n");
+ return ret;
+ }
+ return 0;
+}
+
+/* check, if the firmware version the devices has currently loaded
+ * is known by this driver. 'version' needs to have 4 bytes version
+ * info data. */
+static int usb6fire_fw_check(struct usb_interface *intf, const u8 *version)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
+ if (!memcmp(version, known_fw_versions + i, 2))
+ return 0;
+
+ dev_err(&intf->dev, "invalid fimware version in device: %4ph. "
+ "please reconnect to power. if this failure "
+ "still happens, check your firmware installation.",
+ version);
+ return -EINVAL;
+}
+
+int usb6fire_fw_init(struct usb_interface *intf)
+{
+ int i;
+ int ret;
+ struct usb_device *device = interface_to_usbdev(intf);
+ /* buffer: 8 receiving bytes from device and
+ * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */
+ u8 buffer[12];
+
+ ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8);
+ if (ret < 0) {
+ dev_err(&intf->dev,
+ "unable to receive device firmware state.\n");
+ return ret;
+ }
+ if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
+ dev_err(&intf->dev,
+ "unknown device firmware state received from device:");
+ for (i = 0; i < 8; i++)
+ printk(KERN_CONT "%02x ", buffer[i]);
+ printk(KERN_CONT "\n");
+ return -EIO;
+ }
+ /* do we need fpga loader ezusb firmware? */
+ if (buffer[3] == 0x01) {
+ ret = usb6fire_fw_ezusb_upload(intf,
+ "6fire/dmx6firel2.ihx", 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+ return FW_NOT_READY;
+ }
+ /* do we need fpga firmware and application ezusb firmware? */
+ else if (buffer[3] == 0x02) {
+ ret = usb6fire_fw_check(intf, buffer + 4);
+ if (ret < 0)
+ return ret;
+ ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
+ if (ret < 0)
+ return ret;
+ memcpy(buffer, ep_w_max_packet_size,
+ sizeof(ep_w_max_packet_size));
+ ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx",
+ 0x0003, buffer, sizeof(ep_w_max_packet_size));
+ if (ret < 0)
+ return ret;
+ return FW_NOT_READY;
+ }
+ /* all fw loaded? */
+ else if (buffer[3] == 0x03)
+ return usb6fire_fw_check(intf, buffer + 4);
+ /* unknown data? */
+ else {
+ dev_err(&intf->dev,
+ "unknown device firmware state received from device: ");
+ for (i = 0; i < 8; i++)
+ printk(KERN_CONT "%02x ", buffer[i]);
+ printk(KERN_CONT "\n");
+ return -EIO;
+ }
+ return 0;
+}
+
diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h
new file mode 100644
index 00000000000..c109c4f75ab
--- /dev/null
+++ b/sound/usb/6fire/firmware.h
@@ -0,0 +1,27 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef USB6FIRE_FIRMWARE_H
+#define USB6FIRE_FIRMWARE_H
+
+#include "common.h"
+
+enum /* firmware state of device */
+{
+ FW_READY = 0,
+ FW_NOT_READY = 1
+};
+
+int usb6fire_fw_init(struct usb_interface *intf);
+#endif /* USB6FIRE_FIRMWARE_H */
+
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
new file mode 100644
index 00000000000..3d410969553
--- /dev/null
+++ b/sound/usb/6fire/midi.c
@@ -0,0 +1,218 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Rawmidi driver
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sound/rawmidi.h>
+
+#include "midi.h"
+#include "chip.h"
+#include "comm.h"
+
+enum {
+ MIDI_BUFSIZE = 64
+};
+
+static void usb6fire_midi_out_handler(struct urb *urb)
+{
+ struct midi_runtime *rt = urb->context;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rt->out_lock, flags);
+
+ if (rt->out) {
+ ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
+ MIDI_BUFSIZE - 4);
+ if (ret > 0) { /* more data available, send next packet */
+ rt->out_buffer[1] = ret + 2;
+ rt->out_buffer[3] = rt->out_serial++;
+ urb->transfer_buffer_length = ret + 4;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&urb->dev->dev,
+ "midi out urb submit failed: %d\n",
+ ret);
+ } else /* no more data to transmit */
+ rt->out = NULL;
+ }
+ spin_unlock_irqrestore(&rt->out_lock, flags);
+}
+
+static void usb6fire_midi_in_received(
+ struct midi_runtime *rt, u8 *data, int length)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rt->in_lock, flags);
+ if (rt->in)
+ snd_rawmidi_receive(rt->in, data, length);
+ spin_unlock_irqrestore(&rt->in_lock, flags);
+}
+
+static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
+{
+ return 0;
+}
+
+static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
+{
+ return 0;
+}
+
+static void usb6fire_midi_out_trigger(
+ struct snd_rawmidi_substream *alsa_sub, int up)
+{
+ struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+ struct urb *urb = &rt->out_urb;
+ __s8 ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rt->out_lock, flags);
+ if (up) { /* start transfer */
+ if (rt->out) { /* we are already transmitting so just return */
+ spin_unlock_irqrestore(&rt->out_lock, flags);
+ return;
+ }
+
+ ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
+ MIDI_BUFSIZE - 4);
+ if (ret > 0) {
+ rt->out_buffer[1] = ret + 2;
+ rt->out_buffer[3] = rt->out_serial++;
+ urb->transfer_buffer_length = ret + 4;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&urb->dev->dev,
+ "midi out urb submit failed: %d\n",
+ ret);
+ else
+ rt->out = alsa_sub;
+ }
+ } else if (rt->out == alsa_sub)
+ rt->out = NULL;
+ spin_unlock_irqrestore(&rt->out_lock, flags);
+}
+
+static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
+{
+ struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+ int retry = 0;
+
+ while (rt->out && retry++ < 100)
+ msleep(10);
+}
+
+static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
+{
+ return 0;
+}
+
+static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
+{
+ return 0;
+}
+
+static void usb6fire_midi_in_trigger(
+ struct snd_rawmidi_substream *alsa_sub, int up)
+{
+ struct midi_runtime *rt = alsa_sub->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rt->in_lock, flags);
+ if (up)
+ rt->in = alsa_sub;
+ else
+ rt->in = NULL;
+ spin_unlock_irqrestore(&rt->in_lock, flags);
+}
+
+static struct snd_rawmidi_ops out_ops = {
+ .open = usb6fire_midi_out_open,
+ .close = usb6fire_midi_out_close,
+ .trigger = usb6fire_midi_out_trigger,
+ .drain = usb6fire_midi_out_drain
+};
+
+static struct snd_rawmidi_ops in_ops = {
+ .open = usb6fire_midi_in_open,
+ .close = usb6fire_midi_in_close,
+ .trigger = usb6fire_midi_in_trigger
+};
+
+int usb6fire_midi_init(struct sfire_chip *chip)
+{
+ int ret;
+ struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
+ GFP_KERNEL);
+ struct comm_runtime *comm_rt = chip->comm;
+
+ if (!rt)
+ return -ENOMEM;
+
+ rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL);
+ if (!rt->out_buffer) {
+ kfree(rt);
+ return -ENOMEM;
+ }
+
+ rt->chip = chip;
+ rt->in_received = usb6fire_midi_in_received;
+ rt->out_buffer[0] = 0x80; /* 'send midi' command */
+ rt->out_buffer[1] = 0x00; /* size of data */
+ rt->out_buffer[2] = 0x00; /* always 0 */
+ spin_lock_init(&rt->in_lock);
+ spin_lock_init(&rt->out_lock);
+
+ comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
+ usb6fire_midi_out_handler);
+
+ ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
+ if (ret < 0) {
+ kfree(rt->out_buffer);
+ kfree(rt);
+ dev_err(&chip->dev->dev, "unable to create midi.\n");
+ return ret;
+ }
+ rt->instance->private_data = rt;
+ strcpy(rt->instance->name, "DMX6FireUSB MIDI");
+ rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &out_ops);
+ snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
+ &in_ops);
+
+ chip->midi = rt;
+ return 0;
+}
+
+void usb6fire_midi_abort(struct sfire_chip *chip)
+{
+ struct midi_runtime *rt = chip->midi;
+
+ if (rt)
+ usb_poison_urb(&rt->out_urb);
+}
+
+void usb6fire_midi_destroy(struct sfire_chip *chip)
+{
+ struct midi_runtime *rt = chip->midi;
+
+ kfree(rt->out_buffer);
+ kfree(rt);
+ chip->midi = NULL;
+}
diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h
new file mode 100644
index 00000000000..84851b9f555
--- /dev/null
+++ b/sound/usb/6fire/midi.h
@@ -0,0 +1,41 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef USB6FIRE_MIDI_H
+#define USB6FIRE_MIDI_H
+
+#include "common.h"
+
+struct midi_runtime {
+ struct sfire_chip *chip;
+ struct snd_rawmidi *instance;
+
+ struct snd_rawmidi_substream *in;
+ char in_active;
+
+ spinlock_t in_lock;
+ spinlock_t out_lock;
+ struct snd_rawmidi_substream *out;
+ struct urb out_urb;
+ u8 out_serial; /* serial number of out packet */
+ u8 *out_buffer;
+ int buffer_offset;
+
+ void (*in_received)(struct midi_runtime *rt, u8 *data, int length);
+};
+
+int usb6fire_midi_init(struct sfire_chip *chip);
+void usb6fire_midi_abort(struct sfire_chip *chip);
+void usb6fire_midi_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_MIDI_H */
+
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
new file mode 100644
index 00000000000..ba40489b2de
--- /dev/null
+++ b/sound/usb/6fire/pcm.c
@@ -0,0 +1,717 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * PCM driver
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "pcm.h"
+#include "chip.h"
+#include "comm.h"
+#include "control.h"
+
+enum {
+ OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
+};
+
+/* keep next two synced with
+ * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
+ * and CONTROL_RATE_XXX in control.h */
+static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
+static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
+static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
+static const int rates_alsaid[] = {
+ SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
+ SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
+ SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
+
+enum { /* settings for pcm */
+ OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
+};
+
+enum { /* pcm streaming states */
+ STREAM_DISABLED, /* no pcm streaming */
+ STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
+ STREAM_RUNNING, /* pcm streaming running */
+ STREAM_STOPPING
+};
+
+static const struct snd_pcm_hardware pcm_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH,
+
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+
+ .rate_min = 44100,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 0, /* set in pcm_open, depending on capture/playback */
+ .buffer_bytes_max = MAX_BUFSIZE,
+ .period_bytes_min = PCM_N_PACKETS_PER_URB * (PCM_MAX_PACKET_SIZE - 4),
+ .period_bytes_max = MAX_BUFSIZE,
+ .periods_min = 2,
+ .periods_max = 1024
+};
+
+static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
+{
+ int ret;
+ struct control_runtime *ctrl_rt = rt->chip->control;
+
+ ctrl_rt->usb_streaming = false;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
+ if (ret < 0) {
+ dev_err(&rt->chip->dev->dev,
+ "error stopping streaming while setting samplerate %d.\n",
+ rates[rt->rate]);
+ return ret;
+ }
+
+ ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
+ if (ret < 0) {
+ dev_err(&rt->chip->dev->dev,
+ "error setting samplerate %d.\n",
+ rates[rt->rate]);
+ return ret;
+ }
+
+ ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
+ false, false);
+ if (ret < 0) {
+ dev_err(&rt->chip->dev->dev,
+ "error initializing channels while setting samplerate %d.\n",
+ rates[rt->rate]);
+ return ret;
+ }
+
+ ctrl_rt->usb_streaming = true;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
+ if (ret < 0) {
+ dev_err(&rt->chip->dev->dev,
+ "error starting streaming while setting samplerate %d.\n",
+ rates[rt->rate]);
+ return ret;
+ }
+
+ rt->in_n_analog = IN_N_CHANNELS;
+ rt->out_n_analog = OUT_N_CHANNELS;
+ rt->in_packet_size = rates_in_packet_size[rt->rate];
+ rt->out_packet_size = rates_out_packet_size[rt->rate];
+ return 0;
+}
+
+static struct pcm_substream *usb6fire_pcm_get_substream(
+ struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &rt->playback;
+ else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return &rt->capture;
+ dev_err(&rt->chip->dev->dev, "error getting pcm substream slot.\n");
+ return NULL;
+}
+
+/* call with stream_mutex locked */
+static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
+{
+ int i;
+ struct control_runtime *ctrl_rt = rt->chip->control;
+
+ if (rt->stream_state != STREAM_DISABLED) {
+
+ rt->stream_state = STREAM_STOPPING;
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ usb_kill_urb(&rt->in_urbs[i].instance);
+ usb_kill_urb(&rt->out_urbs[i].instance);
+ }
+ ctrl_rt->usb_streaming = false;
+ ctrl_rt->update_streaming(ctrl_rt);
+ rt->stream_state = STREAM_DISABLED;
+ }
+}
+
+/* call with stream_mutex locked */
+static int usb6fire_pcm_stream_start(struct pcm_runtime *rt)
+{
+ int ret;
+ int i;
+ int k;
+ struct usb_iso_packet_descriptor *packet;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+ /* submit our in urbs */
+ rt->stream_wait_cond = false;
+ rt->stream_state = STREAM_STARTING;
+ for (i = 0; i < PCM_N_URBS; i++) {
+ for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) {
+ packet = &rt->in_urbs[i].packets[k];
+ packet->offset = k * rt->in_packet_size;
+ packet->length = rt->in_packet_size;
+ packet->actual_length = 0;
+ packet->status = 0;
+ }
+ ret = usb_submit_urb(&rt->in_urbs[i].instance,
+ GFP_ATOMIC);
+ if (ret) {
+ usb6fire_pcm_stream_stop(rt);
+ return ret;
+ }
+ }
+
+ /* wait for first out urb to return (sent in in urb handler) */
+ wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
+ HZ);
+ if (rt->stream_wait_cond)
+ rt->stream_state = STREAM_RUNNING;
+ else {
+ usb6fire_pcm_stream_stop(rt);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/* call with substream locked */
+static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
+{
+ int i;
+ int frame;
+ int frame_count;
+ unsigned int total_length = 0;
+ struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
+ struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
+ u32 *src = NULL;
+ u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
+ * (alsa_rt->frame_bits >> 3));
+ u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
+ * (alsa_rt->frame_bits >> 3));
+ int bytes_per_frame = alsa_rt->channels << 2;
+
+ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+ /* at least 4 header bytes for valid packet.
+ * after that: 32 bits per sample for analog channels */
+ if (urb->packets[i].actual_length > 4)
+ frame_count = (urb->packets[i].actual_length - 4)
+ / (rt->in_n_analog << 2);
+ else
+ frame_count = 0;
+
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ src = (u32 *) (urb->buffer + total_length);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ src = (u32 *) (urb->buffer - 1 + total_length);
+ else
+ return;
+ src++; /* skip leading 4 bytes of every packet */
+ total_length += urb->packets[i].length;
+ for (frame = 0; frame < frame_count; frame++) {
+ memcpy(dest, src, bytes_per_frame);
+ dest += alsa_rt->channels;
+ src += rt->in_n_analog;
+ sub->dma_off++;
+ sub->period_off++;
+ if (dest == dest_end) {
+ sub->dma_off = 0;
+ dest = (u32 *) alsa_rt->dma_area;
+ }
+ }
+ }
+}
+
+/* call with substream locked */
+static void usb6fire_pcm_playback(struct pcm_substream *sub,
+ struct pcm_urb *urb)
+{
+ int i;
+ int frame;
+ int frame_count;
+ struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
+ struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
+ u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off
+ * (alsa_rt->frame_bits >> 3));
+ u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
+ * (alsa_rt->frame_bits >> 3));
+ u32 *dest;
+ int bytes_per_frame = alsa_rt->channels << 2;
+
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ dest = (u32 *) (urb->buffer - 1);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ dest = (u32 *) (urb->buffer);
+ else {
+ dev_err(&rt->chip->dev->dev, "Unknown sample format.");
+ return;
+ }
+
+ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+ /* at least 4 header bytes for valid packet.
+ * after that: 32 bits per sample for analog channels */
+ if (urb->packets[i].length > 4)
+ frame_count = (urb->packets[i].length - 4)
+ / (rt->out_n_analog << 2);
+ else
+ frame_count = 0;
+ dest++; /* skip leading 4 bytes of every frame */
+ for (frame = 0; frame < frame_count; frame++) {
+ memcpy(dest, src, bytes_per_frame);
+ src += alsa_rt->channels;
+ dest += rt->out_n_analog;
+ sub->dma_off++;
+ sub->period_off++;
+ if (src == src_end) {
+ src = (u32 *) alsa_rt->dma_area;
+ sub->dma_off = 0;
+ }
+ }
+ }
+}
+
+static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
+{
+ struct pcm_urb *in_urb = usb_urb->context;
+ struct pcm_urb *out_urb = in_urb->peer;
+ struct pcm_runtime *rt = in_urb->chip->pcm;
+ struct pcm_substream *sub;
+ unsigned long flags;
+ int total_length = 0;
+ int frame_count;
+ int frame;
+ int channel;
+ int i;
+ u8 *dest;
+
+ if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING)
+ return;
+ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
+ if (in_urb->packets[i].status) {
+ rt->panic = true;
+ return;
+ }
+
+ if (rt->stream_state == STREAM_DISABLED) {
+ dev_err(&rt->chip->dev->dev,
+ "internal error: stream disabled in in-urb handler.\n");
+ return;
+ }
+
+ /* receive our capture data */
+ sub = &rt->capture;
+ spin_lock_irqsave(&sub->lock, flags);
+ if (sub->active) {
+ usb6fire_pcm_capture(sub, in_urb);
+ if (sub->period_off >= sub->instance->runtime->period_size) {
+ sub->period_off %= sub->instance->runtime->period_size;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ snd_pcm_period_elapsed(sub->instance);
+ } else
+ spin_unlock_irqrestore(&sub->lock, flags);
+ } else
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ /* setup out urb structure */
+ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
+ out_urb->packets[i].offset = total_length;
+ out_urb->packets[i].length = (in_urb->packets[i].actual_length
+ - 4) / (rt->in_n_analog << 2)
+ * (rt->out_n_analog << 2) + 4;
+ out_urb->packets[i].status = 0;
+ total_length += out_urb->packets[i].length;
+ }
+ memset(out_urb->buffer, 0, total_length);
+
+ /* now send our playback data (if a free out urb was found) */
+ sub = &rt->playback;
+ spin_lock_irqsave(&sub->lock, flags);
+ if (sub->active) {
+ usb6fire_pcm_playback(sub, out_urb);
+ if (sub->period_off >= sub->instance->runtime->period_size) {
+ sub->period_off %= sub->instance->runtime->period_size;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ snd_pcm_period_elapsed(sub->instance);
+ } else
+ spin_unlock_irqrestore(&sub->lock, flags);
+ } else
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ /* setup the 4th byte of each sample (0x40 for analog channels) */
+ dest = out_urb->buffer;
+ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
+ if (out_urb->packets[i].length >= 4) {
+ frame_count = (out_urb->packets[i].length - 4)
+ / (rt->out_n_analog << 2);
+ *(dest++) = 0xaa;
+ *(dest++) = 0xaa;
+ *(dest++) = frame_count;
+ *(dest++) = 0x00;
+ for (frame = 0; frame < frame_count; frame++)
+ for (channel = 0;
+ channel < rt->out_n_analog;
+ channel++) {
+ dest += 3; /* skip sample data */
+ *(dest++) = 0x40;
+ }
+ }
+ usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
+ usb_submit_urb(&in_urb->instance, GFP_ATOMIC);
+}
+
+static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb)
+{
+ struct pcm_urb *urb = usb_urb->context;
+ struct pcm_runtime *rt = urb->chip->pcm;
+
+ if (rt->stream_state == STREAM_STARTING) {
+ rt->stream_wait_cond = true;
+ wake_up(&rt->stream_wait_queue);
+ }
+}
+
+static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = NULL;
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+
+ if (rt->panic)
+ return -EPIPE;
+
+ mutex_lock(&rt->stream_mutex);
+ alsa_rt->hw = pcm_hw;
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (rt->rate < ARRAY_SIZE(rates))
+ alsa_rt->hw.rates = rates_alsaid[rt->rate];
+ alsa_rt->hw.channels_max = OUT_N_CHANNELS;
+ sub = &rt->playback;
+ } else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (rt->rate < ARRAY_SIZE(rates))
+ alsa_rt->hw.rates = rates_alsaid[rt->rate];
+ alsa_rt->hw.channels_max = IN_N_CHANNELS;
+ sub = &rt->capture;
+ }
+
+ if (!sub) {
+ mutex_unlock(&rt->stream_mutex);
+ dev_err(&rt->chip->dev->dev, "invalid stream type.\n");
+ return -EINVAL;
+ }
+
+ sub->instance = alsa_sub;
+ sub->active = false;
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+ unsigned long flags;
+
+ if (rt->panic)
+ return 0;
+
+ mutex_lock(&rt->stream_mutex);
+ if (sub) {
+ /* deactivate substream */
+ spin_lock_irqsave(&sub->lock, flags);
+ sub->instance = NULL;
+ sub->active = false;
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ /* all substreams closed? if so, stop streaming */
+ if (!rt->playback.instance && !rt->capture.instance) {
+ usb6fire_pcm_stream_stop(rt);
+ rt->rate = ARRAY_SIZE(rates);
+ }
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub,
+ params_buffer_bytes(hw_params));
+}
+
+static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(alsa_sub);
+}
+
+static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+ int ret;
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ mutex_lock(&rt->stream_mutex);
+ sub->dma_off = 0;
+ sub->period_off = 0;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+ for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
+ if (alsa_rt->rate == rates[rt->rate])
+ break;
+ if (rt->rate == ARRAY_SIZE(rates)) {
+ mutex_unlock(&rt->stream_mutex);
+ dev_err(&rt->chip->dev->dev,
+ "invalid rate %d in prepare.\n",
+ alsa_rt->rate);
+ return -EINVAL;
+ }
+
+ ret = usb6fire_pcm_set_rate(rt);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ ret = usb6fire_pcm_stream_start(rt);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ dev_err(&rt->chip->dev->dev,
+ "could not start pcm stream.\n");
+ return ret;
+ }
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int usb6fire_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
+{
+ struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ unsigned long flags;
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irqsave(&sub->lock, flags);
+ sub->active = true;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irqsave(&sub->lock, flags);
+ sub->active = false;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t usb6fire_pcm_pointer(
+ struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ unsigned long flags;
+ snd_pcm_uframes_t ret;
+
+ if (rt->panic || !sub)
+ return SNDRV_PCM_POS_XRUN;
+
+ spin_lock_irqsave(&sub->lock, flags);
+ ret = sub->dma_off;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ return ret;
+}
+
+static struct snd_pcm_ops pcm_ops = {
+ .open = usb6fire_pcm_open,
+ .close = usb6fire_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = usb6fire_pcm_hw_params,
+ .hw_free = usb6fire_pcm_hw_free,
+ .prepare = usb6fire_pcm_prepare,
+ .trigger = usb6fire_pcm_trigger,
+ .pointer = usb6fire_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+static void usb6fire_pcm_init_urb(struct pcm_urb *urb,
+ struct sfire_chip *chip, bool in, int ep,
+ void (*handler)(struct urb *))
+{
+ urb->chip = chip;
+ usb_init_urb(&urb->instance);
+ urb->instance.transfer_buffer = urb->buffer;
+ urb->instance.transfer_buffer_length =
+ PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE;
+ urb->instance.dev = chip->dev;
+ urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep)
+ : usb_sndisocpipe(chip->dev, ep);
+ urb->instance.interval = 1;
+ urb->instance.complete = handler;
+ urb->instance.context = urb;
+ urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB;
+}
+
+static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt)
+{
+ int i;
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ rt->out_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB
+ * PCM_MAX_PACKET_SIZE, GFP_KERNEL);
+ if (!rt->out_urbs[i].buffer)
+ return -ENOMEM;
+ rt->in_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB
+ * PCM_MAX_PACKET_SIZE, GFP_KERNEL);
+ if (!rt->in_urbs[i].buffer)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void usb6fire_pcm_buffers_destroy(struct pcm_runtime *rt)
+{
+ int i;
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ kfree(rt->out_urbs[i].buffer);
+ kfree(rt->in_urbs[i].buffer);
+ }
+}
+
+int usb6fire_pcm_init(struct sfire_chip *chip)
+{
+ int i;
+ int ret;
+ struct snd_pcm *pcm;
+ struct pcm_runtime *rt =
+ kzalloc(sizeof(struct pcm_runtime), GFP_KERNEL);
+
+ if (!rt)
+ return -ENOMEM;
+
+ ret = usb6fire_pcm_buffers_init(rt);
+ if (ret) {
+ usb6fire_pcm_buffers_destroy(rt);
+ kfree(rt);
+ return ret;
+ }
+
+ rt->chip = chip;
+ rt->stream_state = STREAM_DISABLED;
+ rt->rate = ARRAY_SIZE(rates);
+ init_waitqueue_head(&rt->stream_wait_queue);
+ mutex_init(&rt->stream_mutex);
+
+ spin_lock_init(&rt->playback.lock);
+ spin_lock_init(&rt->capture.lock);
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP,
+ usb6fire_pcm_in_urb_handler);
+ usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP,
+ usb6fire_pcm_out_urb_handler);
+
+ rt->in_urbs[i].peer = &rt->out_urbs[i];
+ rt->out_urbs[i].peer = &rt->in_urbs[i];
+ }
+
+ ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm);
+ if (ret < 0) {
+ usb6fire_pcm_buffers_destroy(rt);
+ kfree(rt);
+ dev_err(&chip->dev->dev, "cannot create pcm instance.\n");
+ return ret;
+ }
+
+ pcm->private_data = rt;
+ strcpy(pcm->name, "DMX 6Fire USB");
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops);
+
+ if (ret) {
+ usb6fire_pcm_buffers_destroy(rt);
+ kfree(rt);
+ dev_err(&chip->dev->dev,
+ "error preallocating pcm buffers.\n");
+ return ret;
+ }
+ rt->instance = pcm;
+
+ chip->pcm = rt;
+ return 0;
+}
+
+void usb6fire_pcm_abort(struct sfire_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+ unsigned long flags;
+ int i;
+
+ if (rt) {
+ rt->panic = true;
+
+ if (rt->playback.instance) {
+ snd_pcm_stream_lock_irqsave(rt->playback.instance, flags);
+ snd_pcm_stop(rt->playback.instance,
+ SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(rt->playback.instance, flags);
+ }
+
+ if (rt->capture.instance) {
+ snd_pcm_stream_lock_irqsave(rt->capture.instance, flags);
+ snd_pcm_stop(rt->capture.instance,
+ SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(rt->capture.instance, flags);
+ }
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ usb_poison_urb(&rt->in_urbs[i].instance);
+ usb_poison_urb(&rt->out_urbs[i].instance);
+ }
+
+ }
+}
+
+void usb6fire_pcm_destroy(struct sfire_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+
+ usb6fire_pcm_buffers_destroy(rt);
+ kfree(rt);
+ chip->pcm = NULL;
+}
diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h
new file mode 100644
index 00000000000..f5779d6182c
--- /dev/null
+++ b/sound/usb/6fire/pcm.h
@@ -0,0 +1,75 @@
+/*
+ * Linux driver for TerraTec DMX 6Fire USB
+ *
+ * Author: Torsten Schenk <torsten.schenk@zoho.com>
+ * Created: Jan 01, 2011
+ * Copyright: (C) Torsten Schenk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef USB6FIRE_PCM_H
+#define USB6FIRE_PCM_H
+
+#include <sound/pcm.h>
+#include <linux/mutex.h>
+
+#include "common.h"
+
+enum /* settings for pcm */
+{
+ /* maximum of EP_W_MAX_PACKET_SIZE[] (see firmware.c) */
+ PCM_N_URBS = 16, PCM_N_PACKETS_PER_URB = 8, PCM_MAX_PACKET_SIZE = 604
+};
+
+struct pcm_urb {
+ struct sfire_chip *chip;
+
+ /* BEGIN DO NOT SEPARATE */
+ struct urb instance;
+ struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB];
+ /* END DO NOT SEPARATE */
+ u8 *buffer;
+
+ struct pcm_urb *peer;
+};
+
+struct pcm_substream {
+ spinlock_t lock;
+ struct snd_pcm_substream *instance;
+
+ bool active;
+
+ snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */
+ snd_pcm_uframes_t period_off; /* current position in current period */
+};
+
+struct pcm_runtime {
+ struct sfire_chip *chip;
+ struct snd_pcm *instance;
+
+ struct pcm_substream playback;
+ struct pcm_substream capture;
+ bool panic; /* if set driver won't do anymore pcm on device */
+
+ struct pcm_urb in_urbs[PCM_N_URBS];
+ struct pcm_urb out_urbs[PCM_N_URBS];
+ int in_packet_size;
+ int out_packet_size;
+ int in_n_analog; /* number of analog channels soundcard sends */
+ int out_n_analog; /* number of analog channels soundcard receives */
+
+ struct mutex stream_mutex;
+ u8 stream_state; /* one of STREAM_XXX (pcm.c) */
+ u8 rate; /* one of PCM_RATE_XXX */
+ wait_queue_head_t stream_wait_queue;
+ bool stream_wait_cond;
+};
+
+int usb6fire_pcm_init(struct sfire_chip *chip);
+void usb6fire_pcm_abort(struct sfire_chip *chip);
+void usb6fire_pcm_destroy(struct sfire_chip *chip);
+#endif /* USB6FIRE_PCM_H */
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 44d6d2ec964..d393153c474 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -14,6 +14,7 @@ config SND_USB_AUDIO
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
+ select BITREVERSE
help
Say Y here to include support for USB audio and USB MIDI
devices.
@@ -62,9 +63,12 @@ config SND_USB_CAIAQ
* Native Instruments Audio 2 DJ
* Native Instruments Audio 4 DJ
* Native Instruments Audio 8 DJ
+ * Native Instruments Traktor Audio 2
* Native Instruments Guitar Rig Session I/O
* Native Instruments Guitar Rig mobile
* Native Instruments Traktor Kontrol X1
+ * Native Instruments Traktor Kontrol S4
+ * Native Instruments Maschine Controller
To compile this driver as a module, choose M here: the module
will be called snd-usb-caiaq.
@@ -82,10 +86,12 @@ config SND_USB_CAIAQ_INPUT
* Native Instruments Kore Controller
* Native Instruments Kore Controller 2
* Native Instruments Audio Kontrol 1
+ * Native Instruments Traktor Kontrol S4
+ * Native Instruments Maschine Controller
config SND_USB_US122L
tristate "Tascam US-122L USB driver"
- depends on X86 && EXPERIMENTAL
+ depends on X86
select SND_HWDEP
select SND_RAWMIDI
help
@@ -95,5 +101,64 @@ config SND_USB_US122L
To compile this driver as a module, choose M here: the module
will be called snd-usb-us122l.
+config SND_USB_6FIRE
+ tristate "TerraTec DMX 6Fire USB"
+ select FW_LOADER
+ select BITREVERSE
+ select SND_RAWMIDI
+ select SND_PCM
+ select SND_VMASTER
+ help
+ Say Y here to include support for TerraTec 6fire DMX USB interface.
+
+ You will need firmware files in order to be able to use the device
+ after it has been coldstarted. An install script for the firmware
+ and further help can be found at
+ http://sixfireusb.sourceforge.net
+
+config SND_USB_HIFACE
+ tristate "M2Tech hiFace USB-SPDIF driver"
+ select SND_PCM
+ help
+ Select this option to include support for M2Tech hiFace USB-SPDIF
+ interface.
+
+ This driver supports the original M2Tech hiFace and some other
+ compatible devices. The supported products are:
+
+ * M2Tech Young
+ * M2Tech hiFace
+ * M2Tech North Star
+ * M2Tech W4S Young
+ * M2Tech Corrson
+ * M2Tech AUDIA
+ * M2Tech SL Audio
+ * M2Tech Empirical
+ * M2Tech Rockna
+ * M2Tech Pathos
+ * M2Tech Metronome
+ * M2Tech CAD
+ * M2Tech Audio Esclusive
+ * M2Tech Rotel
+ * M2Tech Eeaudio
+ * The Chord Company CHORD
+ * AVA Group A/S Vitus
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-hiface.
+
+config SND_BCD2000
+ tristate "Behringer BCD2000 MIDI driver"
+ select SND_RAWMIDI
+ help
+ Say Y here to include MIDI support for the Behringer BCD2000 DJ
+ controller.
+
+ Audio support is still work-in-progress at
+ https://github.com/anyc/snd-usb-bcd2000
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bcd2000.
+
endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 1e362bf8834..2b92f0dcbc4 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -3,16 +3,16 @@
#
snd-usb-audio-objs := card.o \
+ clock.o \
+ endpoint.o \
+ format.o \
+ helper.o \
mixer.o \
mixer_quirks.o \
+ pcm.o \
proc.o \
quirks.o \
- format.o \
- endpoint.o \
- urb.o \
- pcm.o \
- helper.o \
- clock.o
+ stream.o
snd-usbmidi-lib-objs := midi.o
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
diff --git a/sound/usb/bcd2000/Makefile b/sound/usb/bcd2000/Makefile
new file mode 100644
index 00000000000..f09ccc0af6f
--- /dev/null
+++ b/sound/usb/bcd2000/Makefile
@@ -0,0 +1,3 @@
+snd-bcd2000-y := bcd2000.o
+
+obj-$(CONFIG_SND_BCD2000) += snd-bcd2000.o \ No newline at end of file
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
new file mode 100644
index 00000000000..820d6ca8c45
--- /dev/null
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -0,0 +1,461 @@
+/*
+ * Behringer BCD2000 driver
+ *
+ * Copyright (C) 2014 Mario Kicherer (dev@kicherer.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#define PREFIX "snd-bcd2000: "
+#define BUFSIZE 64
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x1397, 0x00bd) },
+ { },
+};
+
+static unsigned char device_cmd_prefix[] = {0x03, 0x00};
+
+static unsigned char bcd2000_init_sequence[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81,
+ 0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7,
+ 0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0xfa, 0x11, 0xff, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf2, 0x34, 0x4a, 0xf7,
+ 0x18, 0xfa, 0x11, 0xff
+};
+
+struct bcd2000 {
+ struct usb_device *dev;
+ struct snd_card *card;
+ struct usb_interface *intf;
+ int card_index;
+
+ int midi_out_active;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_receive_substream;
+ struct snd_rawmidi_substream *midi_out_substream;
+
+ unsigned char midi_in_buf[BUFSIZE];
+ unsigned char midi_out_buf[BUFSIZE];
+
+ struct urb *midi_out_urb;
+ struct urb *midi_in_urb;
+
+ struct usb_anchor anchor;
+};
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+
+static DEFINE_MUTEX(devices_mutex);
+DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+static struct usb_driver bcd2000_driver;
+
+#ifdef CONFIG_SND_DEBUG
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len)
+{
+ print_hex_dump(KERN_DEBUG, prefix,
+ DUMP_PREFIX_NONE, 16, 1,
+ buf, len, false);
+}
+#else
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len) {}
+#endif
+
+static int bcd2000_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int bcd2000_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+ bcd2k->midi_receive_substream = up ? substream : NULL;
+}
+
+static void bcd2000_midi_handle_input(struct bcd2000 *bcd2k,
+ const unsigned char *buf, unsigned int buf_len)
+{
+ unsigned int payload_length, tocopy;
+ struct snd_rawmidi_substream *midi_receive_substream;
+
+ midi_receive_substream = ACCESS_ONCE(bcd2k->midi_receive_substream);
+ if (!midi_receive_substream)
+ return;
+
+ bcd2000_dump_buffer(PREFIX "received from device: ", buf, buf_len);
+
+ if (buf_len < 2)
+ return;
+
+ payload_length = buf[0];
+
+ /* ignore packets without payload */
+ if (payload_length == 0)
+ return;
+
+ tocopy = min(payload_length, buf_len-1);
+
+ bcd2000_dump_buffer(PREFIX "sending to userspace: ",
+ &buf[1], tocopy);
+
+ snd_rawmidi_receive(midi_receive_substream,
+ &buf[1], tocopy);
+}
+
+static void bcd2000_midi_send(struct bcd2000 *bcd2k)
+{
+ int len, ret;
+ struct snd_rawmidi_substream *midi_out_substream;
+
+ BUILD_BUG_ON(sizeof(device_cmd_prefix) >= BUFSIZE);
+
+ midi_out_substream = ACCESS_ONCE(bcd2k->midi_out_substream);
+ if (!midi_out_substream)
+ return;
+
+ /* copy command prefix bytes */
+ memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
+ sizeof(device_cmd_prefix));
+
+ /*
+ * get MIDI packet and leave space for command prefix
+ * and payload length
+ */
+ len = snd_rawmidi_transmit(midi_out_substream,
+ bcd2k->midi_out_buf + 3, BUFSIZE - 3);
+
+ if (len < 0)
+ dev_err(&bcd2k->dev->dev, "%s: snd_rawmidi_transmit error %d\n",
+ __func__, len);
+
+ if (len <= 0)
+ return;
+
+ /* set payload length */
+ bcd2k->midi_out_buf[2] = len;
+ bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
+
+ bcd2000_dump_buffer(PREFIX "sending to device: ",
+ bcd2k->midi_out_buf, len+3);
+
+ /* send packet to the BCD2000 */
+ ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s (%p): usb_submit_urb() failed, ret=%d, len=%d\n",
+ __func__, midi_out_substream, ret, len);
+ else
+ bcd2k->midi_out_active = 1;
+}
+
+static int bcd2000_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+ if (bcd2k->midi_out_active) {
+ usb_kill_urb(bcd2k->midi_out_urb);
+ bcd2k->midi_out_active = 0;
+ }
+
+ return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+ if (up) {
+ bcd2k->midi_out_substream = substream;
+ /* check if there is data userspace wants to send */
+ if (!bcd2k->midi_out_active)
+ bcd2000_midi_send(bcd2k);
+ } else {
+ bcd2k->midi_out_substream = NULL;
+ }
+}
+
+static void bcd2000_output_complete(struct urb *urb)
+{
+ struct bcd2000 *bcd2k = urb->context;
+
+ bcd2k->midi_out_active = 0;
+
+ if (urb->status)
+ dev_warn(&urb->dev->dev,
+ PREFIX "output urb->status: %d\n", urb->status);
+
+ if (urb->status == -ESHUTDOWN)
+ return;
+
+ /* check if there is more data userspace wants to send */
+ bcd2000_midi_send(bcd2k);
+}
+
+static void bcd2000_input_complete(struct urb *urb)
+{
+ int ret;
+ struct bcd2000 *bcd2k = urb->context;
+
+ if (urb->status)
+ dev_warn(&urb->dev->dev,
+ PREFIX "input urb->status: %i\n", urb->status);
+
+ if (!bcd2k || urb->status == -ESHUTDOWN)
+ return;
+
+ if (urb->actual_length > 0)
+ bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer,
+ urb->actual_length);
+
+ /* return URB to device */
+ ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() failed, ret=%d\n",
+ __func__, ret);
+}
+
+static struct snd_rawmidi_ops bcd2000_midi_output = {
+ .open = bcd2000_midi_output_open,
+ .close = bcd2000_midi_output_close,
+ .trigger = bcd2000_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops bcd2000_midi_input = {
+ .open = bcd2000_midi_input_open,
+ .close = bcd2000_midi_input_close,
+ .trigger = bcd2000_midi_input_trigger,
+};
+
+static void bcd2000_init_device(struct bcd2000 *bcd2k)
+{
+ int ret;
+
+ init_usb_anchor(&bcd2k->anchor);
+ usb_anchor_urb(bcd2k->midi_out_urb, &bcd2k->anchor);
+ usb_anchor_urb(bcd2k->midi_in_urb, &bcd2k->anchor);
+
+ /* copy init sequence into buffer */
+ memcpy(bcd2k->midi_out_buf, bcd2000_init_sequence, 52);
+ bcd2k->midi_out_urb->transfer_buffer_length = 52;
+
+ /* submit sequence */
+ ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_KERNEL);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() out failed, ret=%d: ",
+ __func__, ret);
+ else
+ bcd2k->midi_out_active = 1;
+
+ /* pass URB to device to enable button and controller events */
+ ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_KERNEL);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() in failed, ret=%d: ",
+ __func__, ret);
+
+ /* ensure initialization is finished */
+ usb_wait_anchor_empty_timeout(&bcd2k->anchor, 1000);
+}
+
+static int bcd2000_init_midi(struct bcd2000 *bcd2k)
+{
+ int ret;
+ struct snd_rawmidi *rmidi;
+
+ ret = snd_rawmidi_new(bcd2k->card, bcd2k->card->shortname, 0,
+ 1, /* output */
+ 1, /* input */
+ &rmidi);
+
+ if (ret < 0)
+ return ret;
+
+ strlcpy(rmidi->name, bcd2k->card->shortname, sizeof(rmidi->name));
+
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = bcd2k;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &bcd2000_midi_output);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &bcd2000_midi_input);
+
+ bcd2k->rmidi = rmidi;
+
+ bcd2k->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ bcd2k->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!bcd2k->midi_in_urb || !bcd2k->midi_out_urb) {
+ dev_err(&bcd2k->dev->dev, PREFIX "usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+
+ usb_fill_int_urb(bcd2k->midi_in_urb, bcd2k->dev,
+ usb_rcvintpipe(bcd2k->dev, 0x81),
+ bcd2k->midi_in_buf, BUFSIZE,
+ bcd2000_input_complete, bcd2k, 1);
+
+ usb_fill_int_urb(bcd2k->midi_out_urb, bcd2k->dev,
+ usb_sndintpipe(bcd2k->dev, 0x1),
+ bcd2k->midi_out_buf, BUFSIZE,
+ bcd2000_output_complete, bcd2k, 1);
+
+ bcd2000_init_device(bcd2k);
+
+ return 0;
+}
+
+static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
+ struct usb_interface *interface)
+{
+ /* usb_kill_urb not necessary, urb is aborted automatically */
+
+ usb_free_urb(bcd2k->midi_out_urb);
+ usb_free_urb(bcd2k->midi_in_urb);
+
+ if (bcd2k->intf) {
+ usb_set_intfdata(bcd2k->intf, NULL);
+ bcd2k->intf = NULL;
+ }
+}
+
+static int bcd2000_probe(struct usb_interface *interface,
+ const struct usb_device_id *usb_id)
+{
+ struct snd_card *card;
+ struct bcd2000 *bcd2k;
+ unsigned int card_index;
+ char usb_path[32];
+ int err;
+
+ mutex_lock(&devices_mutex);
+
+ for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
+ if (!test_bit(card_index, devices_used))
+ break;
+
+ if (card_index >= SNDRV_CARDS) {
+ mutex_unlock(&devices_mutex);
+ return -ENOENT;
+ }
+
+ err = snd_card_new(&interface->dev, index[card_index], id[card_index],
+ THIS_MODULE, sizeof(*bcd2k), &card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return err;
+ }
+
+ bcd2k = card->private_data;
+ bcd2k->dev = interface_to_usbdev(interface);
+ bcd2k->card = card;
+ bcd2k->card_index = card_index;
+ bcd2k->intf = interface;
+
+ snd_card_set_dev(card, &interface->dev);
+
+ strncpy(card->driver, "snd-bcd2000", sizeof(card->driver));
+ strncpy(card->shortname, "BCD2000", sizeof(card->shortname));
+ usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path));
+ snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname),
+ "Behringer BCD2000 at %s",
+ usb_path);
+
+ err = bcd2000_init_midi(bcd2k);
+ if (err < 0)
+ goto probe_error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto probe_error;
+
+ usb_set_intfdata(interface, bcd2k);
+ set_bit(card_index, devices_used);
+
+ mutex_unlock(&devices_mutex);
+ return 0;
+
+probe_error:
+ dev_info(&bcd2k->dev->dev, PREFIX "error during probing");
+ bcd2000_free_usb_related_resources(bcd2k, interface);
+ snd_card_free(card);
+ mutex_unlock(&devices_mutex);
+ return err;
+}
+
+static void bcd2000_disconnect(struct usb_interface *interface)
+{
+ struct bcd2000 *bcd2k = usb_get_intfdata(interface);
+
+ if (!bcd2k)
+ return;
+
+ mutex_lock(&devices_mutex);
+
+ /* make sure that userspace cannot create new requests */
+ snd_card_disconnect(bcd2k->card);
+
+ bcd2000_free_usb_related_resources(bcd2k, interface);
+
+ clear_bit(bcd2k->card_index, devices_used);
+
+ snd_card_free_when_closed(bcd2k->card);
+
+ mutex_unlock(&devices_mutex);
+}
+
+static struct usb_driver bcd2000_driver = {
+ .name = "snd-bcd2000",
+ .probe = bcd2000_probe,
+ .disconnect = bcd2000_disconnect,
+ .id_table = id_table,
+};
+
+module_usb_driver(bcd2000_driver);
+
+MODULE_DEVICE_TABLE(usb, id_table);
+MODULE_AUTHOR("Mario Kicherer, dev@kicherer.org");
+MODULE_DESCRIPTION("Behringer BCD2000 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index 4328cad6c3a..7103b0908d1 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -16,6 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -39,8 +40,8 @@
#define ENDPOINT_CAPTURE 2
#define ENDPOINT_PLAYBACK 6
-#define MAKE_CHECKBYTE(dev,stream,i) \
- (stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
+#define MAKE_CHECKBYTE(cdev,stream,i) \
+ (stream << 1) | (~(i / (cdev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
@@ -60,32 +61,32 @@ static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
};
static void
-activate_substream(struct snd_usb_caiaqdev *dev,
+activate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
- spin_lock(&dev->spinlock);
+ spin_lock(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dev->sub_playback[sub->number] = sub;
+ cdev->sub_playback[sub->number] = sub;
else
- dev->sub_capture[sub->number] = sub;
+ cdev->sub_capture[sub->number] = sub;
- spin_unlock(&dev->spinlock);
+ spin_unlock(&cdev->spinlock);
}
static void
-deactivate_substream(struct snd_usb_caiaqdev *dev,
+deactivate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
unsigned long flags;
- spin_lock_irqsave(&dev->spinlock, flags);
+ spin_lock_irqsave(&cdev->spinlock, flags);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dev->sub_playback[sub->number] = NULL;
+ cdev->sub_playback[sub->number] = NULL;
else
- dev->sub_capture[sub->number] = NULL;
+ cdev->sub_capture[sub->number] = NULL;
- spin_unlock_irqrestore(&dev->spinlock, flags);
+ spin_unlock_irqrestore(&cdev->spinlock, flags);
}
static int
@@ -98,28 +99,30 @@ all_substreams_zero(struct snd_pcm_substream **subs)
return 1;
}
-static int stream_start(struct snd_usb_caiaqdev *dev)
+static int stream_start(struct snd_usb_caiaqdev *cdev)
{
int i, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, dev);
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
- if (dev->streaming)
+ if (cdev->streaming)
return -EINVAL;
- memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
- memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
- dev->input_panic = 0;
- dev->output_panic = 0;
- dev->first_packet = 1;
- dev->streaming = 1;
- dev->warned = 0;
+ memset(cdev->sub_playback, 0, sizeof(cdev->sub_playback));
+ memset(cdev->sub_capture, 0, sizeof(cdev->sub_capture));
+ cdev->input_panic = 0;
+ cdev->output_panic = 0;
+ cdev->first_packet = 4;
+ cdev->streaming = 1;
+ cdev->warned = 0;
for (i = 0; i < N_URBS; i++) {
- ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC);
+ ret = usb_submit_urb(cdev->data_urbs_in[i], GFP_ATOMIC);
if (ret) {
- log("unable to trigger read #%d! (ret %d)\n", i, ret);
- dev->streaming = 0;
+ dev_err(dev, "unable to trigger read #%d! (ret %d)\n",
+ i, ret);
+ cdev->streaming = 0;
return -EPIPE;
}
}
@@ -127,60 +130,68 @@ static int stream_start(struct snd_usb_caiaqdev *dev)
return 0;
}
-static void stream_stop(struct snd_usb_caiaqdev *dev)
+static void stream_stop(struct snd_usb_caiaqdev *cdev)
{
int i;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, dev);
- if (!dev->streaming)
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
+ if (!cdev->streaming)
return;
- dev->streaming = 0;
+ cdev->streaming = 0;
for (i = 0; i < N_URBS; i++) {
- usb_kill_urb(dev->data_urbs_in[i]);
- usb_kill_urb(dev->data_urbs_out[i]);
+ usb_kill_urb(cdev->data_urbs_in[i]);
+
+ if (test_bit(i, &cdev->outurb_active_mask))
+ usb_kill_urb(cdev->data_urbs_out[i]);
}
+
+ cdev->outurb_active_mask = 0;
}
static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
- debug("%s(%p)\n", __func__, substream);
- substream->runtime->hw = dev->pcm_info;
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
+ substream->runtime->hw = cdev->pcm_info;
snd_pcm_limit_hw_rates(substream->runtime);
+
return 0;
}
static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, substream);
- if (all_substreams_zero(dev->sub_playback) &&
- all_substreams_zero(dev->sub_capture)) {
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
+ if (all_substreams_zero(cdev->sub_playback) &&
+ all_substreams_zero(cdev->sub_capture)) {
/* when the last client has stopped streaming,
* all sample rates are allowed again */
- stream_stop(dev);
- dev->pcm_info.rates = dev->samplerates;
+ stream_stop(cdev);
+ cdev->pcm_info.rates = cdev->samplerates;
}
return 0;
}
static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
- struct snd_pcm_hw_params *hw_params)
+ struct snd_pcm_hw_params *hw_params)
{
- debug("%s(%p)\n", __func__, sub);
- return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
+ return snd_pcm_lib_alloc_vmalloc_buffer(sub,
+ params_buffer_bytes(hw_params));
}
static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
- debug("%s(%p)\n", __func__, sub);
- deactivate_substream(dev, sub);
- return snd_pcm_lib_free_pages(sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
+ deactivate_substream(cdev, sub);
+ return snd_pcm_lib_free_vmalloc_buffer(sub);
}
/* this should probably go upstream */
@@ -189,60 +200,88 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
#endif
static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+ 48000, 64000, 88200, 96000, 176400, 192000 };
static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
{
int bytes_per_sample, bpp, ret, i;
int index = substream->number;
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, substream);
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
- dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
+ int out_pos;
+
+ switch (cdev->spec.data_alignment) {
+ case 0:
+ case 2:
+ out_pos = BYTES_PER_SAMPLE + 1;
+ break;
+ case 3:
+ default:
+ out_pos = 0;
+ break;
+ }
+
+ cdev->period_out_count[index] = out_pos;
+ cdev->audio_out_buf_pos[index] = out_pos;
} else {
- int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2;
- dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos;
- dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos;
+ int in_pos;
+
+ switch (cdev->spec.data_alignment) {
+ case 0:
+ in_pos = BYTES_PER_SAMPLE + 2;
+ break;
+ case 2:
+ in_pos = BYTES_PER_SAMPLE;
+ break;
+ case 3:
+ default:
+ in_pos = 0;
+ break;
+ }
+
+ cdev->period_in_count[index] = in_pos;
+ cdev->audio_in_buf_pos[index] = in_pos;
}
- if (dev->streaming)
+ if (cdev->streaming)
return 0;
/* the first client that opens a stream defines the sample rate
* setting for all subsequent calls, until the last client closed. */
for (i=0; i < ARRAY_SIZE(rates); i++)
if (runtime->rate == rates[i])
- dev->pcm_info.rates = 1 << i;
+ cdev->pcm_info.rates = 1 << i;
snd_pcm_limit_hw_rates(runtime);
bytes_per_sample = BYTES_PER_SAMPLE;
- if (dev->spec.data_alignment == 2)
+ if (cdev->spec.data_alignment >= 2)
bytes_per_sample++;
bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
- * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;
+ * bytes_per_sample * CHANNELS_PER_STREAM * cdev->n_streams;
if (bpp > MAX_ENDPOINT_SIZE)
bpp = MAX_ENDPOINT_SIZE;
- ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate,
+ ret = snd_usb_caiaq_set_audio_params(cdev, runtime->rate,
runtime->sample_bits, bpp);
if (ret)
return ret;
- ret = stream_start(dev);
+ ret = stream_start(cdev);
if (ret)
return ret;
- dev->output_running = 0;
- wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ);
- if (!dev->output_running) {
- stream_stop(dev);
+ cdev->output_running = 0;
+ wait_event_timeout(cdev->prepare_wait_queue, cdev->output_running, HZ);
+ if (!cdev->output_running) {
+ stream_stop(cdev);
return -EPIPE;
}
@@ -251,16 +290,19 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p) cmd %d\n", __func__, sub, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- activate_substream(dev, sub);
+ activate_substream(cdev, sub);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- deactivate_substream(dev, sub);
+ deactivate_substream(cdev, sub);
break;
default:
return -EINVAL;
@@ -273,22 +315,25 @@ static snd_pcm_uframes_t
snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub)
{
int index = sub->number;
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
snd_pcm_uframes_t ptr;
- spin_lock(&dev->spinlock);
+ spin_lock(&cdev->spinlock);
- if (dev->input_panic || dev->output_panic)
+ if (cdev->input_panic || cdev->output_panic) {
ptr = SNDRV_PCM_POS_XRUN;
+ goto unlock;
+ }
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
ptr = bytes_to_frames(sub->runtime,
- dev->audio_out_buf_pos[index]);
+ cdev->audio_out_buf_pos[index]);
else
ptr = bytes_to_frames(sub->runtime,
- dev->audio_in_buf_pos[index]);
+ cdev->audio_in_buf_pos[index]);
- spin_unlock(&dev->spinlock);
+unlock:
+ spin_unlock(&cdev->spinlock);
return ptr;
}
@@ -301,24 +346,26 @@ static struct snd_pcm_ops snd_usb_caiaq_ops = {
.hw_free = snd_usb_caiaq_pcm_hw_free,
.prepare = snd_usb_caiaq_pcm_prepare,
.trigger = snd_usb_caiaq_pcm_trigger,
- .pointer = snd_usb_caiaq_pcm_pointer
+ .pointer = snd_usb_caiaq_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
};
-static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,
+static void check_for_elapsed_periods(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream **subs)
{
int stream, pb, *cnt;
struct snd_pcm_substream *sub;
- for (stream = 0; stream < dev->n_streams; stream++) {
+ for (stream = 0; stream < cdev->n_streams; stream++) {
sub = subs[stream];
if (!sub)
continue;
pb = snd_pcm_lib_period_bytes(sub);
cnt = (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- &dev->period_out_count[stream] :
- &dev->period_in_count[stream];
+ &cdev->period_out_count[stream] :
+ &cdev->period_in_count[stream];
if (*cnt >= pb) {
snd_pcm_period_elapsed(sub);
@@ -327,7 +374,7 @@ static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,
}
}
-static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev,
+static void read_in_urb_mode0(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
@@ -335,27 +382,27 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev,
struct snd_pcm_substream *sub;
int stream, i;
- if (all_substreams_zero(dev->sub_capture))
+ if (all_substreams_zero(cdev->sub_capture))
return;
for (i = 0; i < iso->actual_length;) {
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_capture[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_capture[stream];
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
- audio_buf[dev->audio_in_buf_pos[stream]++]
+ audio_buf[cdev->audio_in_buf_pos[stream]++]
= usb_buf[i];
- dev->period_in_count[stream]++;
- if (dev->audio_in_buf_pos[stream] == sz)
- dev->audio_in_buf_pos[stream] = 0;
+ cdev->period_in_count[stream]++;
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
}
}
}
}
-static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
+static void read_in_urb_mode2(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
@@ -365,120 +412,252 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
int stream, i;
for (i = 0; i < iso->actual_length;) {
- if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
+ if (i % (cdev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
for (stream = 0;
- stream < dev->n_streams;
+ stream < cdev->n_streams;
stream++, i++) {
- if (dev->first_packet)
+ if (cdev->first_packet)
continue;
- check_byte = MAKE_CHECKBYTE(dev, stream, i);
+ check_byte = MAKE_CHECKBYTE(cdev, stream, i);
if ((usb_buf[i] & 0x3f) != check_byte)
- dev->input_panic = 1;
+ cdev->input_panic = 1;
if (usb_buf[i] & 0x80)
- dev->output_panic = 1;
+ cdev->output_panic = 1;
}
}
- dev->first_packet = 0;
+ cdev->first_packet = 0;
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_capture[stream];
- if (dev->input_panic)
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_capture[stream];
+ if (cdev->input_panic)
usb_buf[i] = 0;
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
- audio_buf[dev->audio_in_buf_pos[stream]++] =
+ audio_buf[cdev->audio_in_buf_pos[stream]++] =
usb_buf[i];
- dev->period_in_count[stream]++;
- if (dev->audio_in_buf_pos[stream] == sz)
- dev->audio_in_buf_pos[stream] = 0;
+ cdev->period_in_count[stream]++;
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
+ }
+ }
+ }
+}
+
+static void read_in_urb_mode3(struct snd_usb_caiaqdev *cdev,
+ const struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+ struct device *dev = caiaqdev_to_dev(cdev);
+ int stream, i;
+
+ /* paranoia check */
+ if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM))
+ return;
+
+ for (i = 0; i < iso->actual_length;) {
+ for (stream = 0; stream < cdev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = cdev->sub_capture[stream];
+ char *audio_buf = NULL;
+ int c, n, sz = 0;
+
+ if (sub && !cdev->input_panic) {
+ struct snd_pcm_runtime *rt = sub->runtime;
+ audio_buf = rt->dma_area;
+ sz = frames_to_bytes(rt, rt->buffer_size);
+ }
+
+ for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+ /* 3 audio data bytes, followed by 1 check byte */
+ if (audio_buf) {
+ for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+ audio_buf[cdev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
+
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
+ }
+
+ cdev->period_in_count[stream] += BYTES_PER_SAMPLE;
+ }
+
+ i += BYTES_PER_SAMPLE;
+
+ if (usb_buf[i] != ((stream << 1) | c) &&
+ !cdev->first_packet) {
+ if (!cdev->input_panic)
+ dev_warn(dev, " EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
+ ((stream << 1) | c), usb_buf[i], c, stream, i);
+ cdev->input_panic = 1;
+ }
+
+ i++;
}
}
}
+
+ if (cdev->first_packet > 0)
+ cdev->first_packet--;
}
-static void read_in_urb(struct snd_usb_caiaqdev *dev,
+static void read_in_urb(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
- if (!dev->streaming)
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ if (!cdev->streaming)
return;
- if (iso->actual_length < dev->bpp)
+ if (iso->actual_length < cdev->bpp)
return;
- switch (dev->spec.data_alignment) {
+ switch (cdev->spec.data_alignment) {
case 0:
- read_in_urb_mode0(dev, urb, iso);
+ read_in_urb_mode0(cdev, urb, iso);
break;
case 2:
- read_in_urb_mode2(dev, urb, iso);
+ read_in_urb_mode2(cdev, urb, iso);
+ break;
+ case 3:
+ read_in_urb_mode3(cdev, urb, iso);
break;
}
- if ((dev->input_panic || dev->output_panic) && !dev->warned) {
- debug("streaming error detected %s %s\n",
- dev->input_panic ? "(input)" : "",
- dev->output_panic ? "(output)" : "");
- dev->warned = 1;
+ if ((cdev->input_panic || cdev->output_panic) && !cdev->warned) {
+ dev_warn(dev, "streaming error detected %s %s\n",
+ cdev->input_panic ? "(input)" : "",
+ cdev->output_panic ? "(output)" : "");
+ cdev->warned = 1;
}
}
-static void fill_out_urb(struct snd_usb_caiaqdev *dev,
- struct urb *urb,
- const struct usb_iso_packet_descriptor *iso)
+static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *cdev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
struct snd_pcm_substream *sub;
int stream, i;
for (i = 0; i < iso->length;) {
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_playback[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_playback[stream];
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
usb_buf[i] =
- audio_buf[dev->audio_out_buf_pos[stream]];
- dev->period_out_count[stream]++;
- dev->audio_out_buf_pos[stream]++;
- if (dev->audio_out_buf_pos[stream] == sz)
- dev->audio_out_buf_pos[stream] = 0;
+ audio_buf[cdev->audio_out_buf_pos[stream]];
+ cdev->period_out_count[stream]++;
+ cdev->audio_out_buf_pos[stream]++;
+ if (cdev->audio_out_buf_pos[stream] == sz)
+ cdev->audio_out_buf_pos[stream] = 0;
} else
usb_buf[i] = 0;
}
/* fill in the check bytes */
- if (dev->spec.data_alignment == 2 &&
- i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
- (dev->n_streams * CHANNELS_PER_STREAM))
- for (stream = 0; stream < dev->n_streams; stream++, i++)
- usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+ if (cdev->spec.data_alignment == 2 &&
+ i % (cdev->n_streams * BYTES_PER_SAMPLE_USB) ==
+ (cdev->n_streams * CHANNELS_PER_STREAM))
+ for (stream = 0; stream < cdev->n_streams; stream++, i++)
+ usb_buf[i] = MAKE_CHECKBYTE(cdev, stream, i);
+ }
+}
+
+static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *cdev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+ int stream, i;
+
+ for (i = 0; i < iso->length;) {
+ for (stream = 0; stream < cdev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = cdev->sub_playback[stream];
+ char *audio_buf = NULL;
+ int c, n, sz = 0;
+
+ if (sub) {
+ struct snd_pcm_runtime *rt = sub->runtime;
+ audio_buf = rt->dma_area;
+ sz = frames_to_bytes(rt, rt->buffer_size);
+ }
+
+ for (c = 0; c < CHANNELS_PER_STREAM; c++) {
+ for (n = 0; n < BYTES_PER_SAMPLE; n++) {
+ if (audio_buf) {
+ usb_buf[i+n] = audio_buf[cdev->audio_out_buf_pos[stream]++];
+
+ if (cdev->audio_out_buf_pos[stream] == sz)
+ cdev->audio_out_buf_pos[stream] = 0;
+ } else {
+ usb_buf[i+n] = 0;
+ }
+ }
+
+ if (audio_buf)
+ cdev->period_out_count[stream] += BYTES_PER_SAMPLE;
+
+ i += BYTES_PER_SAMPLE;
+
+ /* fill in the check byte pattern */
+ usb_buf[i++] = (stream << 1) | c;
+ }
+ }
+ }
+}
+
+static inline void fill_out_urb(struct snd_usb_caiaqdev *cdev,
+ struct urb *urb,
+ const struct usb_iso_packet_descriptor *iso)
+{
+ switch (cdev->spec.data_alignment) {
+ case 0:
+ case 2:
+ fill_out_urb_mode_0(cdev, urb, iso);
+ break;
+ case 3:
+ fill_out_urb_mode_3(cdev, urb, iso);
+ break;
}
}
static void read_completed(struct urb *urb)
{
struct snd_usb_caiaq_cb_info *info = urb->context;
- struct snd_usb_caiaqdev *dev;
- struct urb *out;
- int frame, len, send_it = 0, outframe = 0;
+ struct snd_usb_caiaqdev *cdev;
+ struct device *dev;
+ struct urb *out = NULL;
+ int i, frame, len, send_it = 0, outframe = 0;
+ size_t offset = 0;
if (urb->status || !info)
return;
- dev = info->dev;
+ cdev = info->cdev;
+ dev = caiaqdev_to_dev(cdev);
- if (!dev->streaming)
+ if (!cdev->streaming)
return;
- out = dev->data_urbs_out[info->index];
+ /* find an unused output urb that is unused */
+ for (i = 0; i < N_URBS; i++)
+ if (test_and_set_bit(i, &cdev->outurb_active_mask) == 0) {
+ out = cdev->data_urbs_out[i];
+ break;
+ }
+
+ if (!out) {
+ dev_err(dev, "Unable to find an output urb to use\n");
+ goto requeue;
+ }
/* read the recently received packet and send back one which has
* the same layout */
@@ -489,15 +668,16 @@ static void read_completed(struct urb *urb)
len = urb->iso_frame_desc[outframe].actual_length;
out->iso_frame_desc[outframe].length = len;
out->iso_frame_desc[outframe].actual_length = 0;
- out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame;
+ out->iso_frame_desc[outframe].offset = offset;
+ offset += len;
if (len > 0) {
- spin_lock(&dev->spinlock);
- fill_out_urb(dev, out, &out->iso_frame_desc[outframe]);
- read_in_urb(dev, urb, &urb->iso_frame_desc[frame]);
- spin_unlock(&dev->spinlock);
- check_for_elapsed_periods(dev, dev->sub_playback);
- check_for_elapsed_periods(dev, dev->sub_capture);
+ spin_lock(&cdev->spinlock);
+ fill_out_urb(cdev, out, &out->iso_frame_desc[outframe]);
+ read_in_urb(cdev, urb, &urb->iso_frame_desc[frame]);
+ spin_unlock(&cdev->spinlock);
+ check_for_elapsed_periods(cdev, cdev->sub_playback);
+ check_for_elapsed_periods(cdev, cdev->sub_capture);
send_it = 1;
}
@@ -505,11 +685,14 @@ static void read_completed(struct urb *urb)
}
if (send_it) {
- out->number_of_packets = FRAMES_PER_URB;
- out->transfer_flags = URB_ISO_ASAP;
+ out->number_of_packets = outframe;
usb_submit_urb(out, GFP_ATOMIC);
+ } else {
+ struct snd_usb_caiaq_cb_info *oinfo = out->context;
+ clear_bit(oinfo->index, &cdev->outurb_active_mask);
}
+requeue:
/* re-submit inbound urb */
for (frame = 0; frame < FRAMES_PER_URB; frame++) {
urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame;
@@ -518,26 +701,28 @@ static void read_completed(struct urb *urb)
}
urb->number_of_packets = FRAMES_PER_URB;
- urb->transfer_flags = URB_ISO_ASAP;
usb_submit_urb(urb, GFP_ATOMIC);
}
static void write_completed(struct urb *urb)
{
struct snd_usb_caiaq_cb_info *info = urb->context;
- struct snd_usb_caiaqdev *dev = info->dev;
+ struct snd_usb_caiaqdev *cdev = info->cdev;
- if (!dev->output_running) {
- dev->output_running = 1;
- wake_up(&dev->prepare_wait_queue);
+ if (!cdev->output_running) {
+ cdev->output_running = 1;
+ wake_up(&cdev->prepare_wait_queue);
}
+
+ clear_bit(info->index, &cdev->outurb_active_mask);
}
-static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
+static struct urb **alloc_urbs(struct snd_usb_caiaqdev *cdev, int dir, int *ret)
{
int i, frame;
struct urb **urbs;
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
+ struct device *dev = caiaqdev_to_dev(cdev);
unsigned int pipe;
pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ?
@@ -546,7 +731,7 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
urbs = kmalloc(N_URBS * sizeof(*urbs), GFP_KERNEL);
if (!urbs) {
- log("unable to kmalloc() urbs, OOM!?\n");
+ dev_err(dev, "unable to kmalloc() urbs, OOM!?\n");
*ret = -ENOMEM;
return NULL;
}
@@ -554,7 +739,7 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
for (i = 0; i < N_URBS; i++) {
urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL);
if (!urbs[i]) {
- log("unable to usb_alloc_urb(), OOM!?\n");
+ dev_err(dev, "unable to usb_alloc_urb(), OOM!?\n");
*ret = -ENOMEM;
return urbs;
}
@@ -562,7 +747,7 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
urbs[i]->transfer_buffer =
kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL);
if (!urbs[i]->transfer_buffer) {
- log("unable to kmalloc() transfer buffer, OOM!?\n");
+ dev_err(dev, "unable to kmalloc() transfer buffer, OOM!?\n");
*ret = -ENOMEM;
return urbs;
}
@@ -579,9 +764,8 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
urbs[i]->pipe = pipe;
urbs[i]->transfer_buffer_length = FRAMES_PER_URB
* BYTES_PER_FRAME;
- urbs[i]->context = &dev->data_cb_info[i];
+ urbs[i]->context = &cdev->data_cb_info[i];
urbs[i]->interval = 1;
- urbs[i]->transfer_flags = URB_ISO_ASAP;
urbs[i]->number_of_packets = FRAMES_PER_URB;
urbs[i]->complete = (dir == SNDRV_PCM_STREAM_CAPTURE) ?
read_completed : write_completed;
@@ -610,106 +794,108 @@ static void free_urbs(struct urb **urbs)
kfree(urbs);
}
-int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
{
int i, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- dev->n_audio_in = max(dev->spec.num_analog_audio_in,
- dev->spec.num_digital_audio_in) /
+ cdev->n_audio_in = max(cdev->spec.num_analog_audio_in,
+ cdev->spec.num_digital_audio_in) /
CHANNELS_PER_STREAM;
- dev->n_audio_out = max(dev->spec.num_analog_audio_out,
- dev->spec.num_digital_audio_out) /
+ cdev->n_audio_out = max(cdev->spec.num_analog_audio_out,
+ cdev->spec.num_digital_audio_out) /
CHANNELS_PER_STREAM;
- dev->n_streams = max(dev->n_audio_in, dev->n_audio_out);
+ cdev->n_streams = max(cdev->n_audio_in, cdev->n_audio_out);
- debug("dev->n_audio_in = %d\n", dev->n_audio_in);
- debug("dev->n_audio_out = %d\n", dev->n_audio_out);
- debug("dev->n_streams = %d\n", dev->n_streams);
+ dev_dbg(dev, "cdev->n_audio_in = %d\n", cdev->n_audio_in);
+ dev_dbg(dev, "cdev->n_audio_out = %d\n", cdev->n_audio_out);
+ dev_dbg(dev, "cdev->n_streams = %d\n", cdev->n_streams);
- if (dev->n_streams > MAX_STREAMS) {
- log("unable to initialize device, too many streams.\n");
+ if (cdev->n_streams > MAX_STREAMS) {
+ dev_err(dev, "unable to initialize device, too many streams.\n");
return -EINVAL;
}
- ret = snd_pcm_new(dev->chip.card, dev->product_name, 0,
- dev->n_audio_out, dev->n_audio_in, &dev->pcm);
+ ret = snd_pcm_new(cdev->chip.card, cdev->product_name, 0,
+ cdev->n_audio_out, cdev->n_audio_in, &cdev->pcm);
if (ret < 0) {
- log("snd_pcm_new() returned %d\n", ret);
+ dev_err(dev, "snd_pcm_new() returned %d\n", ret);
return ret;
}
- dev->pcm->private_data = dev;
- strcpy(dev->pcm->name, dev->product_name);
+ cdev->pcm->private_data = cdev;
+ strlcpy(cdev->pcm->name, cdev->product_name, sizeof(cdev->pcm->name));
- memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
- memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
+ memset(cdev->sub_playback, 0, sizeof(cdev->sub_playback));
+ memset(cdev->sub_capture, 0, sizeof(cdev->sub_capture));
- memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware,
+ memcpy(&cdev->pcm_info, &snd_usb_caiaq_pcm_hardware,
sizeof(snd_usb_caiaq_pcm_hardware));
/* setup samplerates */
- dev->samplerates = dev->pcm_info.rates;
- switch (dev->chip.usb_id) {
+ cdev->samplerates = cdev->pcm_info.rates;
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE):
- dev->samplerates |= SNDRV_PCM_RATE_192000;
+ cdev->samplerates |= SNDRV_PCM_RATE_192000;
/* fall thru */
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO2DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
- dev->samplerates |= SNDRV_PCM_RATE_88200;
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORAUDIO2):
+ cdev->samplerates |= SNDRV_PCM_RATE_88200;
break;
}
- snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_usb_caiaq_ops);
- snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
+ snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_usb_caiaq_ops);
- snd_pcm_lib_preallocate_pages_for_all(dev->pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
-
- dev->data_cb_info =
+ cdev->data_cb_info =
kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS,
GFP_KERNEL);
- if (!dev->data_cb_info)
+ if (!cdev->data_cb_info)
return -ENOMEM;
+ cdev->outurb_active_mask = 0;
+ BUILD_BUG_ON(N_URBS > (sizeof(cdev->outurb_active_mask) * 8));
+
for (i = 0; i < N_URBS; i++) {
- dev->data_cb_info[i].dev = dev;
- dev->data_cb_info[i].index = i;
+ cdev->data_cb_info[i].cdev = cdev;
+ cdev->data_cb_info[i].index = i;
}
- dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret);
+ cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret);
if (ret < 0) {
- kfree(dev->data_cb_info);
- free_urbs(dev->data_urbs_in);
+ kfree(cdev->data_cb_info);
+ free_urbs(cdev->data_urbs_in);
return ret;
}
- dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
+ cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
if (ret < 0) {
- kfree(dev->data_cb_info);
- free_urbs(dev->data_urbs_in);
- free_urbs(dev->data_urbs_out);
+ kfree(cdev->data_cb_info);
+ free_urbs(cdev->data_urbs_in);
+ free_urbs(cdev->data_urbs_out);
return ret;
}
return 0;
}
-void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev)
+void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)
{
- debug("%s(%p)\n", __func__, dev);
- stream_stop(dev);
- free_urbs(dev->data_urbs_in);
- free_urbs(dev->data_urbs_out);
- kfree(dev->data_cb_info);
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
+ stream_stop(cdev);
+ free_urbs(cdev->data_urbs_in);
+ free_urbs(cdev->data_urbs_out);
+ kfree(cdev->data_cb_info);
}
diff --git a/sound/usb/caiaq/audio.h b/sound/usb/caiaq/audio.h
index 8ab1f8d9529..bdf155300a8 100644
--- a/sound/usb/caiaq/audio.h
+++ b/sound/usb/caiaq/audio.h
@@ -1,7 +1,7 @@
#ifndef CAIAQ_AUDIO_H
#define CAIAQ_AUDIO_H
-int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev);
+int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev);
#endif /* CAIAQ_AUDIO_H */
diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c
index 91c804cd278..f65fc0987cf 100644
--- a/sound/usb/caiaq/control.c
+++ b/sound/usb/caiaq/control.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <sound/control.h>
@@ -27,12 +28,13 @@
#include "control.h"
#define CNT_INTVAL 0x10000
+#define MASCHINE_BANK_SIZE 32
static int control_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
int is_intval = pos & CNT_INTVAL;
int maxval = 63;
@@ -40,7 +42,7 @@ static int control_info(struct snd_kcontrol *kcontrol,
uinfo->count = 1;
pos &= ~CNT_INTVAL;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
if (pos == 0) {
@@ -55,6 +57,10 @@ static int control_info(struct snd_kcontrol *kcontrol,
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
maxval = 127;
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ maxval = 31;
+ break;
}
if (is_intval) {
@@ -74,15 +80,15 @@ static int control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
if (pos & CNT_INTVAL)
ucontrol->value.integer.value[0]
- = dev->control_state[pos & ~CNT_INTVAL];
+ = cdev->control_state[pos & ~CNT_INTVAL];
else
ucontrol->value.integer.value[0]
- = !!(dev->control_state[pos / 8] & (1 << pos % 8));
+ = !!(cdev->control_state[pos / 8] & (1 << pos % 8));
return 0;
}
@@ -91,33 +97,67 @@ static int control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
+ int v = ucontrol->value.integer.value[0];
unsigned char cmd = EP1_CMD_WRITE_IO;
- if (dev->chip.usb_id ==
+ if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1))
cmd = EP1_CMD_DIMM_LEDS;
+ if (cdev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER))
+ cmd = EP1_CMD_DIMM_LEDS;
+
if (pos & CNT_INTVAL) {
- dev->control_state[pos & ~CNT_INTVAL]
- = ucontrol->value.integer.value[0];
- snd_usb_caiaq_send_command(dev, cmd,
- dev->control_state, sizeof(dev->control_state));
+ int i = pos & ~CNT_INTVAL;
+
+ cdev->control_state[i] = v;
+
+ if (cdev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4)) {
+ int actual_len;
+
+ cdev->ep8_out_buf[0] = i;
+ cdev->ep8_out_buf[1] = v;
+
+ usb_bulk_msg(cdev->chip.dev,
+ usb_sndbulkpipe(cdev->chip.dev, 8),
+ cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf),
+ &actual_len, 200);
+ } else if (cdev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) {
+
+ int bank = 0;
+ int offset = 0;
+
+ if (i >= MASCHINE_BANK_SIZE) {
+ bank = 0x1e;
+ offset = MASCHINE_BANK_SIZE;
+ }
+
+ snd_usb_caiaq_send_command_bank(cdev, cmd, bank,
+ cdev->control_state + offset,
+ MASCHINE_BANK_SIZE);
+ } else {
+ snd_usb_caiaq_send_command(cdev, cmd,
+ cdev->control_state, sizeof(cdev->control_state));
+ }
} else {
- if (ucontrol->value.integer.value[0])
- dev->control_state[pos / 8] |= 1 << (pos % 8);
+ if (v)
+ cdev->control_state[pos / 8] |= 1 << (pos % 8);
else
- dev->control_state[pos / 8] &= ~(1 << (pos % 8));
+ cdev->control_state[pos / 8] &= ~(1 << (pos % 8));
- snd_usb_caiaq_send_command(dev, cmd,
- dev->control_state, sizeof(dev->control_state));
+ snd_usb_caiaq_send_command(cdev, cmd,
+ cdev->control_state, sizeof(cdev->control_state));
}
return 1;
}
-static struct snd_kcontrol_new kcontrol_template __devinitdata = {
+static struct snd_kcontrol_new kcontrol_template = {
.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.index = 0,
@@ -296,8 +336,249 @@ static struct caiaq_controller kontrolx1_controller[] = {
{ "LED Deck B: SYNC", 8 | CNT_INTVAL },
};
-static int __devinit add_controls(struct caiaq_controller *c, int num,
- struct snd_usb_caiaqdev *dev)
+static struct caiaq_controller kontrols4_controller[] = {
+ { "LED: Master: Quant", 10 | CNT_INTVAL },
+ { "LED: Master: Headphone", 11 | CNT_INTVAL },
+ { "LED: Master: Master", 12 | CNT_INTVAL },
+ { "LED: Master: Snap", 14 | CNT_INTVAL },
+ { "LED: Master: Warning", 15 | CNT_INTVAL },
+ { "LED: Master: Master button", 112 | CNT_INTVAL },
+ { "LED: Master: Snap button", 113 | CNT_INTVAL },
+ { "LED: Master: Rec", 118 | CNT_INTVAL },
+ { "LED: Master: Size", 119 | CNT_INTVAL },
+ { "LED: Master: Quant button", 120 | CNT_INTVAL },
+ { "LED: Master: Browser button", 121 | CNT_INTVAL },
+ { "LED: Master: Play button", 126 | CNT_INTVAL },
+ { "LED: Master: Undo button", 127 | CNT_INTVAL },
+
+ { "LED: Channel A: >", 4 | CNT_INTVAL },
+ { "LED: Channel A: <", 5 | CNT_INTVAL },
+ { "LED: Channel A: Meter 1", 97 | CNT_INTVAL },
+ { "LED: Channel A: Meter 2", 98 | CNT_INTVAL },
+ { "LED: Channel A: Meter 3", 99 | CNT_INTVAL },
+ { "LED: Channel A: Meter 4", 100 | CNT_INTVAL },
+ { "LED: Channel A: Meter 5", 101 | CNT_INTVAL },
+ { "LED: Channel A: Meter 6", 102 | CNT_INTVAL },
+ { "LED: Channel A: Meter clip", 103 | CNT_INTVAL },
+ { "LED: Channel A: Active", 114 | CNT_INTVAL },
+ { "LED: Channel A: Cue", 116 | CNT_INTVAL },
+ { "LED: Channel A: FX1", 149 | CNT_INTVAL },
+ { "LED: Channel A: FX2", 148 | CNT_INTVAL },
+
+ { "LED: Channel B: >", 2 | CNT_INTVAL },
+ { "LED: Channel B: <", 3 | CNT_INTVAL },
+ { "LED: Channel B: Meter 1", 89 | CNT_INTVAL },
+ { "LED: Channel B: Meter 2", 90 | CNT_INTVAL },
+ { "LED: Channel B: Meter 3", 91 | CNT_INTVAL },
+ { "LED: Channel B: Meter 4", 92 | CNT_INTVAL },
+ { "LED: Channel B: Meter 5", 93 | CNT_INTVAL },
+ { "LED: Channel B: Meter 6", 94 | CNT_INTVAL },
+ { "LED: Channel B: Meter clip", 95 | CNT_INTVAL },
+ { "LED: Channel B: Active", 122 | CNT_INTVAL },
+ { "LED: Channel B: Cue", 125 | CNT_INTVAL },
+ { "LED: Channel B: FX1", 147 | CNT_INTVAL },
+ { "LED: Channel B: FX2", 146 | CNT_INTVAL },
+
+ { "LED: Channel C: >", 6 | CNT_INTVAL },
+ { "LED: Channel C: <", 7 | CNT_INTVAL },
+ { "LED: Channel C: Meter 1", 105 | CNT_INTVAL },
+ { "LED: Channel C: Meter 2", 106 | CNT_INTVAL },
+ { "LED: Channel C: Meter 3", 107 | CNT_INTVAL },
+ { "LED: Channel C: Meter 4", 108 | CNT_INTVAL },
+ { "LED: Channel C: Meter 5", 109 | CNT_INTVAL },
+ { "LED: Channel C: Meter 6", 110 | CNT_INTVAL },
+ { "LED: Channel C: Meter clip", 111 | CNT_INTVAL },
+ { "LED: Channel C: Active", 115 | CNT_INTVAL },
+ { "LED: Channel C: Cue", 117 | CNT_INTVAL },
+ { "LED: Channel C: FX1", 151 | CNT_INTVAL },
+ { "LED: Channel C: FX2", 150 | CNT_INTVAL },
+
+ { "LED: Channel D: >", 0 | CNT_INTVAL },
+ { "LED: Channel D: <", 1 | CNT_INTVAL },
+ { "LED: Channel D: Meter 1", 81 | CNT_INTVAL },
+ { "LED: Channel D: Meter 2", 82 | CNT_INTVAL },
+ { "LED: Channel D: Meter 3", 83 | CNT_INTVAL },
+ { "LED: Channel D: Meter 4", 84 | CNT_INTVAL },
+ { "LED: Channel D: Meter 5", 85 | CNT_INTVAL },
+ { "LED: Channel D: Meter 6", 86 | CNT_INTVAL },
+ { "LED: Channel D: Meter clip", 87 | CNT_INTVAL },
+ { "LED: Channel D: Active", 123 | CNT_INTVAL },
+ { "LED: Channel D: Cue", 124 | CNT_INTVAL },
+ { "LED: Channel D: FX1", 145 | CNT_INTVAL },
+ { "LED: Channel D: FX2", 144 | CNT_INTVAL },
+
+ { "LED: Deck A: 1 (blue)", 22 | CNT_INTVAL },
+ { "LED: Deck A: 1 (green)", 23 | CNT_INTVAL },
+ { "LED: Deck A: 2 (blue)", 20 | CNT_INTVAL },
+ { "LED: Deck A: 2 (green)", 21 | CNT_INTVAL },
+ { "LED: Deck A: 3 (blue)", 18 | CNT_INTVAL },
+ { "LED: Deck A: 3 (green)", 19 | CNT_INTVAL },
+ { "LED: Deck A: 4 (blue)", 16 | CNT_INTVAL },
+ { "LED: Deck A: 4 (green)", 17 | CNT_INTVAL },
+ { "LED: Deck A: Load", 44 | CNT_INTVAL },
+ { "LED: Deck A: Deck C button", 45 | CNT_INTVAL },
+ { "LED: Deck A: In", 47 | CNT_INTVAL },
+ { "LED: Deck A: Out", 46 | CNT_INTVAL },
+ { "LED: Deck A: Shift", 24 | CNT_INTVAL },
+ { "LED: Deck A: Sync", 27 | CNT_INTVAL },
+ { "LED: Deck A: Cue", 26 | CNT_INTVAL },
+ { "LED: Deck A: Play", 25 | CNT_INTVAL },
+ { "LED: Deck A: Tempo up", 33 | CNT_INTVAL },
+ { "LED: Deck A: Tempo down", 32 | CNT_INTVAL },
+ { "LED: Deck A: Master", 34 | CNT_INTVAL },
+ { "LED: Deck A: Keylock", 35 | CNT_INTVAL },
+ { "LED: Deck A: Deck A", 37 | CNT_INTVAL },
+ { "LED: Deck A: Deck C", 36 | CNT_INTVAL },
+ { "LED: Deck A: Samples", 38 | CNT_INTVAL },
+ { "LED: Deck A: On Air", 39 | CNT_INTVAL },
+ { "LED: Deck A: Sample 1", 31 | CNT_INTVAL },
+ { "LED: Deck A: Sample 2", 30 | CNT_INTVAL },
+ { "LED: Deck A: Sample 3", 29 | CNT_INTVAL },
+ { "LED: Deck A: Sample 4", 28 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - A", 55 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - B", 54 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - C", 53 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - D", 52 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - E", 51 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - F", 50 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - G", 49 | CNT_INTVAL },
+ { "LED: Deck A: Digit 1 - dot", 48 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - A", 63 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - B", 62 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - C", 61 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - D", 60 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - E", 59 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - F", 58 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - G", 57 | CNT_INTVAL },
+ { "LED: Deck A: Digit 2 - dot", 56 | CNT_INTVAL },
+
+ { "LED: Deck B: 1 (blue)", 78 | CNT_INTVAL },
+ { "LED: Deck B: 1 (green)", 79 | CNT_INTVAL },
+ { "LED: Deck B: 2 (blue)", 76 | CNT_INTVAL },
+ { "LED: Deck B: 2 (green)", 77 | CNT_INTVAL },
+ { "LED: Deck B: 3 (blue)", 74 | CNT_INTVAL },
+ { "LED: Deck B: 3 (green)", 75 | CNT_INTVAL },
+ { "LED: Deck B: 4 (blue)", 72 | CNT_INTVAL },
+ { "LED: Deck B: 4 (green)", 73 | CNT_INTVAL },
+ { "LED: Deck B: Load", 180 | CNT_INTVAL },
+ { "LED: Deck B: Deck D button", 181 | CNT_INTVAL },
+ { "LED: Deck B: In", 183 | CNT_INTVAL },
+ { "LED: Deck B: Out", 182 | CNT_INTVAL },
+ { "LED: Deck B: Shift", 64 | CNT_INTVAL },
+ { "LED: Deck B: Sync", 67 | CNT_INTVAL },
+ { "LED: Deck B: Cue", 66 | CNT_INTVAL },
+ { "LED: Deck B: Play", 65 | CNT_INTVAL },
+ { "LED: Deck B: Tempo up", 185 | CNT_INTVAL },
+ { "LED: Deck B: Tempo down", 184 | CNT_INTVAL },
+ { "LED: Deck B: Master", 186 | CNT_INTVAL },
+ { "LED: Deck B: Keylock", 187 | CNT_INTVAL },
+ { "LED: Deck B: Deck B", 189 | CNT_INTVAL },
+ { "LED: Deck B: Deck D", 188 | CNT_INTVAL },
+ { "LED: Deck B: Samples", 190 | CNT_INTVAL },
+ { "LED: Deck B: On Air", 191 | CNT_INTVAL },
+ { "LED: Deck B: Sample 1", 71 | CNT_INTVAL },
+ { "LED: Deck B: Sample 2", 70 | CNT_INTVAL },
+ { "LED: Deck B: Sample 3", 69 | CNT_INTVAL },
+ { "LED: Deck B: Sample 4", 68 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - A", 175 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - B", 174 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - C", 173 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - D", 172 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - E", 171 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - F", 170 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - G", 169 | CNT_INTVAL },
+ { "LED: Deck B: Digit 1 - dot", 168 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - A", 167 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - B", 166 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - C", 165 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - D", 164 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - E", 163 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - F", 162 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - G", 161 | CNT_INTVAL },
+ { "LED: Deck B: Digit 2 - dot", 160 | CNT_INTVAL },
+
+ { "LED: FX1: dry/wet", 153 | CNT_INTVAL },
+ { "LED: FX1: 1", 154 | CNT_INTVAL },
+ { "LED: FX1: 2", 155 | CNT_INTVAL },
+ { "LED: FX1: 3", 156 | CNT_INTVAL },
+ { "LED: FX1: Mode", 157 | CNT_INTVAL },
+ { "LED: FX2: dry/wet", 129 | CNT_INTVAL },
+ { "LED: FX2: 1", 130 | CNT_INTVAL },
+ { "LED: FX2: 2", 131 | CNT_INTVAL },
+ { "LED: FX2: 3", 132 | CNT_INTVAL },
+ { "LED: FX2: Mode", 133 | CNT_INTVAL },
+};
+
+static struct caiaq_controller maschine_controller[] = {
+ { "LED: Pad 1", 3 | CNT_INTVAL },
+ { "LED: Pad 2", 2 | CNT_INTVAL },
+ { "LED: Pad 3", 1 | CNT_INTVAL },
+ { "LED: Pad 4", 0 | CNT_INTVAL },
+ { "LED: Pad 5", 7 | CNT_INTVAL },
+ { "LED: Pad 6", 6 | CNT_INTVAL },
+ { "LED: Pad 7", 5 | CNT_INTVAL },
+ { "LED: Pad 8", 4 | CNT_INTVAL },
+ { "LED: Pad 9", 11 | CNT_INTVAL },
+ { "LED: Pad 10", 10 | CNT_INTVAL },
+ { "LED: Pad 11", 9 | CNT_INTVAL },
+ { "LED: Pad 12", 8 | CNT_INTVAL },
+ { "LED: Pad 13", 15 | CNT_INTVAL },
+ { "LED: Pad 14", 14 | CNT_INTVAL },
+ { "LED: Pad 15", 13 | CNT_INTVAL },
+ { "LED: Pad 16", 12 | CNT_INTVAL },
+
+ { "LED: Mute", 16 | CNT_INTVAL },
+ { "LED: Solo", 17 | CNT_INTVAL },
+ { "LED: Select", 18 | CNT_INTVAL },
+ { "LED: Duplicate", 19 | CNT_INTVAL },
+ { "LED: Navigate", 20 | CNT_INTVAL },
+ { "LED: Pad Mode", 21 | CNT_INTVAL },
+ { "LED: Pattern", 22 | CNT_INTVAL },
+ { "LED: Scene", 23 | CNT_INTVAL },
+
+ { "LED: Shift", 24 | CNT_INTVAL },
+ { "LED: Erase", 25 | CNT_INTVAL },
+ { "LED: Grid", 26 | CNT_INTVAL },
+ { "LED: Right Bottom", 27 | CNT_INTVAL },
+ { "LED: Rec", 28 | CNT_INTVAL },
+ { "LED: Play", 29 | CNT_INTVAL },
+ { "LED: Left Bottom", 32 | CNT_INTVAL },
+ { "LED: Restart", 33 | CNT_INTVAL },
+
+ { "LED: Group A", 41 | CNT_INTVAL },
+ { "LED: Group B", 40 | CNT_INTVAL },
+ { "LED: Group C", 37 | CNT_INTVAL },
+ { "LED: Group D", 36 | CNT_INTVAL },
+ { "LED: Group E", 39 | CNT_INTVAL },
+ { "LED: Group F", 38 | CNT_INTVAL },
+ { "LED: Group G", 35 | CNT_INTVAL },
+ { "LED: Group H", 34 | CNT_INTVAL },
+
+ { "LED: Auto Write", 42 | CNT_INTVAL },
+ { "LED: Snap", 43 | CNT_INTVAL },
+ { "LED: Right Top", 44 | CNT_INTVAL },
+ { "LED: Left Top", 45 | CNT_INTVAL },
+ { "LED: Sampling", 46 | CNT_INTVAL },
+ { "LED: Browse", 47 | CNT_INTVAL },
+ { "LED: Step", 48 | CNT_INTVAL },
+ { "LED: Control", 49 | CNT_INTVAL },
+
+ { "LED: Top Button 1", 57 | CNT_INTVAL },
+ { "LED: Top Button 2", 56 | CNT_INTVAL },
+ { "LED: Top Button 3", 55 | CNT_INTVAL },
+ { "LED: Top Button 4", 54 | CNT_INTVAL },
+ { "LED: Top Button 5", 53 | CNT_INTVAL },
+ { "LED: Top Button 6", 52 | CNT_INTVAL },
+ { "LED: Top Button 7", 51 | CNT_INTVAL },
+ { "LED: Top Button 8", 50 | CNT_INTVAL },
+
+ { "LED: Note Repeat", 58 | CNT_INTVAL },
+
+ { "Backlight Display", 59 | CNT_INTVAL }
+};
+
+static int add_controls(struct caiaq_controller *c, int num,
+ struct snd_usb_caiaqdev *cdev)
{
int i, ret;
struct snd_kcontrol *kc;
@@ -305,8 +586,8 @@ static int __devinit add_controls(struct caiaq_controller *c, int num,
for (i = 0; i < num; i++, c++) {
kcontrol_template.name = c->name;
kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- ret = snd_ctl_add(dev->chip.card, kc);
+ kc = snd_ctl_new1(&kcontrol_template, cdev);
+ ret = snd_ctl_add(cdev->chip.card, kc);
if (ret < 0)
return ret;
}
@@ -314,45 +595,55 @@ static int __devinit add_controls(struct caiaq_controller *c, int num,
return 0;
}
-int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev)
{
int ret = 0;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
ret = add_controls(ak1_controller,
- ARRAY_SIZE(ak1_controller), dev);
+ ARRAY_SIZE(ak1_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
ret = add_controls(rk2_controller,
- ARRAY_SIZE(rk2_controller), dev);
+ ARRAY_SIZE(rk2_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
ret = add_controls(rk3_controller,
- ARRAY_SIZE(rk3_controller), dev);
+ ARRAY_SIZE(rk3_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
ret = add_controls(kore_controller,
- ARRAY_SIZE(kore_controller), dev);
+ ARRAY_SIZE(kore_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
ret = add_controls(a8dj_controller,
- ARRAY_SIZE(a8dj_controller), dev);
+ ARRAY_SIZE(a8dj_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
ret = add_controls(a4dj_controller,
- ARRAY_SIZE(a4dj_controller), dev);
+ ARRAY_SIZE(a4dj_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
ret = add_controls(kontrolx1_controller,
- ARRAY_SIZE(kontrolx1_controller), dev);
+ ARRAY_SIZE(kontrolx1_controller), cdev);
+ break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ ret = add_controls(kontrols4_controller,
+ ARRAY_SIZE(kontrols4_controller), cdev);
+ break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ ret = add_controls(maschine_controller,
+ ARRAY_SIZE(maschine_controller), cdev);
break;
}
diff --git a/sound/usb/caiaq/control.h b/sound/usb/caiaq/control.h
index 2e7ab1aa4fb..501c4883aef 100644
--- a/sound/usb/caiaq/control.h
+++ b/sound/usb/caiaq/control.h
@@ -1,6 +1,6 @@
#ifndef CAIAQ_CONTROL_H
#define CAIAQ_CONTROL_H
-int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev);
+int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev);
#endif /* CAIAQ_CONTROL_H */
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index cdfb856bddd..b871ba407e4 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -20,6 +20,7 @@
*/
#include <linux/moduleparam.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -36,24 +37,26 @@
#include "input.h"
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.21");
+MODULE_DESCRIPTION("caiaq USB audio");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
- "{Native Instruments, RigKontrol3},"
- "{Native Instruments, Kore Controller},"
- "{Native Instruments, Kore Controller 2},"
- "{Native Instruments, Audio Kontrol 1},"
- "{Native Instruments, Audio 2 DJ},"
- "{Native Instruments, Audio 4 DJ},"
- "{Native Instruments, Audio 8 DJ},"
- "{Native Instruments, Session I/O},"
- "{Native Instruments, GuitarRig mobile}"
- "{Native Instruments, Traktor Kontrol X1}");
+MODULE_SUPPORTED_DEVICE("{{Native Instruments,RigKontrol2},"
+ "{Native Instruments,RigKontrol3},"
+ "{Native Instruments,Kore Controller},"
+ "{Native Instruments,Kore Controller 2},"
+ "{Native Instruments,Audio Kontrol 1},"
+ "{Native Instruments,Audio 2 DJ},"
+ "{Native Instruments,Audio 4 DJ},"
+ "{Native Instruments,Audio 8 DJ},"
+ "{Native Instruments,Traktor Audio 2},"
+ "{Native Instruments,Session I/O},"
+ "{Native Instruments,GuitarRig mobile},"
+ "{Native Instruments,Traktor Kontrol X1},"
+ "{Native Instruments,Traktor Kontrol S4},"
+ "{Native Instruments,Maschine Controller}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int snd_card_used[SNDRV_CARDS];
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the caiaq sound device");
@@ -134,73 +137,89 @@ static struct usb_device_id snd_usb_id_table[] = {
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_TRAKTORKONTROLX1
},
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_TRAKTORKONTROLS4
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_TRAKTORAUDIO2
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_MASCHINECONTROLLER
+ },
{ /* terminator */ }
};
static void usb_ep1_command_reply_dispatch (struct urb* urb)
{
int ret;
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct device *dev = &urb->dev->dev;
+ struct snd_usb_caiaqdev *cdev = urb->context;
unsigned char *buf = urb->transfer_buffer;
- if (urb->status || !dev) {
- log("received EP1 urb->status = %i\n", urb->status);
+ if (urb->status || !cdev) {
+ dev_warn(dev, "received EP1 urb->status = %i\n", urb->status);
return;
}
switch(buf[0]) {
case EP1_CMD_GET_DEVICE_INFO:
- memcpy(&dev->spec, buf+1, sizeof(struct caiaq_device_spec));
- dev->spec.fw_version = le16_to_cpu(dev->spec.fw_version);
- debug("device spec (firmware %d): audio: %d in, %d out, "
+ memcpy(&cdev->spec, buf+1, sizeof(struct caiaq_device_spec));
+ cdev->spec.fw_version = le16_to_cpu(cdev->spec.fw_version);
+ dev_dbg(dev, "device spec (firmware %d): audio: %d in, %d out, "
"MIDI: %d in, %d out, data alignment %d\n",
- dev->spec.fw_version,
- dev->spec.num_analog_audio_in,
- dev->spec.num_analog_audio_out,
- dev->spec.num_midi_in,
- dev->spec.num_midi_out,
- dev->spec.data_alignment);
-
- dev->spec_received++;
- wake_up(&dev->ep1_wait_queue);
+ cdev->spec.fw_version,
+ cdev->spec.num_analog_audio_in,
+ cdev->spec.num_analog_audio_out,
+ cdev->spec.num_midi_in,
+ cdev->spec.num_midi_out,
+ cdev->spec.data_alignment);
+
+ cdev->spec_received++;
+ wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_AUDIO_PARAMS:
- dev->audio_parm_answer = buf[1];
- wake_up(&dev->ep1_wait_queue);
+ cdev->audio_parm_answer = buf[1];
+ wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_MIDI_READ:
- snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]);
+ snd_usb_caiaq_midi_handle_input(cdev, buf[1], buf + 3, buf[2]);
break;
case EP1_CMD_READ_IO:
- if (dev->chip.usb_id ==
+ if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
- if (urb->actual_length > sizeof(dev->control_state))
- urb->actual_length = sizeof(dev->control_state);
- memcpy(dev->control_state, buf + 1, urb->actual_length);
- wake_up(&dev->ep1_wait_queue);
+ if (urb->actual_length > sizeof(cdev->control_state))
+ urb->actual_length = sizeof(cdev->control_state);
+ memcpy(cdev->control_state, buf + 1, urb->actual_length);
+ wake_up(&cdev->ep1_wait_queue);
break;
}
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
case EP1_CMD_READ_ERP:
case EP1_CMD_READ_ANALOG:
- snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length);
+ snd_usb_caiaq_input_dispatch(cdev, buf, urb->actual_length);
#endif
break;
}
- dev->ep1_in_urb.actual_length = 0;
- ret = usb_submit_urb(&dev->ep1_in_urb, GFP_ATOMIC);
+ cdev->ep1_in_urb.actual_length = 0;
+ ret = usb_submit_urb(&cdev->ep1_in_urb, GFP_ATOMIC);
if (ret < 0)
- log("unable to submit urb. OOM!?\n");
+ dev_err(dev, "unable to submit urb. OOM!?\n");
}
-int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
unsigned char command,
const unsigned char *buffer,
int len)
{
int actual_len;
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
if (!usb_dev)
return -EIO;
@@ -209,18 +228,44 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
len = EP1_BUFSIZE - 1;
if (buffer && len > 0)
- memcpy(dev->ep1_out_buf+1, buffer, len);
+ memcpy(cdev->ep1_out_buf+1, buffer, len);
+
+ cdev->ep1_out_buf[0] = command;
+ return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
+ cdev->ep1_out_buf, len+1, &actual_len, 200);
+}
+
+int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev,
+ unsigned char command,
+ unsigned char bank,
+ const unsigned char *buffer,
+ int len)
+{
+ int actual_len;
+ struct usb_device *usb_dev = cdev->chip.dev;
+
+ if (!usb_dev)
+ return -EIO;
+
+ if (len > EP1_BUFSIZE - 2)
+ len = EP1_BUFSIZE - 2;
+
+ if (buffer && len > 0)
+ memcpy(cdev->ep1_out_buf+2, buffer, len);
+
+ cdev->ep1_out_buf[0] = command;
+ cdev->ep1_out_buf[1] = bank;
- dev->ep1_out_buf[0] = command;
return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
- dev->ep1_out_buf, len+1, &actual_len, 200);
+ cdev->ep1_out_buf, len+2, &actual_len, 200);
}
-int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev,
int rate, int depth, int bpp)
{
int ret;
char tmp[5];
+ struct device *dev = caiaqdev_to_dev(cdev);
switch (rate) {
case 44100: tmp[0] = SAMPLERATE_44100; break;
@@ -241,49 +286,50 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
tmp[3] = bpp >> 8;
tmp[4] = 1; /* packets per microframe */
- debug("setting audio params: %d Hz, %d bits, %d bpp\n",
+ dev_dbg(dev, "setting audio params: %d Hz, %d bits, %d bpp\n",
rate, depth, bpp);
- dev->audio_parm_answer = -1;
- ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS,
+ cdev->audio_parm_answer = -1;
+ ret = snd_usb_caiaq_send_command(cdev, EP1_CMD_AUDIO_PARAMS,
tmp, sizeof(tmp));
if (ret)
return ret;
- if (!wait_event_timeout(dev->ep1_wait_queue,
- dev->audio_parm_answer >= 0, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue,
+ cdev->audio_parm_answer >= 0, HZ))
return -EPIPE;
- if (dev->audio_parm_answer != 1)
- debug("unable to set the device's audio params\n");
+ if (cdev->audio_parm_answer != 1)
+ dev_dbg(dev, "unable to set the device's audio params\n");
else
- dev->bpp = bpp;
+ cdev->bpp = bpp;
- return dev->audio_parm_answer == 1 ? 0 : -EINVAL;
+ return cdev->audio_parm_answer == 1 ? 0 : -EINVAL;
}
-int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *cdev,
int digital, int analog, int erp)
{
char tmp[3] = { digital, analog, erp };
- return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
+ return snd_usb_caiaq_send_command(cdev, EP1_CMD_AUTO_MSG,
tmp, sizeof(tmp));
}
-static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
+static void setup_card(struct snd_usb_caiaqdev *cdev)
{
int ret;
char val[4];
+ struct device *dev = caiaqdev_to_dev(cdev);
/* device-specific startup specials */
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
/* RigKontrol2 - display centered dash ('-') */
val[0] = 0x00;
val[1] = 0x00;
val[2] = 0x01;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 3);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
/* RigKontrol2 - display two centered dashes ('--') */
@@ -291,69 +337,69 @@ static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
val[1] = 0x40;
val[2] = 0x40;
val[3] = 0x00;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 4);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
/* Audio Kontrol 1 - make USB-LED stop blinking */
val[0] = 0x00;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 1);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
/* Audio 8 DJ - trigger read of current settings */
- dev->control_state[0] = 0xff;
- snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0);
- snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0);
+ cdev->control_state[0] = 0xff;
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 0, 0);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_READ_IO, NULL, 0);
- if (!wait_event_timeout(dev->ep1_wait_queue,
- dev->control_state[0] != 0xff, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue,
+ cdev->control_state[0] != 0xff, HZ))
return;
/* fix up some defaults */
- if ((dev->control_state[1] != 2) ||
- (dev->control_state[2] != 3) ||
- (dev->control_state[4] != 2)) {
- dev->control_state[1] = 2;
- dev->control_state[2] = 3;
- dev->control_state[4] = 2;
- snd_usb_caiaq_send_command(dev,
- EP1_CMD_WRITE_IO, dev->control_state, 6);
+ if ((cdev->control_state[1] != 2) ||
+ (cdev->control_state[2] != 3) ||
+ (cdev->control_state[4] != 2)) {
+ cdev->control_state[1] = 2;
+ cdev->control_state[2] = 3;
+ cdev->control_state[4] = 2;
+ snd_usb_caiaq_send_command(cdev,
+ EP1_CMD_WRITE_IO, cdev->control_state, 6);
}
break;
}
- if (dev->spec.num_analog_audio_out +
- dev->spec.num_analog_audio_in +
- dev->spec.num_digital_audio_out +
- dev->spec.num_digital_audio_in > 0) {
- ret = snd_usb_caiaq_audio_init(dev);
+ if (cdev->spec.num_analog_audio_out +
+ cdev->spec.num_analog_audio_in +
+ cdev->spec.num_digital_audio_out +
+ cdev->spec.num_digital_audio_in > 0) {
+ ret = snd_usb_caiaq_audio_init(cdev);
if (ret < 0)
- log("Unable to set up audio system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up audio system (ret=%d)\n", ret);
}
- if (dev->spec.num_midi_in +
- dev->spec.num_midi_out > 0) {
- ret = snd_usb_caiaq_midi_init(dev);
+ if (cdev->spec.num_midi_in +
+ cdev->spec.num_midi_out > 0) {
+ ret = snd_usb_caiaq_midi_init(cdev);
if (ret < 0)
- log("Unable to set up MIDI system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up MIDI system (ret=%d)\n", ret);
}
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
- ret = snd_usb_caiaq_input_init(dev);
+ ret = snd_usb_caiaq_input_init(cdev);
if (ret < 0)
- log("Unable to set up input system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up input system (ret=%d)\n", ret);
#endif
/* finally, register the card and all its sub-instances */
- ret = snd_card_register(dev->chip.card);
+ ret = snd_card_register(cdev->chip.card);
if (ret < 0) {
- log("snd_card_register() returned %d\n", ret);
- snd_card_free(dev->chip.card);
+ dev_err(dev, "snd_card_register() returned %d\n", ret);
+ snd_card_free(cdev->chip.card);
}
- ret = snd_usb_caiaq_control_init(dev);
+ ret = snd_usb_caiaq_control_init(cdev);
if (ret < 0)
- log("Unable to set up control system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up control system (ret=%d)\n", ret);
}
static int create_card(struct usb_device *usb_dev,
@@ -363,79 +409,80 @@ static int create_card(struct usb_device *usb_dev,
int devnum;
int err;
struct snd_card *card;
- struct snd_usb_caiaqdev *dev;
+ struct snd_usb_caiaqdev *cdev;
for (devnum = 0; devnum < SNDRV_CARDS; devnum++)
- if (enable[devnum] && !snd_card_used[devnum])
+ if (enable[devnum])
break;
if (devnum >= SNDRV_CARDS)
return -ENODEV;
- err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
- sizeof(struct snd_usb_caiaqdev), &card);
+ err = snd_card_new(&intf->dev,
+ index[devnum], id[devnum], THIS_MODULE,
+ sizeof(struct snd_usb_caiaqdev), &card);
if (err < 0)
return err;
- dev = caiaqdev(card);
- dev->chip.dev = usb_dev;
- dev->chip.card = card;
- dev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
+ cdev = caiaqdev(card);
+ cdev->chip.dev = usb_dev;
+ cdev->chip.card = card;
+ cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct));
- spin_lock_init(&dev->spinlock);
- snd_card_set_dev(card, &intf->dev);
+ spin_lock_init(&cdev->spinlock);
*cardp = card;
return 0;
}
-static int __devinit init_card(struct snd_usb_caiaqdev *dev)
+static int init_card(struct snd_usb_caiaqdev *cdev)
{
char *c, usbpath[32];
- struct usb_device *usb_dev = dev->chip.dev;
- struct snd_card *card = dev->chip.card;
+ struct usb_device *usb_dev = cdev->chip.dev;
+ struct snd_card *card = cdev->chip.card;
+ struct device *dev = caiaqdev_to_dev(cdev);
int err, len;
if (usb_set_interface(usb_dev, 0, 1) != 0) {
- log("can't set alt interface.\n");
+ dev_err(dev, "can't set alt interface.\n");
return -EIO;
}
- usb_init_urb(&dev->ep1_in_urb);
- usb_init_urb(&dev->midi_out_urb);
+ usb_init_urb(&cdev->ep1_in_urb);
+ usb_init_urb(&cdev->midi_out_urb);
- usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev,
+ usb_fill_bulk_urb(&cdev->ep1_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x1),
- dev->ep1_in_buf, EP1_BUFSIZE,
- usb_ep1_command_reply_dispatch, dev);
+ cdev->ep1_in_buf, EP1_BUFSIZE,
+ usb_ep1_command_reply_dispatch, cdev);
- usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev,
+ usb_fill_bulk_urb(&cdev->midi_out_urb, usb_dev,
usb_sndbulkpipe(usb_dev, 0x1),
- dev->midi_out_buf, EP1_BUFSIZE,
- snd_usb_caiaq_midi_output_done, dev);
+ cdev->midi_out_buf, EP1_BUFSIZE,
+ snd_usb_caiaq_midi_output_done, cdev);
- init_waitqueue_head(&dev->ep1_wait_queue);
- init_waitqueue_head(&dev->prepare_wait_queue);
+ init_waitqueue_head(&cdev->ep1_wait_queue);
+ init_waitqueue_head(&cdev->prepare_wait_queue);
- if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
+ if (usb_submit_urb(&cdev->ep1_in_urb, GFP_KERNEL) != 0)
return -EIO;
- err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
+ err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
if (err)
return err;
- if (!wait_event_timeout(dev->ep1_wait_queue, dev->spec_received, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ))
return -ENODEV;
usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
- dev->vendor_name, CAIAQ_USB_STR_LEN);
+ cdev->vendor_name, CAIAQ_USB_STR_LEN);
usb_string(usb_dev, usb_dev->descriptor.iProduct,
- dev->product_name, CAIAQ_USB_STR_LEN);
+ cdev->product_name, CAIAQ_USB_STR_LEN);
strlcpy(card->driver, MODNAME, sizeof(card->driver));
- strlcpy(card->shortname, dev->product_name, sizeof(card->shortname));
- strlcpy(card->mixername, dev->product_name, sizeof(card->mixername));
+ strlcpy(card->shortname, cdev->product_name, sizeof(card->shortname));
+ strlcpy(card->mixername, cdev->product_name, sizeof(card->mixername));
/* if the id was not passed as module option, fill it with a shortened
* version of the product string which does not contain any
@@ -455,22 +502,21 @@ static int __devinit init_card(struct snd_usb_caiaqdev *dev)
}
usb_make_path(usb_dev, usbpath, sizeof(usbpath));
- snprintf(card->longname, sizeof(card->longname),
- "%s %s (%s)",
- dev->vendor_name, dev->product_name, usbpath);
+ snprintf(card->longname, sizeof(card->longname), "%s %s (%s)",
+ cdev->vendor_name, cdev->product_name, usbpath);
- setup_card(dev);
+ setup_card(cdev);
return 0;
}
-static int __devinit snd_probe(struct usb_interface *intf,
+static int snd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret;
- struct snd_card *card;
- struct usb_device *device = interface_to_usbdev(intf);
+ struct snd_card *card = NULL;
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
- ret = create_card(device, intf, &card);
+ ret = create_card(usb_dev, intf, &card);
if (ret < 0)
return ret;
@@ -478,7 +524,7 @@ static int __devinit snd_probe(struct usb_interface *intf,
usb_set_intfdata(intf, card);
ret = init_card(caiaqdev(card));
if (ret < 0) {
- log("unable to init card! (ret=%d)\n", ret);
+ dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret);
snd_card_free(card);
return ret;
}
@@ -488,24 +534,25 @@ static int __devinit snd_probe(struct usb_interface *intf,
static void snd_disconnect(struct usb_interface *intf)
{
- struct snd_usb_caiaqdev *dev;
struct snd_card *card = usb_get_intfdata(intf);
-
- debug("%s(%p)\n", __func__, intf);
+ struct device *dev = intf->usb_dev;
+ struct snd_usb_caiaqdev *cdev;
if (!card)
return;
- dev = caiaqdev(card);
+ cdev = caiaqdev(card);
+ dev_dbg(dev, "%s(%p)\n", __func__, intf);
+
snd_card_disconnect(card);
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
- snd_usb_caiaq_input_free(dev);
+ snd_usb_caiaq_input_free(cdev);
#endif
- snd_usb_caiaq_audio_free(dev);
+ snd_usb_caiaq_audio_free(cdev);
- usb_kill_urb(&dev->ep1_in_urb);
- usb_kill_urb(&dev->midi_out_urb);
+ usb_kill_urb(&cdev->ep1_in_urb);
+ usb_kill_urb(&cdev->midi_out_urb);
snd_card_free(card);
usb_reset_device(interface_to_usbdev(intf));
@@ -520,16 +567,4 @@ static struct usb_driver snd_usb_driver = {
.id_table = snd_usb_id_table,
};
-static int __init snd_module_init(void)
-{
- return usb_register(&snd_usb_driver);
-}
-
-static void __exit snd_module_exit(void)
-{
- usb_deregister(&snd_usb_driver);
-}
-
-module_init(snd_module_init)
-module_exit(snd_module_exit)
-
+module_usb_driver(snd_usb_driver);
diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h
index f1117ecc84f..ab0f7520a99 100644
--- a/sound/usb/caiaq/device.h
+++ b/sound/usb/caiaq/device.h
@@ -16,22 +16,16 @@
#define USB_PID_SESSIONIO 0x1915
#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define USB_PID_TRAKTORKONTROLX1 0x2305
+#define USB_PID_TRAKTORKONTROLS4 0xbaff
+#define USB_PID_TRAKTORAUDIO2 0x041d
+#define USB_PID_MASCHINECONTROLLER 0x0808
#define EP1_BUFSIZE 64
#define EP4_BUFSIZE 512
#define CAIAQ_USB_STR_LEN 0xff
#define MAX_STREAMS 32
-//#define SND_USB_CAIAQ_DEBUG
-
#define MODNAME "snd-usb-caiaq"
-#define log(x...) snd_printk(KERN_WARNING MODNAME" log: " x)
-
-#ifdef SND_USB_CAIAQ_DEBUG
-#define debug(x...) snd_printk(KERN_WARNING MODNAME " debug: " x)
-#else
-#define debug(x...) do { } while(0)
-#endif
#define EP1_CMD_GET_DEVICE_INFO 0x1
#define EP1_CMD_READ_ERP 0x2
@@ -94,18 +88,20 @@ struct snd_usb_caiaqdev {
int input_panic, output_panic, warned;
char *audio_in_buf, *audio_out_buf;
unsigned int samplerates, bpp;
+ unsigned long outurb_active_mask;
struct snd_pcm_substream *sub_playback[MAX_STREAMS];
struct snd_pcm_substream *sub_capture[MAX_STREAMS];
/* Controls */
- unsigned char control_state[64];
+ unsigned char control_state[256];
+ unsigned char ep8_out_buf[2];
/* Linux input */
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
struct input_dev *input_dev;
char phys[64]; /* physical device path */
- unsigned short keycode[64];
+ unsigned short keycode[128];
struct urb *ep4_in_urb;
unsigned char ep4_in_buf[EP4_BUFSIZE];
#endif
@@ -119,16 +115,22 @@ struct snd_usb_caiaqdev {
};
struct snd_usb_caiaq_cb_info {
- struct snd_usb_caiaqdev *dev;
+ struct snd_usb_caiaqdev *cdev;
int index;
};
#define caiaqdev(c) ((struct snd_usb_caiaqdev*)(c)->private_data)
+#define caiaqdev_to_dev(d) (d->chip.card->dev)
-int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp);
-int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp);
-int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev, int rate, int depth, int bbp);
+int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *cdev, int digital, int analog, int erp);
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
+ unsigned char command,
+ const unsigned char *buffer,
+ int len);
+int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev,
unsigned char command,
+ unsigned char bank,
const unsigned char *buffer,
int len);
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index dcb620796d9..4b3fb91deec 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -16,6 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/usb.h>
@@ -30,7 +31,7 @@ static unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A };
static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4,
KEY_5, KEY_6, KEY_7 };
static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4,
- KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
+ KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 };
static unsigned short keycode_kore[] = {
KEY_FN_F1, /* "menu" */
@@ -67,7 +68,67 @@ static unsigned short keycode_kore[] = {
KEY_BRL_DOT5
};
-#define KONTROLX1_INPUTS 40
+#define MASCHINE_BUTTONS (42)
+#define MASCHINE_BUTTON(X) ((X) + BTN_MISC)
+#define MASCHINE_PADS (16)
+#define MASCHINE_PAD(X) ((X) + ABS_PRESSURE)
+
+static unsigned short keycode_maschine[] = {
+ MASCHINE_BUTTON(40), /* mute */
+ MASCHINE_BUTTON(39), /* solo */
+ MASCHINE_BUTTON(38), /* select */
+ MASCHINE_BUTTON(37), /* duplicate */
+ MASCHINE_BUTTON(36), /* navigate */
+ MASCHINE_BUTTON(35), /* pad mode */
+ MASCHINE_BUTTON(34), /* pattern */
+ MASCHINE_BUTTON(33), /* scene */
+ KEY_RESERVED, /* spacer */
+
+ MASCHINE_BUTTON(30), /* rec */
+ MASCHINE_BUTTON(31), /* erase */
+ MASCHINE_BUTTON(32), /* shift */
+ MASCHINE_BUTTON(28), /* grid */
+ MASCHINE_BUTTON(27), /* > */
+ MASCHINE_BUTTON(26), /* < */
+ MASCHINE_BUTTON(25), /* restart */
+
+ MASCHINE_BUTTON(21), /* E */
+ MASCHINE_BUTTON(22), /* F */
+ MASCHINE_BUTTON(23), /* G */
+ MASCHINE_BUTTON(24), /* H */
+ MASCHINE_BUTTON(20), /* D */
+ MASCHINE_BUTTON(19), /* C */
+ MASCHINE_BUTTON(18), /* B */
+ MASCHINE_BUTTON(17), /* A */
+
+ MASCHINE_BUTTON(0), /* control */
+ MASCHINE_BUTTON(2), /* browse */
+ MASCHINE_BUTTON(4), /* < */
+ MASCHINE_BUTTON(6), /* snap */
+ MASCHINE_BUTTON(7), /* autowrite */
+ MASCHINE_BUTTON(5), /* > */
+ MASCHINE_BUTTON(3), /* sampling */
+ MASCHINE_BUTTON(1), /* step */
+
+ MASCHINE_BUTTON(15), /* 8 softkeys */
+ MASCHINE_BUTTON(14),
+ MASCHINE_BUTTON(13),
+ MASCHINE_BUTTON(12),
+ MASCHINE_BUTTON(11),
+ MASCHINE_BUTTON(10),
+ MASCHINE_BUTTON(9),
+ MASCHINE_BUTTON(8),
+
+ MASCHINE_BUTTON(16), /* note repeat */
+ MASCHINE_BUTTON(29) /* play */
+};
+
+#define KONTROLX1_INPUTS (40)
+#define KONTROLS4_BUTTONS (12 * 8)
+#define KONTROLS4_AXIS (46)
+
+#define KONTROLS4_BUTTON(X) ((X) + BTN_MISC)
+#define KONTROLS4_ABS(X) ((X) + ABS_HAT0X)
#define DEG90 (range / 2)
#define DEG180 (range)
@@ -139,54 +200,55 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
#undef HIGH_PEAK
#undef LOW_PEAK
+static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *cdev,
+ int axis, const unsigned char *buf,
+ int offset)
+{
+ input_report_abs(cdev->input_dev, axis,
+ (buf[offset * 2] << 8) | buf[offset * 2 + 1]);
+}
-static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev,
const unsigned char *buf,
unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
- input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]);
- input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(cdev, ABS_X, buf, 2);
+ snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 1);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
- input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
- input_sync(input_dev);
- break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(cdev, ABS_X, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 1);
+ snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 2);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
- input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8) | buf[9]);
- input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8) | buf[5]);
- input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]);
- input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8) | buf[3]);
- input_report_abs(input_dev, ABS_HAT2X, (buf[14] << 8) | buf[15]);
- input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8) | buf[1]);
- input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]);
- input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8) | buf[7]);
- input_sync(input_dev);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT0X, buf, 4);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT0Y, buf, 2);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT1X, buf, 6);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT1Y, buf, 1);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT2X, buf, 7);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT2Y, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT3X, buf, 5);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT3Y, buf, 3);
break;
}
+
+ input_sync(input_dev);
}
-static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev,
const char *buf, unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
int i;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
i = decode_erp(buf[0], buf[1]);
input_report_abs(input_dev, ABS_X, i);
@@ -212,13 +274,36 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
input_report_abs(input_dev, ABS_HAT3Y, i);
input_sync(input_dev);
break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ /* 4 under the left screen */
+ input_report_abs(input_dev, ABS_HAT0X, decode_erp(buf[21], buf[20]));
+ input_report_abs(input_dev, ABS_HAT0Y, decode_erp(buf[15], buf[14]));
+ input_report_abs(input_dev, ABS_HAT1X, decode_erp(buf[9], buf[8]));
+ input_report_abs(input_dev, ABS_HAT1Y, decode_erp(buf[3], buf[2]));
+
+ /* 4 under the right screen */
+ input_report_abs(input_dev, ABS_HAT2X, decode_erp(buf[19], buf[18]));
+ input_report_abs(input_dev, ABS_HAT2Y, decode_erp(buf[13], buf[12]));
+ input_report_abs(input_dev, ABS_HAT3X, decode_erp(buf[7], buf[6]));
+ input_report_abs(input_dev, ABS_HAT3Y, decode_erp(buf[1], buf[0]));
+
+ /* volume */
+ input_report_abs(input_dev, ABS_RX, decode_erp(buf[17], buf[16]));
+ /* tempo */
+ input_report_abs(input_dev, ABS_RY, decode_erp(buf[11], buf[10]));
+ /* swing */
+ input_report_abs(input_dev, ABS_RZ, decode_erp(buf[5], buf[4]));
+
+ input_sync(input_dev);
+ break;
}
}
-static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *cdev,
unsigned char *buf, unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
unsigned short *keycode = input_dev->keycode;
int i;
@@ -233,63 +318,242 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
input_report_key(input_dev, keycode[i],
buf[i / 8] & (1 << (i % 8)));
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
+ input_report_abs(cdev->input_dev, ABS_MISC, 255 - buf[4]);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
/* rotary encoders */
- input_report_abs(dev->input_dev, ABS_X, buf[5] & 0xf);
- input_report_abs(dev->input_dev, ABS_Y, buf[5] >> 4);
- input_report_abs(dev->input_dev, ABS_Z, buf[6] & 0xf);
- input_report_abs(dev->input_dev, ABS_MISC, buf[6] >> 4);
+ input_report_abs(cdev->input_dev, ABS_X, buf[5] & 0xf);
+ input_report_abs(cdev->input_dev, ABS_Y, buf[5] >> 4);
+ input_report_abs(cdev->input_dev, ABS_Z, buf[6] & 0xf);
+ input_report_abs(cdev->input_dev, ABS_MISC, buf[6] >> 4);
break;
}
input_sync(input_dev);
}
+#define TKS4_MSGBLOCK_SIZE 16
+
+static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev,
+ const unsigned char *buf,
+ unsigned int len)
+{
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ while (len) {
+ unsigned int i, block_id = (buf[0] << 8) | buf[1];
+
+ switch (block_id) {
+ case 0:
+ /* buttons */
+ for (i = 0; i < KONTROLS4_BUTTONS; i++)
+ input_report_key(cdev->input_dev, KONTROLS4_BUTTON(i),
+ (buf[4 + (i / 8)] >> (i % 8)) & 1);
+ break;
+
+ case 1:
+ /* left wheel */
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8));
+ /* right wheel */
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8));
+
+ /* rotary encoders */
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf);
+
+ break;
+ case 2:
+ /* Volume Fader Channel D */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(0), buf, 1);
+ /* Volume Fader Channel B */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(1), buf, 2);
+ /* Volume Fader Channel A */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(2), buf, 3);
+ /* Volume Fader Channel C */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(3), buf, 4);
+ /* Loop Volume */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(4), buf, 6);
+ /* Crossfader */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(7), buf, 7);
+
+ break;
+
+ case 3:
+ /* Tempo Fader R */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(6), buf, 3);
+ /* Tempo Fader L */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(5), buf, 4);
+ /* Mic Volume */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(8), buf, 6);
+ /* Cue Mix */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(9), buf, 7);
+
+ break;
+
+ case 4:
+ /* Wheel distance sensor L */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(10), buf, 1);
+ /* Wheel distance sensor R */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(11), buf, 2);
+ /* Channel D EQ - Filter */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(12), buf, 3);
+ /* Channel D EQ - Low */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(13), buf, 4);
+ /* Channel D EQ - Mid */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(14), buf, 5);
+ /* Channel D EQ - Hi */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(15), buf, 6);
+ /* FX2 - dry/wet */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(16), buf, 7);
+
+ break;
+
+ case 5:
+ /* FX2 - 1 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(17), buf, 1);
+ /* FX2 - 2 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(18), buf, 2);
+ /* FX2 - 3 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(19), buf, 3);
+ /* Channel B EQ - Filter */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(20), buf, 4);
+ /* Channel B EQ - Low */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(21), buf, 5);
+ /* Channel B EQ - Mid */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(22), buf, 6);
+ /* Channel B EQ - Hi */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(23), buf, 7);
+
+ break;
+
+ case 6:
+ /* Channel A EQ - Filter */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(24), buf, 1);
+ /* Channel A EQ - Low */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(25), buf, 2);
+ /* Channel A EQ - Mid */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(26), buf, 3);
+ /* Channel A EQ - Hi */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(27), buf, 4);
+ /* Channel C EQ - Filter */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(28), buf, 5);
+ /* Channel C EQ - Low */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(29), buf, 6);
+ /* Channel C EQ - Mid */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(30), buf, 7);
+
+ break;
+
+ case 7:
+ /* Channel C EQ - Hi */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(31), buf, 1);
+ /* FX1 - wet/dry */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(32), buf, 2);
+ /* FX1 - 1 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(33), buf, 3);
+ /* FX1 - 2 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(34), buf, 4);
+ /* FX1 - 3 */
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(35), buf, 5);
+
+ break;
+
+ default:
+ dev_dbg(dev, "%s(): bogus block (id %d)\n",
+ __func__, block_id);
+ return;
+ }
+
+ len -= TKS4_MSGBLOCK_SIZE;
+ buf += TKS4_MSGBLOCK_SIZE;
+ }
+
+ input_sync(cdev->input_dev);
+}
+
+#define MASCHINE_MSGBLOCK_SIZE 2
+
+static void snd_usb_caiaq_maschine_dispatch(struct snd_usb_caiaqdev *cdev,
+ const unsigned char *buf,
+ unsigned int len)
+{
+ unsigned int i, pad_id;
+ __le16 *pressure = (__le16 *) buf;
+
+ for (i = 0; i < MASCHINE_PADS; i++) {
+ pad_id = le16_to_cpu(*pressure) >> 12;
+ input_report_abs(cdev->input_dev, MASCHINE_PAD(pad_id),
+ le16_to_cpu(*pressure) & 0xfff);
+ pressure++;
+ }
+
+ input_sync(cdev->input_dev);
+}
+
static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
{
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct snd_usb_caiaqdev *cdev = urb->context;
unsigned char *buf = urb->transfer_buffer;
+ struct device *dev = &urb->dev->dev;
int ret;
- if (urb->status || !dev || urb != dev->ep4_in_urb)
+ if (urb->status || !cdev || urb != cdev->ep4_in_urb)
return;
- if (urb->actual_length < 24)
- goto requeue;
-
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+ if (urb->actual_length < 24)
+ goto requeue;
+
if (buf[0] & 0x3)
- snd_caiaq_input_read_io(dev, buf + 1, 7);
+ snd_caiaq_input_read_io(cdev, buf + 1, 7);
if (buf[0] & 0x4)
- snd_caiaq_input_read_analog(dev, buf + 8, 16);
+ snd_caiaq_input_read_analog(cdev, buf + 8, 16);
+
+ break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ snd_usb_caiaq_tks4_dispatch(cdev, buf, urb->actual_length);
+ break;
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ if (urb->actual_length < (MASCHINE_PADS * MASCHINE_MSGBLOCK_SIZE))
+ goto requeue;
+
+ snd_usb_caiaq_maschine_dispatch(cdev, buf, urb->actual_length);
break;
}
requeue:
- dev->ep4_in_urb->actual_length = 0;
- ret = usb_submit_urb(dev->ep4_in_urb, GFP_ATOMIC);
+ cdev->ep4_in_urb->actual_length = 0;
+ ret = usb_submit_urb(cdev->ep4_in_urb, GFP_ATOMIC);
if (ret < 0)
- log("unable to submit urb. OOM!?\n");
+ dev_err(dev, "unable to submit urb. OOM!?\n");
}
static int snd_usb_caiaq_input_open(struct input_dev *idev)
{
- struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+ struct snd_usb_caiaqdev *cdev = input_get_drvdata(idev);
- if (!dev)
+ if (!cdev)
return -EINVAL;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
- if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ if (usb_submit_urb(cdev->ep4_in_urb, GFP_KERNEL) != 0)
return -EIO;
break;
}
@@ -299,41 +563,43 @@ static int snd_usb_caiaq_input_open(struct input_dev *idev)
static void snd_usb_caiaq_input_close(struct input_dev *idev)
{
- struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+ struct snd_usb_caiaqdev *cdev = input_get_drvdata(idev);
- if (!dev)
+ if (!cdev)
return;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
- usb_kill_urb(dev->ep4_in_urb);
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ usb_kill_urb(cdev->ep4_in_urb);
break;
}
}
-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
+void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev,
char *buf,
unsigned int len)
{
- if (!dev->input_dev || len < 1)
+ if (!cdev->input_dev || len < 1)
return;
switch (buf[0]) {
case EP1_CMD_READ_ANALOG:
- snd_caiaq_input_read_analog(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_analog(cdev, buf + 1, len - 1);
break;
case EP1_CMD_READ_ERP:
- snd_caiaq_input_read_erp(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_erp(cdev, buf + 1, len - 1);
break;
case EP1_CMD_READ_IO:
- snd_caiaq_input_read_io(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_io(cdev, buf + 1, len - 1);
break;
}
}
-int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev)
{
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
struct input_dev *input;
int i, ret = 0;
@@ -341,49 +607,49 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
if (!input)
return -ENOMEM;
- usb_make_path(usb_dev, dev->phys, sizeof(dev->phys));
- strlcat(dev->phys, "/input0", sizeof(dev->phys));
+ usb_make_path(usb_dev, cdev->phys, sizeof(cdev->phys));
+ strlcat(cdev->phys, "/input0", sizeof(cdev->phys));
- input->name = dev->product_name;
- input->phys = dev->phys;
+ input->name = cdev->product_name;
+ input->phys = cdev->phys;
usb_to_input_id(usb_dev, &input->id);
input->dev.parent = &usb_dev->dev;
- input_set_drvdata(input, dev);
+ input_set_drvdata(input, cdev);
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2));
- memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_rk2));
+ memcpy(cdev->keycode, keycode_rk2, sizeof(keycode_rk2));
input->keycodemax = ARRAY_SIZE(keycode_rk2);
input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 0);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3));
- memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_rk3));
+ memcpy(cdev->keycode, keycode_rk3, sizeof(keycode_rk3));
input->keycodemax = ARRAY_SIZE(keycode_rk3);
input_set_abs_params(input, ABS_X, 0, 1024, 0, 10);
input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 0);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1));
- memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_ak1));
+ memcpy(cdev->keycode, keycode_ak1, sizeof(keycode_ak1));
input->keycodemax = ARRAY_SIZE(keycode_ak1);
input_set_abs_params(input, ABS_X, 0, 999, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 0, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
@@ -395,8 +661,8 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_kore));
- memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_kore));
+ memcpy(cdev->keycode, keycode_kore, sizeof(keycode_kore));
input->keycodemax = ARRAY_SIZE(keycode_kore);
input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
@@ -410,7 +676,7 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
@@ -421,9 +687,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
- BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLX1_INPUTS);
+ BUILD_BUG_ON(sizeof(cdev->keycode) < KONTROLX1_INPUTS);
for (i = 0; i < KONTROLX1_INPUTS; i++)
- dev->keycode[i] = BTN_MISC + i;
+ cdev->keycode[i] = BTN_MISC + i;
input->keycodemax = KONTROLX1_INPUTS;
/* analog potentiometers */
@@ -442,20 +708,104 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
input_set_abs_params(input, ABS_Z, 0, 0xf, 0, 1);
input_set_abs_params(input, ABS_MISC, 0, 0xf, 0, 1);
- dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->ep4_in_urb) {
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
+ ret = -ENOMEM;
+ goto exit_free_idev;
+ }
+
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
+ usb_rcvbulkpipe(usb_dev, 0x4),
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
+
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
+
+ break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ BUILD_BUG_ON(sizeof(cdev->keycode) < KONTROLS4_BUTTONS);
+ for (i = 0; i < KONTROLS4_BUTTONS; i++)
+ cdev->keycode[i] = KONTROLS4_BUTTON(i);
+ input->keycodemax = KONTROLS4_BUTTONS;
+
+ for (i = 0; i < KONTROLS4_AXIS; i++) {
+ int axis = KONTROLS4_ABS(i);
+ input->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+ }
+
+ /* 36 analog potentiometers and faders */
+ for (i = 0; i < 36; i++)
+ input_set_abs_params(input, KONTROLS4_ABS(i), 0, 0xfff, 0, 10);
+
+ /* 2 encoder wheels */
+ input_set_abs_params(input, KONTROLS4_ABS(36), 0, 0x3ff, 0, 1);
+ input_set_abs_params(input, KONTROLS4_ABS(37), 0, 0x3ff, 0, 1);
+
+ /* 9 rotary encoders */
+ for (i = 0; i < 9; i++)
+ input_set_abs_params(input, KONTROLS4_ABS(38+i), 0, 0xf, 0, 1);
+
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
ret = -ENOMEM;
goto exit_free_idev;
}
- usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x4),
- dev->ep4_in_buf, EP4_BUFSIZE,
- snd_usb_caiaq_ep4_reply_dispatch, dev);
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
+
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
+
+ break;
+
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
+ input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
+ BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
+ BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
+ BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
+ BIT_MASK(ABS_RX) | BIT_MASK(ABS_RY) |
+ BIT_MASK(ABS_RZ);
+
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_maschine));
+ memcpy(cdev->keycode, keycode_maschine, sizeof(keycode_maschine));
+ input->keycodemax = ARRAY_SIZE(keycode_maschine);
+
+ for (i = 0; i < MASCHINE_PADS; i++) {
+ input->absbit[0] |= MASCHINE_PAD(i);
+ input_set_abs_params(input, MASCHINE_PAD(i), 0, 0xfff, 5, 10);
+ }
+
+ input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_RX, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_RY, 0, 999, 0, 10);
+ input_set_abs_params(input, ABS_RZ, 0, 999, 0, 10);
+
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
+ ret = -ENOMEM;
+ goto exit_free_idev;
+ }
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
+ usb_rcvbulkpipe(usb_dev, 0x4),
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
+
default:
/* no input methods supported on this device */
goto exit_free_idev;
@@ -463,33 +813,34 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
input->open = snd_usb_caiaq_input_open;
input->close = snd_usb_caiaq_input_close;
- input->keycode = dev->keycode;
+ input->keycode = cdev->keycode;
input->keycodesize = sizeof(unsigned short);
for (i = 0; i < input->keycodemax; i++)
- __set_bit(dev->keycode[i], input->keybit);
+ __set_bit(cdev->keycode[i], input->keybit);
+
+ cdev->input_dev = input;
ret = input_register_device(input);
if (ret < 0)
goto exit_free_idev;
- dev->input_dev = input;
return 0;
exit_free_idev:
input_free_device(input);
+ cdev->input_dev = NULL;
return ret;
}
-void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
+void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev)
{
- if (!dev || !dev->input_dev)
+ if (!cdev || !cdev->input_dev)
return;
- usb_kill_urb(dev->ep4_in_urb);
- usb_free_urb(dev->ep4_in_urb);
- dev->ep4_in_urb = NULL;
+ usb_kill_urb(cdev->ep4_in_urb);
+ usb_free_urb(cdev->ep4_in_urb);
+ cdev->ep4_in_urb = NULL;
- input_unregister_device(dev->input_dev);
- dev->input_dev = NULL;
+ input_unregister_device(cdev->input_dev);
+ cdev->input_dev = NULL;
}
-
diff --git a/sound/usb/caiaq/input.h b/sound/usb/caiaq/input.h
index ced53557786..6014e2713a6 100644
--- a/sound/usb/caiaq/input.h
+++ b/sound/usb/caiaq/input.h
@@ -1,8 +1,8 @@
#ifndef CAIAQ_INPUT_H
#define CAIAQ_INPUT_H
-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len);
-int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev);
+void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev, char *buf, unsigned int len);
+int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev);
#endif
diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c
index 2f218c77fff..2d7588461b3 100644
--- a/sound/usb/caiaq/midi.c
+++ b/sound/usb/caiaq/midi.c
@@ -16,6 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/usb.h>
#include <linux/gfp.h>
#include <sound/rawmidi.h>
@@ -37,12 +38,12 @@ static int snd_usb_caiaq_midi_input_close(struct snd_rawmidi_substream *substrea
static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
- if (!dev)
+ if (!cdev)
return;
- dev->midi_receive_substream = up ? substream : NULL;
+ cdev->midi_receive_substream = up ? substream : NULL;
}
@@ -53,49 +54,50 @@ static int snd_usb_caiaq_midi_output_open(struct snd_rawmidi_substream *substrea
static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substream)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
- if (dev->midi_out_active) {
- usb_kill_urb(&dev->midi_out_urb);
- dev->midi_out_active = 0;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
+ if (cdev->midi_out_active) {
+ usb_kill_urb(&cdev->midi_out_urb);
+ cdev->midi_out_active = 0;
}
return 0;
}
-static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev,
+static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev,
struct snd_rawmidi_substream *substream)
{
int len, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
- dev->midi_out_buf[1] = 0; /* port */
- len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3,
+ cdev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
+ cdev->midi_out_buf[1] = 0; /* port */
+ len = snd_rawmidi_transmit(substream, cdev->midi_out_buf + 3,
EP1_BUFSIZE - 3);
if (len <= 0)
return;
- dev->midi_out_buf[2] = len;
- dev->midi_out_urb.transfer_buffer_length = len+3;
+ cdev->midi_out_buf[2] = len;
+ cdev->midi_out_urb.transfer_buffer_length = len+3;
- ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC);
+ ret = usb_submit_urb(&cdev->midi_out_urb, GFP_ATOMIC);
if (ret < 0)
- log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
- "ret=%d, len=%d\n",
- substream, ret, len);
+ dev_err(dev,
+ "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
+ "ret=%d, len=%d\n", substream, ret, len);
else
- dev->midi_out_active = 1;
+ cdev->midi_out_active = 1;
}
static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
if (up) {
- dev->midi_out_substream = substream;
- if (!dev->midi_out_active)
- snd_usb_caiaq_midi_send(dev, substream);
+ cdev->midi_out_substream = substream;
+ if (!cdev->midi_out_active)
+ snd_usb_caiaq_midi_send(cdev, substream);
} else {
- dev->midi_out_substream = NULL;
+ cdev->midi_out_substream = NULL;
}
}
@@ -114,13 +116,13 @@ static struct snd_rawmidi_ops snd_usb_caiaq_midi_input =
.trigger = snd_usb_caiaq_midi_input_trigger,
};
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *cdev,
int port, const char *buf, int len)
{
- if (!dev->midi_receive_substream)
+ if (!cdev->midi_receive_substream)
return;
- snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
+ snd_rawmidi_receive(cdev->midi_receive_substream, buf, len);
}
int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
@@ -136,7 +138,7 @@ int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
if (ret < 0)
return ret;
- strcpy(rmidi->name, device->product_name);
+ strlcpy(rmidi->name, device->product_name, sizeof(rmidi->name));
rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = device;
@@ -160,15 +162,14 @@ int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
void snd_usb_caiaq_midi_output_done(struct urb* urb)
{
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct snd_usb_caiaqdev *cdev = urb->context;
- dev->midi_out_active = 0;
+ cdev->midi_out_active = 0;
if (urb->status != 0)
return;
- if (!dev->midi_out_substream)
+ if (!cdev->midi_out_substream)
return;
- snd_usb_caiaq_midi_send(dev, dev->midi_out_substream);
+ snd_usb_caiaq_midi_send(cdev, cdev->midi_out_substream);
}
-
diff --git a/sound/usb/caiaq/midi.h b/sound/usb/caiaq/midi.h
index 380f984babc..60bf3442b28 100644
--- a/sound/usb/caiaq/midi.h
+++ b/sound/usb/caiaq/midi.h
@@ -1,8 +1,9 @@
#ifndef CAIAQ_MIDI_H
#define CAIAQ_MIDI_H
-int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, int port, const char *buf, int len);
+int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *cdev,
+ int port, const char *buf, int len);
void snd_usb_caiaq_midi_output_done(struct urb *urb);
#endif /* CAIAQ_MIDI_H */
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 7a8ac1d81be..a09e5f3519e 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -25,9 +25,6 @@
*
* NOTES:
*
- * - async unlink should be used for avoiding the sleep inside lock.
- * 2.4.22 usb-uhci seems buggy for async unlinking and results in
- * oops. in such a cse, pass async_unlink=0 option.
* - the linked URBs would be preferred but not used so far because of
* the instability of unlinking.
* - type II is not supported properly. there is no device which supports
@@ -41,12 +38,15 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/ctype.h>
#include <linux/usb.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/module.h>
+#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
@@ -63,8 +63,9 @@
#include "helper.h"
#include "debug.h"
#include "pcm.h"
-#include "urb.h"
#include "format.h"
+#include "power.h"
+#include "stream.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
@@ -74,14 +75,13 @@ MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
/* Vendor/product IDs for this card */
static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int nrpacks = 8; /* max. number of packets per urb */
-static int async_unlink = 1;
static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
-static int ignore_ctl_error;
+static bool ignore_ctl_error;
+static bool autoclock = true;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -93,15 +93,13 @@ module_param_array(vid, int, NULL, 0444);
MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
module_param_array(pid, int, NULL, 0444);
MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
-module_param(nrpacks, int, 0644);
-MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
-module_param(async_unlink, bool, 0444);
-MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
module_param_array(device_setup, int, NULL, 0444);
MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
module_param(ignore_ctl_error, bool, 0444);
MODULE_PARM_DESC(ignore_ctl_error,
"Ignore errors from USB controller for mixer interfaces.");
+module_param(autoclock, bool, 0444);
+MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
@@ -126,9 +124,10 @@ static void snd_usb_stream_disconnect(struct list_head *head)
for (idx = 0; idx < 2; idx++) {
subs = &as->substream[idx];
if (!subs->num_formats)
- return;
- snd_usb_release_substream_urbs(subs, 1);
+ continue;
subs->interface = -1;
+ subs->data_endpoint = NULL;
+ subs->sync_endpoint = NULL;
}
}
@@ -140,27 +139,46 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
- snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
- dev->devnum, ctrlif, interface);
+ dev_err(&dev->dev, "%u:%d : does not exist\n",
+ ctrlif, interface);
return -EINVAL;
}
+ alts = &iface->altsetting[0];
+ altsd = get_iface_desc(alts);
+
+ /*
+ * Android with both accessory and audio interfaces enabled gets the
+ * interface numbers wrong.
+ */
+ if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) ||
+ chip->usb_id == USB_ID(0x18d1, 0x2d05)) &&
+ interface == 0 &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) {
+ interface = 2;
+ iface = usb_ifnum_to_if(dev, interface);
+ if (!iface)
+ return -EINVAL;
+ alts = &iface->altsetting[0];
+ altsd = get_iface_desc(alts);
+ }
+
if (usb_interface_claimed(iface)) {
- snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
- dev->devnum, ctrlif, interface);
+ dev_dbg(&dev->dev, "%d:%d: skipping, already claimed\n",
+ ctrlif, interface);
return -EINVAL;
}
- alts = &iface->altsetting[0];
- altsd = get_iface_desc(alts);
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
int err = snd_usbmidi_create(chip->card, iface,
&chip->midi_list, NULL);
if (err < 0) {
- snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
- dev->devnum, ctrlif, interface);
+ dev_err(&dev->dev,
+ "%u:%d: cannot create sequencer device\n",
+ ctrlif, interface);
return -EINVAL;
}
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
@@ -171,18 +189,19 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
- snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
- dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
+ dev_dbg(&dev->dev,
+ "%u:%d: skipping non-supported interface %d\n",
+ ctrlif, interface, altsd->bInterfaceClass);
/* skip non-supported classes */
return -EINVAL;
}
if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
- snd_printk(KERN_ERR "low speed audio streaming not supported\n");
+ dev_err(&dev->dev, "low speed audio streaming not supported\n");
return -EINVAL;
}
- if (! snd_usb_parse_audio_endpoints(chip, interface)) {
+ if (! snd_usb_parse_audio_interface(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
return -EINVAL;
@@ -211,21 +230,27 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
protocol = altsd->bInterfaceProtocol;
if (!control_header) {
- snd_printk(KERN_ERR "cannot find UAC_HEADER\n");
+ dev_err(&dev->dev, "cannot find UAC_HEADER\n");
return -EINVAL;
}
switch (protocol) {
+ default:
+ dev_warn(&dev->dev,
+ "unknown interface protocol %#02x, assuming v1\n",
+ protocol);
+ /* fall through */
+
case UAC_VERSION_1: {
- struct uac_ac_header_descriptor_v1 *h1 = control_header;
+ struct uac1_ac_header_descriptor *h1 = control_header;
if (!h1->bInCollection) {
- snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
+ dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
return -EINVAL;
}
if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
- snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n");
+ dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n");
return -EINVAL;
}
@@ -240,7 +265,22 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
if (!assoc) {
- snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
+ /*
+ * Firmware writers cannot count to three. So to find
+ * the IAD on the NuForce UDH-100, also check the next
+ * interface.
+ */
+ struct usb_interface *iface =
+ usb_ifnum_to_if(dev, ctrlif + 1);
+ if (iface &&
+ iface->intf_assoc &&
+ iface->intf_assoc->bFunctionClass == USB_CLASS_AUDIO &&
+ iface->intf_assoc->bFunctionProtocol == UAC_VERSION_2)
+ assoc = iface->intf_assoc;
+ }
+
+ if (!assoc) {
+ dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n");
return -EINVAL;
}
@@ -253,10 +293,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
break;
}
-
- default:
- snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
- return -EINVAL;
}
return 0;
@@ -271,6 +307,12 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
static int snd_usb_audio_free(struct snd_usb_audio *chip)
{
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &chip->ep_list)
+ snd_usb_endpoint_free(p);
+
+ mutex_destroy(&chip->mutex);
kfree(chip);
return 0;
}
@@ -281,11 +323,21 @@ static int snd_usb_audio_dev_free(struct snd_device *device)
return snd_usb_audio_free(chip);
}
+static void remove_trailing_spaces(char *str)
+{
+ char *p;
+
+ if (!*str)
+ return;
+ for (p = str + strlen(str) - 1; p >= str && isspace(*p); p--)
+ *p = 0;
+}
/*
* create a chip instance and set its names.
*/
-static int snd_usb_audio_create(struct usb_device *dev, int idx,
+static int snd_usb_audio_create(struct usb_interface *intf,
+ struct usb_device *dev, int idx,
const struct snd_usb_audio_quirk *quirk,
struct snd_usb_audio **rchip)
{
@@ -299,16 +351,22 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
*rchip = NULL;
- if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
- snd_usb_get_speed(dev) != USB_SPEED_FULL &&
- snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
- snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
+ switch (snd_usb_get_speed(dev)) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_WIRELESS:
+ case USB_SPEED_SUPER:
+ break;
+ default:
+ dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
return -ENXIO;
}
- err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
+ err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
+ dev_err(&dev->dev, "cannot create card instance %d\n", idx);
return err;
}
@@ -318,16 +376,19 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
return -ENOMEM;
}
+ mutex_init(&chip->mutex);
+ init_rwsem(&chip->shutdown_rwsem);
chip->index = idx;
chip->dev = dev;
chip->card = card;
chip->setup = device_setup[idx];
- chip->nrpacks = nrpacks;
- chip->async_unlink = async_unlink;
+ chip->autoclock = autoclock;
+ chip->probing = 1;
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->midi_list);
INIT_LIST_HEAD(&chip->mixer_list);
@@ -343,7 +404,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
snd_component_add(card, component);
/* retrieve the device string as shortname */
- if (quirk && quirk->product_name) {
+ if (quirk && quirk->product_name && *quirk->product_name) {
strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
} else {
if (!dev->descriptor.iProduct ||
@@ -355,9 +416,10 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
USB_ID_PRODUCT(chip->usb_id));
}
}
+ remove_trailing_spaces(card->shortname);
/* retrieve the vendor and device strings as longname */
- if (quirk && quirk->vendor_name) {
+ if (quirk && quirk->vendor_name && *quirk->vendor_name) {
len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
} else {
if (dev->descriptor.iManufacturer)
@@ -367,8 +429,11 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
len = 0;
/* we don't really care if there isn't any vendor string */
}
- if (len > 0)
- strlcat(card->longname, " ", sizeof(card->longname));
+ if (len > 0) {
+ remove_trailing_spaces(card->longname);
+ if (*card->longname)
+ strlcat(card->longname, " ", sizeof(card->longname));
+ }
strlcat(card->longname, card->shortname, sizeof(card->longname));
@@ -377,11 +442,22 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
if (len < sizeof(card->longname))
usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
- strlcat(card->longname,
- snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
- snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
- ", high speed",
- sizeof(card->longname));
+ switch (snd_usb_get_speed(dev)) {
+ case USB_SPEED_LOW:
+ strlcat(card->longname, ", low speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_FULL:
+ strlcat(card->longname, ", full speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_HIGH:
+ strlcat(card->longname, ", high speed", sizeof(card->longname));
+ break;
+ case USB_SPEED_SUPER:
+ strlcat(card->longname, ", super speed", sizeof(card->longname));
+ break;
+ default:
+ break;
+ }
snd_usb_audio_create_proc(chip);
@@ -399,9 +475,10 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
* only at the first time. the successive calls of this function will
* append the pcm interface to the corresponding card.
*/
-static void *snd_usb_audio_probe(struct usb_device *dev,
- struct usb_interface *intf,
- const struct usb_device_id *usb_id)
+static struct snd_usb_audio *
+snd_usb_audio_probe(struct usb_device *dev,
+ struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
{
const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
int i, err;
@@ -430,10 +507,11 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
for (i = 0; i < SNDRV_CARDS; i++) {
if (usb_chip[i] && usb_chip[i]->dev == dev) {
if (usb_chip[i]->shutdown) {
- snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n");
+ dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n");
goto __error;
}
chip = usb_chip[i];
+ chip->probing = 1;
break;
}
}
@@ -445,18 +523,27 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
if (enable[i] && ! usb_chip[i] &&
(vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
(pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
- if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
+ if (snd_usb_audio_create(intf, dev, i, quirk,
+ &chip) < 0) {
goto __error;
}
- snd_card_set_dev(chip->card, &intf->dev);
+ chip->pm_intf = intf;
break;
}
if (!chip) {
- printk(KERN_ERR "no available usb audio device\n");
+ dev_err(&dev->dev, "no available usb audio device\n");
goto __error;
}
}
+ /*
+ * For devices with more than one control interface, we assume the
+ * first contains the audio controls. We might need a more specific
+ * check here in the future.
+ */
+ if (!chip->ctrl_intf)
+ chip->ctrl_intf = alts;
+
chip->txfr_quirk = 0;
err = 1; /* continue */
if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
@@ -465,8 +552,6 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
goto __error;
}
- chip->ctrl_intf = alts;
-
if (err > 0) {
/* create normal USB audio interfaces */
if (snd_usb_create_streams(chip, ifnum) < 0 ||
@@ -482,12 +567,16 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
usb_chip[chip->index] = chip;
chip->num_interfaces++;
+ chip->probing = 0;
mutex_unlock(&register_mutex);
return chip;
__error:
- if (chip && !chip->num_interfaces)
- snd_card_free(chip->card);
+ if (chip) {
+ if (!chip->num_interfaces)
+ snd_card_free(chip->card);
+ chip->probing = 0;
+ }
mutex_unlock(&register_mutex);
__err_val:
return NULL;
@@ -497,26 +586,34 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
-static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
+static void snd_usb_audio_disconnect(struct usb_device *dev,
+ struct snd_usb_audio *chip)
{
- struct snd_usb_audio *chip;
struct snd_card *card;
struct list_head *p;
- if (ptr == (void *)-1L)
+ if (chip == (void *)-1L)
return;
- chip = ptr;
card = chip->card;
- mutex_lock(&register_mutex);
+ down_write(&chip->shutdown_rwsem);
chip->shutdown = 1;
+ up_write(&chip->shutdown_rwsem);
+
+ mutex_lock(&register_mutex);
chip->num_interfaces--;
if (chip->num_interfaces <= 0) {
+ struct snd_usb_endpoint *ep;
+
snd_card_disconnect(card);
/* release the pcm resources */
list_for_each(p, &chip->pcm_list) {
snd_usb_stream_disconnect(p);
}
+ /* release the endpoint resources */
+ list_for_each_entry(ep, &chip->ep_list, list) {
+ snd_usb_endpoint_release(ep);
+ }
/* release the midi resources */
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
@@ -539,7 +636,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
static int usb_audio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- void *chip;
+ struct snd_usb_audio *chip;
chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
if (chip) {
usb_set_intfdata(intf, chip);
@@ -555,46 +652,107 @@ static void usb_audio_disconnect(struct usb_interface *intf)
}
#ifdef CONFIG_PM
+
+int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+ int err = -ENODEV;
+
+ down_read(&chip->shutdown_rwsem);
+ if (chip->probing && chip->in_pm)
+ err = 0;
+ else if (!chip->shutdown)
+ err = usb_autopm_get_interface(chip->pm_intf);
+ up_read(&chip->shutdown_rwsem);
+
+ return err;
+}
+
+void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+ down_read(&chip->shutdown_rwsem);
+ if (!chip->shutdown && !chip->probing && !chip->in_pm)
+ usb_autopm_put_interface(chip->pm_intf);
+ up_read(&chip->shutdown_rwsem);
+}
+
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
- struct list_head *p;
struct snd_usb_stream *as;
+ struct usb_mixer_interface *mixer;
if (chip == (void *)-1L)
return 0;
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
- if (!chip->num_suspended_intf++) {
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
- snd_pcm_suspend_all(as->pcm);
+ if (!PMSG_IS_AUTO(message)) {
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+ if (!chip->num_suspended_intf++) {
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ snd_pcm_suspend_all(as->pcm);
+ as->substream[0].need_setup_ep =
+ as->substream[1].need_setup_ep = true;
+ }
}
+ } else {
+ /*
+ * otherwise we keep the rest of the system in the dark
+ * to keep this transparent
+ */
+ if (!chip->num_suspended_intf++)
+ chip->autosuspended = 1;
}
+ if (chip->num_suspended_intf == 1)
+ list_for_each_entry(mixer, &chip->mixer_list, list)
+ snd_usb_mixer_suspend(mixer);
+
return 0;
}
-static int usb_audio_resume(struct usb_interface *intf)
+static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
+ struct usb_mixer_interface *mixer;
+ int err = 0;
if (chip == (void *)-1L)
return 0;
if (--chip->num_suspended_intf)
return 0;
+
+ chip->in_pm = 1;
/*
* ALSA leaves material resumption to user space
- * we just notify
+ * we just notify and restart the mixers
*/
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+ err = snd_usb_mixer_resume(mixer, reset_resume);
+ if (err < 0)
+ goto err_out;
+ }
- snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ if (!chip->autosuspended)
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ chip->autosuspended = 0;
- return 0;
+err_out:
+ chip->in_pm = 0;
+ return err;
+}
+
+static int usb_audio_resume(struct usb_interface *intf)
+{
+ return __usb_audio_resume(intf, false);
+}
+
+static int usb_audio_reset_resume(struct usb_interface *intf)
+{
+ return __usb_audio_resume(intf, true);
}
#else
#define usb_audio_suspend NULL
#define usb_audio_resume NULL
+#define usb_audio_reset_resume NULL
#endif /* CONFIG_PM */
static struct usb_device_id usb_audio_ids [] = {
@@ -604,8 +762,7 @@ static struct usb_device_id usb_audio_ids [] = {
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
{ } /* Terminating entry */
};
-
-MODULE_DEVICE_TABLE (usb, usb_audio_ids);
+MODULE_DEVICE_TABLE(usb, usb_audio_ids);
/*
* entry point for linux usb interface
@@ -617,22 +774,9 @@ static struct usb_driver usb_audio_driver = {
.disconnect = usb_audio_disconnect,
.suspend = usb_audio_suspend,
.resume = usb_audio_resume,
+ .reset_resume = usb_audio_reset_resume,
.id_table = usb_audio_ids,
+ .supports_autosuspend = 1,
};
-static int __init snd_usb_audio_init(void)
-{
- if (nrpacks < 1 || nrpacks > MAX_PACKS) {
- printk(KERN_WARNING "invalid nrpacks value.\n");
- return -EINVAL;
- }
- return usb_register(&usb_audio_driver);
-}
-
-static void __exit snd_usb_audio_cleanup(void)
-{
- usb_deregister(&usb_audio_driver);
-}
-
-module_init(snd_usb_audio_init);
-module_exit(snd_usb_audio_cleanup);
+module_usb_driver(usb_audio_driver);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 1febf2f2375..97acb906acc 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -1,11 +1,12 @@
#ifndef __USBAUDIO_CARD_H
#define __USBAUDIO_CARD_H
-#define MAX_PACKS 20
+#define MAX_NR_RATES 1024
+#define MAX_PACKS 6 /* per URB */
#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS 8
+#define MAX_URBS 12
#define SYNC_URBS 4 /* always four urbs for sync */
-#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */
+#define MAX_QUEUE 18 /* try not to exceed this queue length, in ms */
struct audioformat {
struct list_head list;
@@ -20,29 +21,88 @@ struct audioformat {
unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */
+ unsigned char protocol; /* UAC_VERSION_1/2 */
unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */
unsigned int nr_rates; /* number of rate table entries */
unsigned int *rate_table; /* rate table */
unsigned char clock; /* associated clock */
+ struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
+ bool dsd_dop; /* add DOP headers in case of DSD samples */
+ bool dsd_bitrev; /* reverse the bits of each DSD sample */
};
struct snd_usb_substream;
+struct snd_usb_endpoint;
struct snd_urb_ctx {
struct urb *urb;
unsigned int buffer_size; /* size of data buffer, if data URB */
struct snd_usb_substream *subs;
+ struct snd_usb_endpoint *ep;
int index; /* index for urb array */
int packets; /* number of packets per urb */
+ int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */
+ struct list_head ready_list;
};
-struct snd_urb_ops {
- int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
- int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+struct snd_usb_endpoint {
+ struct snd_usb_audio *chip;
+
+ int use_count;
+ int ep_num; /* the referenced endpoint number */
+ int type; /* SND_USB_ENDPOINT_TYPE_* */
+ unsigned long flags;
+
+ void (*prepare_data_urb) (struct snd_usb_substream *subs,
+ struct urb *urb);
+ void (*retire_data_urb) (struct snd_usb_substream *subs,
+ struct urb *urb);
+
+ struct snd_usb_substream *data_subs;
+ struct snd_usb_endpoint *sync_master;
+ struct snd_usb_endpoint *sync_slave;
+
+ struct snd_urb_ctx urb[MAX_URBS];
+
+ struct snd_usb_packet_info {
+ uint32_t packet_size[MAX_PACKS_HS];
+ int packets;
+ } next_packet[MAX_URBS];
+ int next_packet_read_pos, next_packet_write_pos;
+ struct list_head ready_playback_urbs;
+
+ unsigned int nurbs; /* # urbs */
+ unsigned long active_mask; /* bitmask of active urbs */
+ unsigned long unlink_mask; /* bitmask of unlinked urbs */
+ char *syncbuf; /* sync buffer for all sync URBs */
+ dma_addr_t sync_dma; /* DMA address of syncbuf */
+
+ unsigned int pipe; /* the data i/o pipe */
+ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
+ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
+ int freqshift; /* how much to shift the feedback value to get Q16.16 */
+ unsigned int freqmax; /* maximum sampling rate, used for buffer management */
+ unsigned int phase; /* phase accumulator */
+ unsigned int maxpacksize; /* max packet size in bytes */
+ unsigned int maxframesize; /* max packet size in frames */
+ unsigned int max_urb_frames; /* max URB size in frames */
+ unsigned int curpacksize; /* current packet size in bytes (for capture) */
+ unsigned int curframesize; /* current packet size in frames (for capture) */
+ unsigned int syncmaxsize; /* sync endpoint packet size */
+ unsigned int fill_max:1; /* fill max packet size always */
+ unsigned int udh01_fb_quirk:1; /* corrupted feedback data */
+ unsigned int datainterval; /* log_2 of data packet interval */
+ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
+ unsigned char silence_value;
+ unsigned int stride;
+ int iface, altsetting;
+ int skip_packets; /* quirks for devices to ignore the first n packets
+ in a stream */
+
+ spinlock_t lock;
+ struct list_head list;
};
struct snd_usb_substream {
@@ -53,37 +113,31 @@ struct snd_usb_substream {
int interface; /* current interface */
int endpoint; /* assigned endpoint */
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
+ snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */
+ unsigned int channels; /* current number of channels (for hw_params callback) */
+ unsigned int channels_max; /* max channels in the all audiofmts */
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
+ unsigned int period_frames; /* current frames per period */
+ unsigned int buffer_periods; /* current periods per buffer */
unsigned int altset_idx; /* USB data format: index of alternate setting */
- unsigned int datapipe; /* the data i/o pipe */
- unsigned int syncpipe; /* 1 - async out or adaptive in */
- unsigned int datainterval; /* log_2 of data packet interval */
- unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
- unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
- unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
- unsigned int freqmax; /* maximum sampling rate, used for buffer management */
- unsigned int phase; /* phase accumulator */
- unsigned int maxpacksize; /* max packet size in bytes */
- unsigned int maxframesize; /* max packet size in frames */
- unsigned int curpacksize; /* current packet size in bytes (for capture) */
- unsigned int curframesize; /* current packet size in frames (for capture) */
- unsigned int fill_max: 1; /* fill max packet size always */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
unsigned int fmt_type; /* USB audio format type (1-3) */
+ unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
unsigned int running: 1; /* running status */
unsigned int hwptr_done; /* processed byte position in the buffer */
unsigned int transfer_done; /* processed frames since last period update */
- unsigned long active_mask; /* bitmask of active urbs */
- unsigned long unlink_mask; /* bitmask of unlinked urbs */
+ unsigned int frame_limit; /* limits number of packets in URB */
- unsigned int nurbs; /* # urbs */
- struct snd_urb_ctx dataurb[MAX_URBS]; /* data urb table */
- struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */
- char *syncbuf; /* sync buffer for all sync URBs */
- dma_addr_t sync_dma; /* DMA address of syncbuf */
+ /* data and sync endpoints for this stream */
+ unsigned int ep_num; /* the endpoint number */
+ struct snd_usb_endpoint *data_endpoint;
+ struct snd_usb_endpoint *sync_endpoint;
+ unsigned long flags;
+ bool need_setup_ep; /* (re)configure EP at prepare? */
+ unsigned int speed; /* USB_SPEED_XXX */
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
@@ -91,7 +145,14 @@ struct snd_usb_substream {
struct snd_pcm_hw_constraint_list rate_list; /* limited rates */
spinlock_t lock;
- struct snd_urb_ops ops; /* callbacks (must be filled at init) */
+ int last_frame_number; /* stored frame number */
+ int last_delay; /* stored delay */
+
+ struct {
+ int marker;
+ int channel;
+ int byte_idx;
+ } dsd_dop;
};
struct snd_usb_stream {
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index b5855114667..03fed6611d9 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -19,33 +19,20 @@
#include <linux/bitops.h>
#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/slab.h>
#include <linux/string.h>
#include <linux/usb.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include "usbaudio.h"
#include "card.h"
-#include "midi.h"
-#include "mixer.h"
-#include "proc.h"
-#include "quirks.h"
-#include "endpoint.h"
#include "helper.h"
-#include "debug.h"
-#include "pcm.h"
-#include "urb.h"
-#include "format.h"
+#include "clock.h"
+#include "quirks.h"
static struct uac_clock_source_descriptor *
snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
@@ -105,7 +92,7 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
UAC2_CX_CLOCK_SELECTOR << 8,
snd_usb_ctrl_intf(chip) | (selector_id << 8),
- &buf, sizeof(buf), 1000);
+ &buf, sizeof(buf));
if (ret < 0)
return ret;
@@ -113,32 +100,76 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i
return buf;
}
+static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
+ unsigned char pin)
+{
+ int ret;
+
+ ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+ UAC2_CS_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ UAC2_CX_CLOCK_SELECTOR << 8,
+ snd_usb_ctrl_intf(chip) | (selector_id << 8),
+ &pin, sizeof(pin));
+ if (ret < 0)
+ return ret;
+
+ if (ret != sizeof(pin)) {
+ usb_audio_err(chip,
+ "setting selector (id %d) unexpected length %d\n",
+ selector_id, ret);
+ return -EINVAL;
+ }
+
+ ret = uac_clock_selector_get_val(chip, selector_id);
+ if (ret < 0)
+ return ret;
+
+ if (ret != pin) {
+ usb_audio_err(chip,
+ "setting selector (id %d) to %x failed (current: %d)\n",
+ selector_id, pin, ret);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
{
int err;
unsigned char data;
struct usb_device *dev = chip->dev;
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+
+ /* If a clock source can't tell us whether it's valid, we assume it is */
+ if (!uac2_control_is_readable(cs_desc->bmControls,
+ UAC2_CS_CONTROL_CLOCK_VALID - 1))
+ return 1;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_CLOCK_VALID << 8,
snd_usb_ctrl_intf(chip) | (source_id << 8),
- &data, sizeof(data), 1000);
+ &data, sizeof(data));
if (err < 0) {
- snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
+ dev_warn(&dev->dev,
+ "%s(): cannot get clock validity for id %d\n",
__func__, source_id);
- return err;
+ return 0;
}
return !!data;
}
-/* Try to find the clock source ID of a given clock entity */
-
static int __uac_clock_find_source(struct snd_usb_audio *chip,
- struct usb_host_interface *host_iface,
- int entity_id, unsigned long *visited)
+ int entity_id, unsigned long *visited,
+ bool validate)
{
struct uac_clock_source_descriptor *source;
struct uac_clock_selector_descriptor *selector;
@@ -147,20 +178,28 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
entity_id &= 0xff;
if (test_and_set_bit(entity_id, visited)) {
- snd_printk(KERN_WARNING
- "%s(): recursive clock topology detected, id %d.\n",
- __func__, entity_id);
+ usb_audio_warn(chip,
+ "%s(): recursive clock topology detected, id %d.\n",
+ __func__, entity_id);
return -EINVAL;
}
/* first, see if the ID we're looking for is a clock source already */
- source = snd_usb_find_clock_source(host_iface, entity_id);
- if (source)
- return source->bClockID;
+ source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
+ if (source) {
+ entity_id = source->bClockID;
+ if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
+ usb_audio_err(chip,
+ "clock source %d is not valid, cannot use\n",
+ entity_id);
+ return -ENXIO;
+ }
+ return entity_id;
+ }
- selector = snd_usb_find_clock_selector(host_iface, entity_id);
+ selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
if (selector) {
- int ret;
+ int ret, i, cur;
/* the entity ID we are looking for is a selector.
* find out what it currently selects */
@@ -168,35 +207,73 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
if (ret < 0)
return ret;
+ /* Selector values are one-based */
+
if (ret > selector->bNrInPins || ret < 1) {
- printk(KERN_ERR
+ usb_audio_err(chip,
"%s(): selector reported illegal value, id %d, ret %d\n",
__func__, selector->bClockID, ret);
return -EINVAL;
}
- return __uac_clock_find_source(chip, host_iface,
- selector->baCSourceID[ret-1],
- visited);
+ cur = ret;
+ ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
+ visited, validate);
+ if (!validate || ret > 0 || !chip->autoclock)
+ return ret;
+
+ /* The current clock source is invalid, try others. */
+ for (i = 1; i <= selector->bNrInPins; i++) {
+ int err;
+
+ if (i == cur)
+ continue;
+
+ ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
+ visited, true);
+ if (ret < 0)
+ continue;
+
+ err = uac_clock_selector_set_val(chip, entity_id, i);
+ if (err < 0)
+ continue;
+
+ usb_audio_info(chip,
+ "found and selected valid clock source %d\n",
+ ret);
+ return ret;
+ }
+
+ return -ENXIO;
}
/* FIXME: multipliers only act as pass-thru element for now */
- multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
+ multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
if (multiplier)
- return __uac_clock_find_source(chip, host_iface,
- multiplier->bCSourceID, visited);
+ return __uac_clock_find_source(chip, multiplier->bCSourceID,
+ visited, validate);
return -EINVAL;
}
-int snd_usb_clock_find_source(struct snd_usb_audio *chip,
- struct usb_host_interface *host_iface,
- int entity_id)
+/*
+ * For all kinds of sample rate settings and other device queries,
+ * the clock source (end-leaf) must be used. However, clock selectors,
+ * clock multipliers and sample rate converters may be specified as
+ * clock source input to terminal. This functions walks the clock path
+ * to its end and tries to find the source.
+ *
+ * The 'visited' bitfield is used internally to detect recursive loops.
+ *
+ * Returns the clock source UnitID (>=0) on success, or an error.
+ */
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ bool validate)
{
DECLARE_BITMAP(visited, 256);
memset(visited, 0, sizeof(visited));
- return __uac_clock_find_source(chip, host_iface, entity_id, visited);
+ return __uac_clock_find_source(chip, entity_id, visited, validate);
}
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
@@ -211,11 +288,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have sampling rate control, bail out */
- if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
- snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
- dev->devnum, iface, fmt->altsetting);
+ if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
return 0;
- }
data[0] = rate;
data[1] = rate >> 8;
@@ -223,75 +297,111 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
- dev->devnum, iface, fmt->altsetting, rate, ep);
+ data, sizeof(data))) < 0) {
+ dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
+ iface, fmt->altsetting, rate, ep);
return err;
}
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
- dev->devnum, iface, fmt->altsetting, ep);
+ data, sizeof(data))) < 0) {
+ dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
+ iface, fmt->altsetting, ep);
return 0; /* some devices don't support reading */
}
crate = data[0] | (data[1] << 8) | (data[2] << 16);
if (crate != rate) {
- snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+ dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate);
// runtime->rate = crate;
}
return 0;
}
+static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+ int altsetting, int clock)
+{
+ struct usb_device *dev = chip->dev;
+ __le32 data;
+ int err;
+
+ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ UAC2_CS_CONTROL_SAM_FREQ << 8,
+ snd_usb_ctrl_intf(chip) | (clock << 8),
+ &data, sizeof(data));
+ if (err < 0) {
+ dev_warn(&dev->dev, "%d:%d: cannot get freq (v2): err %d\n",
+ iface, altsetting, err);
+ return 0;
+ }
+
+ return le32_to_cpu(data);
+}
+
static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
struct usb_device *dev = chip->dev;
- unsigned char data[4];
- int err, crate;
- int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
+ __le32 data;
+ int err, cur_rate, prev_rate;
+ int clock;
+ bool writeable;
+ struct uac_clock_source_descriptor *cs_desc;
+ clock = snd_usb_clock_find_source(chip, fmt->clock, true);
if (clock < 0)
return clock;
- if (!uac_clock_source_is_valid(chip, clock)) {
- snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
- dev->devnum, iface, fmt->altsetting, clock);
- return -ENXIO;
- }
+ prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+ if (prev_rate == rate)
+ return 0;
- data[0] = rate;
- data[1] = rate >> 8;
- data[2] = rate >> 16;
- data[3] = rate >> 24;
- if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
- dev->devnum, iface, fmt->altsetting, rate);
- return err;
+ cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
+ writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
+ if (writeable) {
+ data = cpu_to_le32(rate);
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ UAC2_CS_CONTROL_SAM_FREQ << 8,
+ snd_usb_ctrl_intf(chip) | (clock << 8),
+ &data, sizeof(data));
+ if (err < 0) {
+ usb_audio_err(chip,
+ "%d:%d: cannot set freq %d (v2): err %d\n",
+ iface, fmt->altsetting, rate, err);
+ return err;
+ }
+
+ cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+ } else {
+ cur_rate = prev_rate;
}
- if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
- UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
- dev->devnum, iface, fmt->altsetting);
- return err;
+ if (cur_rate != rate) {
+ if (!writeable) {
+ usb_audio_warn(chip,
+ "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
+ iface, fmt->altsetting, rate, cur_rate);
+ return -ENXIO;
+ }
+ usb_audio_dbg(chip,
+ "current rate %d is different from the runtime rate %d\n",
+ cur_rate, rate);
}
- crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
- if (crate != rate)
- snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+ /* Some devices doesn't respond to sample rate changes while the
+ * interface is active. */
+ if (rate != prev_rate) {
+ usb_set_interface(dev, iface, 0);
+ snd_usb_set_interface_quirk(dev);
+ usb_set_interface(dev, iface, fmt->altsetting);
+ snd_usb_set_interface_quirk(dev);
+ }
return 0;
}
@@ -300,16 +410,13 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
- switch (altsd->bInterfaceProtocol) {
+ switch (fmt->protocol) {
case UAC_VERSION_1:
+ default:
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
case UAC_VERSION_2:
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
}
-
- return -EINVAL;
}
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
index beb253684e2..d592e4a2985 100644
--- a/sound/usb/clock.h
+++ b/sound/usb/clock.h
@@ -5,8 +5,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt, int rate);
-int snd_usb_clock_find_source(struct snd_usb_audio *chip,
- struct usb_host_interface *host_iface,
- int entity_id);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ bool validate);
#endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/debug.h b/sound/usb/debug.h
index 343ec2d9ee6..58030176f00 100644
--- a/sound/usb/debug.h
+++ b/sound/usb/debug.h
@@ -8,7 +8,7 @@
#ifdef HW_CONST_DEBUG
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
-#define hwc_debug(fmt, args...) /**/
+#define hwc_debug(fmt, args...) do { } while(0)
#endif
#endif /* __USBAUDIO_DEBUG_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 6f6596cf2b1..114e3e7ff51 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -15,434 +15,1156 @@
*
*/
+#include <linux/gfp.h>
#include <linux/init.h>
-#include <linux/slab.h>
+#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
-#include <linux/usb/audio-v2.h>
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include "usbaudio.h"
+#include "helper.h"
#include "card.h"
-#include "proc.h"
-#include "quirks.h"
#include "endpoint.h"
-#include "urb.h"
#include "pcm.h"
-#include "helper.h"
-#include "format.h"
+#include "quirks.h"
+
+#define EP_FLAG_RUNNING 1
+#define EP_FLAG_STOPPING 2
+
+/*
+ * snd_usb_endpoint is a model that abstracts everything related to an
+ * USB endpoint and its streaming.
+ *
+ * There are functions to activate and deactivate the streaming URBs and
+ * optional callbacks to let the pcm logic handle the actual content of the
+ * packets for playback and record. Thus, the bus streaming and the audio
+ * handlers are fully decoupled.
+ *
+ * There are two different types of endpoints in audio applications.
+ *
+ * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both
+ * inbound and outbound traffic.
+ *
+ * SND_USB_ENDPOINT_TYPE_SYNC endpoints are for inbound traffic only and
+ * expect the payload to carry Q10.14 / Q16.16 formatted sync information
+ * (3 or 4 bytes).
+ *
+ * Each endpoint has to be configured prior to being used by calling
+ * snd_usb_endpoint_set_params().
+ *
+ * The model incorporates a reference counting, so that multiple users
+ * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and
+ * only the first user will effectively start the URBs, and only the last
+ * one to stop it will tear the URBs down again.
+ */
/*
- * free a substream
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
*/
-static void free_substream(struct snd_usb_substream *subs)
+static inline unsigned get_usb_full_speed_rate(unsigned int rate)
{
- struct list_head *p, *n;
-
- if (!subs->num_formats)
- return; /* not initialized */
- list_for_each_safe(p, n, &subs->fmt_list) {
- struct audioformat *fp = list_entry(p, struct audioformat, list);
- kfree(fp->rate_table);
- kfree(fp);
+ return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned int rate)
+{
+ return ((rate << 10) + 62) / 125;
+}
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(struct snd_urb_ctx *u)
+{
+ if (u->buffer_size)
+ usb_free_coherent(u->ep->chip->dev, u->buffer_size,
+ u->urb->transfer_buffer,
+ u->urb->transfer_dma);
+ usb_free_urb(u->urb);
+ u->urb = NULL;
+}
+
+static const char *usb_error_string(int err)
+{
+ switch (err) {
+ case -ENODEV:
+ return "no device";
+ case -ENOENT:
+ return "endpoint not enabled";
+ case -EPIPE:
+ return "endpoint stalled";
+ case -ENOSPC:
+ return "not enough bandwidth";
+ case -ESHUTDOWN:
+ return "device disabled";
+ case -EHOSTUNREACH:
+ return "device suspended";
+ case -EINVAL:
+ case -EAGAIN:
+ case -EFBIG:
+ case -EMSGSIZE:
+ return "internal error";
+ default:
+ return "unknown error";
}
- kfree(subs->rate_list.list);
}
+/**
+ * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type
+ *
+ * @ep: The snd_usb_endpoint
+ *
+ * Determine whether an endpoint is driven by an implicit feedback
+ * data endpoint source.
+ */
+int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
+{
+ return ep->sync_master &&
+ ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+ ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+ usb_pipeout(ep->pipe);
+}
/*
- * free a usb stream instance
+ * For streaming based on information derived from sync endpoints,
+ * prepare_outbound_urb_sizes() will call next_packet_size() to
+ * determine the number of samples to be sent in the next packet.
+ *
+ * For implicit feedback, next_packet_size() is unused.
*/
-static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
+int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
+{
+ unsigned long flags;
+ int ret;
+
+ if (ep->fill_max)
+ return ep->maxframesize;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->phase = (ep->phase & 0xffff)
+ + (ep->freqm << ep->datainterval);
+ ret = min(ep->phase >> 16, ep->maxframesize);
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ return ret;
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
{
- free_substream(&stream->substream[0]);
- free_substream(&stream->substream[1]);
- list_del(&stream->list);
- kfree(stream);
+ if (ep->retire_data_urb)
+ ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
}
-static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
{
- struct snd_usb_stream *stream = pcm->private_data;
- if (stream) {
- stream->pcm = NULL;
- snd_usb_audio_stream_free(stream);
+ struct urb *urb = urb_ctx->urb;
+
+ if (unlikely(ep->skip_packets > 0)) {
+ ep->skip_packets--;
+ return;
}
+
+ if (ep->sync_slave)
+ snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+ if (ep->retire_data_urb)
+ ep->retire_data_urb(ep->data_subs, urb);
}
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *ctx)
+{
+ int i;
+ struct urb *urb = ctx->urb;
+ unsigned char *cp = urb->transfer_buffer;
+
+ urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ if (ep->prepare_data_urb) {
+ ep->prepare_data_urb(ep->data_subs, urb);
+ } else {
+ /* no data provider, so send silence */
+ unsigned int offs = 0;
+ for (i = 0; i < ctx->packets; ++i) {
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
+ urb->iso_frame_desc[i].offset = offs * ep->stride;
+ urb->iso_frame_desc[i].length = counts * ep->stride;
+ offs += counts;
+ }
+
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * ep->stride;
+ memset(urb->transfer_buffer, ep->silence_value,
+ offs * ep->stride);
+ }
+ break;
+
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+ /*
+ * fill the length and offset of each urb descriptor.
+ * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ */
+ urb->iso_frame_desc[0].length = 4;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = ep->freqn;
+ cp[1] = ep->freqn >> 8;
+ cp[2] = ep->freqn >> 16;
+ cp[3] = ep->freqn >> 24;
+ } else {
+ /*
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+ urb->iso_frame_desc[0].length = 3;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = ep->freqn >> 2;
+ cp[1] = ep->freqn >> 10;
+ cp[2] = ep->freqn >> 18;
+ }
+
+ break;
+ }
+}
/*
- * add this endpoint to the chip instance.
- * if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
*/
-int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp)
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+ struct snd_urb_ctx *urb_ctx)
{
- struct list_head *p;
- struct snd_usb_stream *as;
- struct snd_usb_substream *subs;
- struct snd_pcm *pcm;
- int err;
+ int i, offs;
+ struct urb *urb = urb_ctx->urb;
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
- if (as->fmt_type != fp->fmt_type)
- continue;
- subs = &as->substream[stream];
- if (!subs->endpoint)
- continue;
- if (subs->endpoint == fp->endpoint) {
- list_add_tail(&fp->list, &subs->fmt_list);
- subs->num_formats++;
- subs->formats |= fp->formats;
- return 0;
+ urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ offs = 0;
+ for (i = 0; i < urb_ctx->packets; i++) {
+ urb->iso_frame_desc[i].offset = offs;
+ urb->iso_frame_desc[i].length = ep->curpacksize;
+ offs += ep->curpacksize;
}
+
+ urb->transfer_buffer_length = offs;
+ urb->number_of_packets = urb_ctx->packets;
+ break;
+
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+ urb->iso_frame_desc[0].offset = 0;
+ break;
}
- /* look for an empty stream */
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
- if (as->fmt_type != fp->fmt_type)
- continue;
- subs = &as->substream[stream];
- if (subs->endpoint)
- continue;
- err = snd_pcm_new_stream(as->pcm, stream, 1);
+}
+
+/*
+ * Send output urbs that have been prepared previously. URBs are dequeued
+ * from ep->ready_playback_urbs and in case there there aren't any available
+ * or there are no packets that have been prepared, this function does
+ * nothing.
+ *
+ * The reason why the functionality of sending and preparing URBs is separated
+ * is that host controllers don't guarantee the order in which they return
+ * inbound and outbound packets to their submitters.
+ *
+ * This function is only used for implicit feedback endpoints. For endpoints
+ * driven by dedicated sync endpoints, URBs are immediately re-submitted
+ * from their completion handler.
+ */
+static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+{
+ while (test_bit(EP_FLAG_RUNNING, &ep->flags)) {
+
+ unsigned long flags;
+ struct snd_usb_packet_info *uninitialized_var(packet);
+ struct snd_urb_ctx *ctx = NULL;
+ struct urb *urb;
+ int err, i;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ if (ep->next_packet_read_pos != ep->next_packet_write_pos) {
+ packet = ep->next_packet + ep->next_packet_read_pos;
+ ep->next_packet_read_pos++;
+ ep->next_packet_read_pos %= MAX_URBS;
+
+ /* take URB out of FIFO */
+ if (!list_empty(&ep->ready_playback_urbs))
+ ctx = list_first_entry(&ep->ready_playback_urbs,
+ struct snd_urb_ctx, ready_list);
+ }
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (ctx == NULL)
+ return;
+
+ list_del_init(&ctx->ready_list);
+ urb = ctx->urb;
+
+ /* copy over the length information */
+ for (i = 0; i < packet->packets; i++)
+ ctx->packet_size[i] = packet->packet_size[i];
+
+ /* call the data handler to fill in playback data */
+ prepare_outbound_urb(ep, ctx);
+
+ err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
if (err < 0)
- return err;
- snd_usb_init_substream(as, stream, fp);
- return 0;
+ usb_audio_err(ep->chip,
+ "Unable to submit urb #%d: %d (urb %p)\n",
+ ctx->index, err, ctx->urb);
+ else
+ set_bit(ctx->index, &ep->active_mask);
}
+}
- /* create a new pcm */
- as = kzalloc(sizeof(*as), GFP_KERNEL);
- if (!as)
- return -ENOMEM;
- as->pcm_index = chip->pcm_devs;
- as->chip = chip;
- as->fmt_type = fp->fmt_type;
- err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
- stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
- stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
- &pcm);
- if (err < 0) {
- kfree(as);
- return err;
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+ struct snd_usb_endpoint *ep = ctx->ep;
+ int err;
+
+ if (unlikely(urb->status == -ENOENT || /* unlinked */
+ urb->status == -ENODEV || /* device removed */
+ urb->status == -ECONNRESET || /* unlinked */
+ urb->status == -ESHUTDOWN || /* device disabled */
+ ep->chip->shutdown)) /* device disconnected */
+ goto exit_clear;
+
+ if (usb_pipeout(ep->pipe)) {
+ retire_outbound_urb(ep, ctx);
+ /* can be stopped during retire callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
+ if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+ spin_unlock_irqrestore(&ep->lock, flags);
+ queue_pending_output_urbs(ep);
+
+ goto exit_clear;
+ }
+
+ prepare_outbound_urb(ep, ctx);
+ } else {
+ retire_inbound_urb(ep, ctx);
+ /* can be stopped during retire callback */
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
+ prepare_inbound_urb(ep, ctx);
}
- as->pcm = pcm;
- pcm->private_data = as;
- pcm->private_free = snd_usb_audio_pcm_free;
- pcm->info_flags = 0;
- if (chip->pcm_devs > 0)
- sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err == 0)
+ return;
+
+ usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
+ //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+ clear_bit(ctx->index, &ep->active_mask);
+}
+
+/**
+ * snd_usb_add_endpoint: Add an endpoint to an USB audio chip
+ *
+ * @chip: The chip
+ * @alts: The USB host interface
+ * @ep_num: The number of the endpoint to use
+ * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC
+ *
+ * If the requested endpoint has not been added to the given chip before,
+ * a new instance is created. Otherwise, a pointer to the previoulsy
+ * created instance is returned. In case of any error, NULL is returned.
+ *
+ * New endpoints will be added to chip->ep_list and must be freed by
+ * calling snd_usb_endpoint_free().
+ */
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int ep_num, int direction, int type)
+{
+ struct snd_usb_endpoint *ep;
+ int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (WARN_ON(!alts))
+ return NULL;
+
+ mutex_lock(&chip->mutex);
+
+ list_for_each_entry(ep, &chip->ep_list, list) {
+ if (ep->ep_num == ep_num &&
+ ep->iface == alts->desc.bInterfaceNumber &&
+ ep->altsetting == alts->desc.bAlternateSetting) {
+ usb_audio_dbg(ep->chip,
+ "Re-using EP %x in iface %d,%d @%p\n",
+ ep_num, ep->iface, ep->altsetting, ep);
+ goto __exit_unlock;
+ }
+ }
+
+ usb_audio_dbg(chip, "Creating new %s %s endpoint #%x\n",
+ is_playback ? "playback" : "capture",
+ type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+ ep_num);
+
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ goto __exit_unlock;
+
+ ep->chip = chip;
+ spin_lock_init(&ep->lock);
+ ep->type = type;
+ ep->ep_num = ep_num;
+ ep->iface = alts->desc.bInterfaceNumber;
+ ep->altsetting = alts->desc.bAlternateSetting;
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+ if (is_playback)
+ ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
else
- strcpy(pcm->name, "USB Audio");
+ ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+ if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+ if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ get_endpoint(alts, 1)->bRefresh >= 1 &&
+ get_endpoint(alts, 1)->bRefresh <= 9)
+ ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+ else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+ ep->syncinterval = 1;
+ else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+ get_endpoint(alts, 1)->bInterval <= 16)
+ ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+ else
+ ep->syncinterval = 3;
- snd_usb_init_substream(as, stream, fp);
+ ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
- list_add(&as->list, &chip->pcm_list);
- chip->pcm_devs++;
+ if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ &&
+ ep->syncmaxsize == 4)
+ ep->udh01_fb_quirk = 1;
+ }
+
+ list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+ mutex_unlock(&chip->mutex);
+
+ return ep;
+}
+
+/*
+ * wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+ int alive;
+
+ do {
+ alive = bitmap_weight(&ep->active_mask, ep->nurbs);
+ if (!alive)
+ break;
+
+ schedule_timeout_uninterruptible(1);
+ } while (time_before(jiffies, end_time));
- snd_usb_proc_pcm_format_add(as);
+ if (alive)
+ usb_audio_err(ep->chip,
+ "timeout: still %d active urbs on EP #%x\n",
+ alive, ep->ep_num);
+ clear_bit(EP_FLAG_STOPPING, &ep->flags);
return 0;
}
-static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
- struct usb_host_interface *alts,
- int protocol, int iface_no)
+/* sync the pending stop operation;
+ * this function itself doesn't trigger the stop operation
+ */
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
{
- /* parsed with a v1 header here. that's ok as we only look at the
- * header first which is the same for both versions */
- struct uac_iso_endpoint_descriptor *csep;
- struct usb_interface_descriptor *altsd = get_iface_desc(alts);
- int attributes = 0;
-
- csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
-
- /* Creamware Noah has this descriptor after the 2nd endpoint */
- if (!csep && altsd->bNumEndpoints >= 2)
- csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
-
- if (!csep || csep->bLength < 7 ||
- csep->bDescriptorSubtype != UAC_EP_GENERAL) {
- snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
- " class specific endpoint descriptor\n",
- chip->dev->devnum, iface_no,
- altsd->bAlternateSetting);
- return 0;
+ if (ep && test_bit(EP_FLAG_STOPPING, &ep->flags))
+ wait_clear_urbs(ep);
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force)
+{
+ unsigned int i;
+
+ if (!force && ep->chip->shutdown) /* to be sure... */
+ return -EBADFD;
+
+ clear_bit(EP_FLAG_RUNNING, &ep->flags);
+
+ INIT_LIST_HEAD(&ep->ready_playback_urbs);
+ ep->next_packet_read_pos = 0;
+ ep->next_packet_write_pos = 0;
+
+ for (i = 0; i < ep->nurbs; i++) {
+ if (test_bit(i, &ep->active_mask)) {
+ if (!test_and_set_bit(i, &ep->unlink_mask)) {
+ struct urb *u = ep->urb[i].urb;
+ usb_unlink_urb(u);
+ }
+ }
}
- if (protocol == UAC_VERSION_1) {
- attributes = csep->bmAttributes;
+ return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+ int i;
+
+ /* route incoming urbs to nirvana */
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
+ /* stop urbs */
+ deactivate_urbs(ep, force);
+ wait_clear_urbs(ep);
+
+ for (i = 0; i < ep->nurbs; i++)
+ release_urb_ctx(&ep->urb[i]);
+
+ if (ep->syncbuf)
+ usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+ ep->syncbuf, ep->sync_dma);
+
+ ep->syncbuf = NULL;
+ ep->nurbs = 0;
+}
+
+/*
+ * configure a data endpoint
+ */
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int frames_per_period,
+ unsigned int periods_per_buffer,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep)
+{
+ unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb;
+ unsigned int max_packs_per_period, urbs_per_period, urb_packs;
+ unsigned int max_urbs, i;
+ int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+
+ if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
+ /*
+ * When operating in DSD DOP mode, the size of a sample frame
+ * in hardware differs from the actual physical format width
+ * because we need to make room for the DOP markers.
+ */
+ frame_bits += channels << 3;
+ }
+
+ ep->datainterval = fmt->datainterval;
+ ep->stride = frame_bits >> 3;
+ ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+ /* assume max. frequency is 25% higher than nominal */
+ ep->freqmax = ep->freqn + (ep->freqn >> 2);
+ maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+ >> (16 - ep->datainterval);
+ /* but wMaxPacketSize might reduce this */
+ if (ep->maxpacksize && ep->maxpacksize < maxsize) {
+ /* whatever fits into a max. size packet */
+ maxsize = ep->maxpacksize;
+ ep->freqmax = (maxsize / (frame_bits >> 3))
+ << (16 - ep->datainterval);
+ }
+
+ if (ep->fill_max)
+ ep->curpacksize = ep->maxpacksize;
+ else
+ ep->curpacksize = maxsize;
+
+ if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) {
+ packs_per_ms = 8 >> ep->datainterval;
+ max_packs_per_urb = MAX_PACKS_HS;
} else {
- struct uac2_iso_endpoint_descriptor *csep2 =
- (struct uac2_iso_endpoint_descriptor *) csep;
+ packs_per_ms = 1;
+ max_packs_per_urb = MAX_PACKS;
+ }
+ if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
+ max_packs_per_urb = min(max_packs_per_urb,
+ 1U << sync_ep->syncinterval);
+ max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval);
+
+ /*
+ * Capture endpoints need to use small URBs because there's no way
+ * to tell in advance where the next period will end, and we don't
+ * want the next URB to complete much after the period ends.
+ *
+ * Playback endpoints with implicit sync much use the same parameters
+ * as their corresponding capture endpoint.
+ */
+ if (usb_pipein(ep->pipe) ||
+ snd_usb_endpoint_implicit_feedback_sink(ep)) {
- attributes = csep->bmAttributes & UAC_EP_CS_ATTR_FILL_MAX;
+ urb_packs = packs_per_ms;
+ /*
+ * Wireless devices can poll at a max rate of once per 4ms.
+ * For dataintervals less than 5, increase the packet count to
+ * allow the host controller to use bursting to fill in the
+ * gaps.
+ */
+ if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) {
+ int interval = ep->datainterval;
+ while (interval < 5) {
+ urb_packs <<= 1;
+ ++interval;
+ }
+ }
+ /* make capture URBs <= 1 ms and smaller than a period */
+ urb_packs = min(max_packs_per_urb, urb_packs);
+ while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+ urb_packs >>= 1;
+ ep->nurbs = MAX_URBS;
- /* emulate the endpoint attributes of a v1 device */
- if (csep2->bmControls & UAC2_CONTROL_PITCH)
- attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
+ /*
+ * Playback endpoints without implicit sync are adjusted so that
+ * a period fits as evenly as possible in the smallest number of
+ * URBs. The total number of URBs is adjusted to the size of the
+ * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits.
+ */
+ } else {
+ /* determine how small a packet can be */
+ minsize = (ep->freqn >> (16 - ep->datainterval)) *
+ (frame_bits >> 3);
+ /* with sync from device, assume it can be 12% lower */
+ if (sync_ep)
+ minsize -= minsize >> 3;
+ minsize = max(minsize, 1u);
+
+ /* how many packets will contain an entire ALSA period? */
+ max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize);
+
+ /* how many URBs will contain a period? */
+ urbs_per_period = DIV_ROUND_UP(max_packs_per_period,
+ max_packs_per_urb);
+ /* how many packets are needed in each URB? */
+ urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period);
+
+ /* limit the number of frames in a single URB */
+ ep->max_urb_frames = DIV_ROUND_UP(frames_per_period,
+ urbs_per_period);
+
+ /* try to use enough URBs to contain an entire ALSA buffer */
+ max_urbs = min((unsigned) MAX_URBS,
+ MAX_QUEUE * packs_per_ms / urb_packs);
+ ep->nurbs = min(max_urbs, urbs_per_period * periods_per_buffer);
}
- return attributes;
+ /* allocate and initialize data urbs */
+ for (i = 0; i < ep->nurbs; i++) {
+ struct snd_urb_ctx *u = &ep->urb[i];
+ u->index = i;
+ u->ep = ep;
+ u->packets = urb_packs;
+ u->buffer_size = maxsize * u->packets;
+
+ if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+ u->packets++; /* for transfer delimiter */
+ u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+ if (!u->urb)
+ goto out_of_memory;
+
+ u->urb->transfer_buffer =
+ usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+ GFP_KERNEL, &u->urb->transfer_dma);
+ if (!u->urb->transfer_buffer)
+ goto out_of_memory;
+ u->urb->pipe = ep->pipe;
+ u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ u->urb->interval = 1 << ep->datainterval;
+ u->urb->context = u;
+ u->urb->complete = snd_complete_urb;
+ INIT_LIST_HEAD(&u->ready_list);
+ }
+
+ return 0;
+
+out_of_memory:
+ release_urbs(ep, 0);
+ return -ENOMEM;
}
-static struct uac2_input_terminal_descriptor *
- snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+/*
+ * configure a sync endpoint
+ */
+static int sync_ep_set_params(struct snd_usb_endpoint *ep)
{
- struct uac2_input_terminal_descriptor *term = NULL;
+ int i;
- while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
- ctrl_iface->extralen,
- term, UAC_INPUT_TERMINAL))) {
- if (term->bTerminalID == terminal_id)
- return term;
+ ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+ GFP_KERNEL, &ep->sync_dma);
+ if (!ep->syncbuf)
+ return -ENOMEM;
+
+ for (i = 0; i < SYNC_URBS; i++) {
+ struct snd_urb_ctx *u = &ep->urb[i];
+ u->index = i;
+ u->ep = ep;
+ u->packets = 1;
+ u->urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!u->urb)
+ goto out_of_memory;
+ u->urb->transfer_buffer = ep->syncbuf + i * 4;
+ u->urb->transfer_dma = ep->sync_dma + i * 4;
+ u->urb->transfer_buffer_length = 4;
+ u->urb->pipe = ep->pipe;
+ u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ u->urb->number_of_packets = 1;
+ u->urb->interval = 1 << ep->syncinterval;
+ u->urb->context = u;
+ u->urb->complete = snd_complete_urb;
}
- return NULL;
+ ep->nurbs = SYNC_URBS;
+
+ return 0;
+
+out_of_memory:
+ release_urbs(ep, 0);
+ return -ENOMEM;
}
-static struct uac2_output_terminal_descriptor *
- snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+/**
+ * snd_usb_endpoint_set_params: configure an snd_usb_endpoint
+ *
+ * @ep: the snd_usb_endpoint to configure
+ * @pcm_format: the audio fomat.
+ * @channels: the number of audio channels.
+ * @period_bytes: the number of bytes in one alsa period.
+ * @period_frames: the number of frames in one alsa period.
+ * @buffer_periods: the number of periods in one alsa buffer.
+ * @rate: the frame rate.
+ * @fmt: the USB audio format information
+ * @sync_ep: the sync endpoint to use, if any
+ *
+ * Determine the number of URBs to be used on this endpoint.
+ * An endpoint must be configured before it can be started.
+ * An endpoint that is already running can not be reconfigured.
+ */
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int period_frames,
+ unsigned int buffer_periods,
+ unsigned int rate,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep)
{
- struct uac2_output_terminal_descriptor *term = NULL;
+ int err;
+
+ if (ep->use_count != 0) {
+ usb_audio_warn(ep->chip,
+ "Unable to change format on ep #%x: already in use\n",
+ ep->ep_num);
+ return -EBUSY;
+ }
+
+ /* release old buffers, if any */
+ release_urbs(ep, 0);
+
+ ep->datainterval = fmt->datainterval;
+ ep->maxpacksize = fmt->maxpacksize;
+ ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
+
+ if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+ ep->freqn = get_usb_full_speed_rate(rate);
+ else
+ ep->freqn = get_usb_high_speed_rate(rate);
+
+ /* calculate the frequency in 16.16 format */
+ ep->freqm = ep->freqn;
+ ep->freqshift = INT_MIN;
+
+ ep->phase = 0;
- while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
- ctrl_iface->extralen,
- term, UAC_OUTPUT_TERMINAL))) {
- if (term->bTerminalID == terminal_id)
- return term;
+ switch (ep->type) {
+ case SND_USB_ENDPOINT_TYPE_DATA:
+ err = data_ep_set_params(ep, pcm_format, channels,
+ period_bytes, period_frames,
+ buffer_periods, fmt, sync_ep);
+ break;
+ case SND_USB_ENDPOINT_TYPE_SYNC:
+ err = sync_ep_set_params(ep);
+ break;
+ default:
+ err = -EINVAL;
}
- return NULL;
+ usb_audio_dbg(ep->chip,
+ "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+ ep->ep_num, ep->type, ep->nurbs, err);
+
+ return err;
}
-int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
+/**
+ * snd_usb_endpoint_start: start an snd_usb_endpoint
+ *
+ * @ep: the endpoint to start
+ * @can_sleep: flag indicating whether the operation is executed in
+ * non-atomic context
+ *
+ * A call to this function will increment the use count of the endpoint.
+ * In case it is not already running, the URBs for this endpoint will be
+ * submitted. Otherwise, this function does nothing.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_stop().
+ *
+ * Returns an error if the URB submission failed, 0 in all other cases.
+ */
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
{
- struct usb_device *dev;
- struct usb_interface *iface;
- struct usb_host_interface *alts;
- struct usb_interface_descriptor *altsd;
- int i, altno, err, stream;
- int format = 0, num_channels = 0;
- struct audioformat *fp = NULL;
- int num, protocol, clock = 0;
- struct uac_format_type_i_continuous_descriptor *fmt;
+ int err;
+ unsigned int i;
- dev = chip->dev;
+ if (ep->chip->shutdown)
+ return -EBADFD;
- /* parse the interface's altsettings */
- iface = usb_ifnum_to_if(dev, iface_no);
+ /* already running? */
+ if (++ep->use_count != 1)
+ return 0;
+
+ /* just to be sure */
+ deactivate_urbs(ep, false);
+ if (can_sleep)
+ wait_clear_urbs(ep);
+
+ ep->active_mask = 0;
+ ep->unlink_mask = 0;
+ ep->phase = 0;
- num = iface->num_altsetting;
+ snd_usb_endpoint_start_quirk(ep);
/*
- * Dallas DS4201 workaround: It presents 5 altsettings, but the last
- * one misses syncpipe, and does not produce any sound.
+ * If this endpoint has a data endpoint as implicit feedback source,
+ * don't start the urbs here. Instead, mark them all as available,
+ * wait for the record urbs to return and queue the playback urbs
+ * from that context.
*/
- if (chip->usb_id == USB_ID(0x04fa, 0x4201))
- num = 4;
-
- for (i = 0; i < num; i++) {
- alts = &iface->altsetting[i];
- altsd = get_iface_desc(alts);
- protocol = altsd->bInterfaceProtocol;
- /* skip invalid one */
- if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
- altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
- (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
- altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
- altsd->bNumEndpoints < 1 ||
- le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
- continue;
- /* must be isochronous */
- if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_ISOC)
- continue;
- /* check direction */
- stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
- SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
- altno = altsd->bAlternateSetting;
-
- if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
- continue;
-
- /* get audio formats */
- switch (protocol) {
- case UAC_VERSION_1: {
- struct uac_as_header_descriptor_v1 *as =
- snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
- if (!as) {
- snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
- dev->devnum, iface_no, altno);
- continue;
- }
- if (as->bLength < sizeof(*as)) {
- snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
- dev->devnum, iface_no, altno);
- continue;
- }
+ set_bit(EP_FLAG_RUNNING, &ep->flags);
- format = le16_to_cpu(as->wFormatTag); /* remember the format value */
- break;
+ if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
+ for (i = 0; i < ep->nurbs; i++) {
+ struct snd_urb_ctx *ctx = ep->urb + i;
+ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
}
- case UAC_VERSION_2: {
- struct uac2_input_terminal_descriptor *input_term;
- struct uac2_output_terminal_descriptor *output_term;
- struct uac_as_header_descriptor_v2 *as =
- snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ return 0;
+ }
- if (!as) {
- snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
- dev->devnum, iface_no, altno);
- continue;
- }
+ for (i = 0; i < ep->nurbs; i++) {
+ struct urb *urb = ep->urb[i].urb;
- if (as->bLength < sizeof(*as)) {
- snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
- dev->devnum, iface_no, altno);
- continue;
- }
+ if (snd_BUG_ON(!urb))
+ goto __error;
- num_channels = as->bNrChannels;
- format = le32_to_cpu(as->bmFormats);
+ if (usb_pipeout(ep->pipe)) {
+ prepare_outbound_urb(ep, urb->context);
+ } else {
+ prepare_inbound_urb(ep, urb->context);
+ }
- /* lookup the terminal associated to this interface
- * to extract the clock */
- input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (input_term) {
- clock = input_term->bCSourceID;
- break;
- }
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ usb_audio_err(ep->chip,
+ "cannot submit urb %d, error %d: %s\n",
+ i, err, usb_error_string(err));
+ goto __error;
+ }
+ set_bit(i, &ep->active_mask);
+ }
- output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
- if (output_term) {
- clock = output_term->bCSourceID;
- break;
- }
+ return 0;
- snd_printk(KERN_ERR "%d:%u:%d : bogus bTerminalLink %d\n",
- dev->devnum, iface_no, altno, as->bTerminalLink);
- continue;
- }
+__error:
+ clear_bit(EP_FLAG_RUNNING, &ep->flags);
+ ep->use_count--;
+ deactivate_urbs(ep, false);
+ return -EPIPE;
+}
- default:
- snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
- dev->devnum, iface_no, altno, protocol);
- continue;
- }
+/**
+ * snd_usb_endpoint_stop: stop an snd_usb_endpoint
+ *
+ * @ep: the endpoint to stop (may be NULL)
+ *
+ * A call to this function will decrement the use count of the endpoint.
+ * In case the last user has requested the endpoint stop, the URBs will
+ * actually be deactivated.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_start().
+ *
+ * The caller needs to synchronize the pending stop operation via
+ * snd_usb_endpoint_sync_pending_stop().
+ */
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
+{
+ if (!ep)
+ return;
- /* get format type */
- fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
- if (!fmt) {
- snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
- dev->devnum, iface_no, altno);
- continue;
- }
- if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
- ((protocol == UAC_VERSION_2) && (fmt->bLength != 6))) {
- snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
- dev->devnum, iface_no, altno);
- continue;
- }
+ if (snd_BUG_ON(ep->use_count == 0))
+ return;
+
+ if (--ep->use_count == 0) {
+ deactivate_urbs(ep, false);
+ ep->data_subs = NULL;
+ ep->sync_slave = NULL;
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+ set_bit(EP_FLAG_STOPPING, &ep->flags);
+ }
+}
+
+/**
+ * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to deactivate
+ *
+ * If the endpoint is not currently in use, this functions will
+ * deactivate its associated URBs.
+ *
+ * In case of any active users, this functions does nothing.
+ */
+void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
+{
+ if (!ep)
+ return;
+
+ if (ep->use_count != 0)
+ return;
+
+ deactivate_urbs(ep, true);
+ wait_clear_urbs(ep);
+}
+
+/**
+ * snd_usb_endpoint_release: Tear down an snd_usb_endpoint
+ *
+ * @ep: the endpoint to release
+ *
+ * This function does not care for the endpoint's use count but will tear
+ * down all the streaming URBs immediately.
+ */
+void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
+{
+ release_urbs(ep, 1);
+}
+
+/**
+ * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
+ *
+ * @ep: the list header of the endpoint to free
+ *
+ * This free all resources of the given ep.
+ */
+void snd_usb_endpoint_free(struct list_head *head)
+{
+ struct snd_usb_endpoint *ep;
+
+ ep = list_entry(head, struct snd_usb_endpoint, list);
+ kfree(ep);
+}
+
+/**
+ * snd_usb_handle_sync_urb: parse an USB sync packet
+ *
+ * @ep: the endpoint to handle the packet
+ * @sender: the sending endpoint
+ * @urb: the received packet
+ *
+ * This function is called from the context of an endpoint that received
+ * the packet and is used to let another endpoint object handle the payload.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+ struct snd_usb_endpoint *sender,
+ const struct urb *urb)
+{
+ int shift;
+ unsigned int f;
+ unsigned long flags;
+
+ snd_BUG_ON(ep == sender);
+
+ /*
+ * In case the endpoint is operating in implicit feedback mode, prepare
+ * a new outbound URB that has the same layout as the received packet
+ * and add it to the list of pending urbs. queue_pending_output_urbs()
+ * will take care of them later.
+ */
+ if (snd_usb_endpoint_implicit_feedback_sink(ep) &&
+ ep->use_count != 0) {
+
+ /* implicit feedback case */
+ int i, bytes = 0;
+ struct snd_urb_ctx *in_ctx;
+ struct snd_usb_packet_info *out_packet;
+
+ in_ctx = urb->context;
+
+ /* Count overall packet size */
+ for (i = 0; i < in_ctx->packets; i++)
+ if (urb->iso_frame_desc[i].status == 0)
+ bytes += urb->iso_frame_desc[i].actual_length;
/*
- * Blue Microphones workaround: The last altsetting is identical
- * with the previous one, except for a larger packet size, but
- * is actually a mislabeled two-channel setting; ignore it.
+ * skip empty packets. At least M-Audio's Fast Track Ultra stops
+ * streaming once it received a 0-byte OUT URB
*/
- if (fmt->bNrChannels == 1 &&
- fmt->bSubframeSize == 2 &&
- altno == 2 && num == 3 &&
- fp && fp->altsetting == 1 && fp->channels == 1 &&
- fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
- protocol == UAC_VERSION_1 &&
- le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
- fp->maxpacksize * 2)
- continue;
-
- fp = kzalloc(sizeof(*fp), GFP_KERNEL);
- if (! fp) {
- snd_printk(KERN_ERR "cannot malloc\n");
- return -ENOMEM;
- }
+ if (bytes == 0)
+ return;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ out_packet = ep->next_packet + ep->next_packet_write_pos;
- fp->iface = iface_no;
- fp->altsetting = altno;
- fp->altset_idx = i;
- fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
- fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
- fp->datainterval = snd_usb_parse_datainterval(chip, alts);
- fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
- /* num_channels is only set for v2 interfaces */
- fp->channels = num_channels;
- if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
- fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
- * (fp->maxpacksize & 0x7ff);
- fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
- fp->clock = clock;
-
- /* some quirks for attributes here */
-
- switch (chip->usb_id) {
- case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
- /* Optoplay sets the sample rate attribute although
- * it seems not supporting it in fact.
- */
- fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
- break;
- case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
- case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
- case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
- case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
- /* doesn't set the sample rate attribute, but supports it */
- fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
- break;
- case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
- case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
- an older model 77d:223) */
/*
- * plantronics headset and Griffin iMic have set adaptive-in
- * although it's really not...
+ * Iterate through the inbound packet and prepare the lengths
+ * for the output packet. The OUT packet we are about to send
+ * will have the same amount of payload bytes per stride as the
+ * IN packet we just received. Since the actual size is scaled
+ * by the stride, use the sender stride to calculate the length
+ * in case the number of channels differ between the implicitly
+ * fed-back endpoint and the synchronizing endpoint.
*/
- fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
+
+ out_packet->packets = in_ctx->packets;
+ for (i = 0; i < in_ctx->packets; i++) {
+ if (urb->iso_frame_desc[i].status == 0)
+ out_packet->packet_size[i] =
+ urb->iso_frame_desc[i].actual_length / sender->stride;
else
- fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
- break;
+ out_packet->packet_size[i] = 0;
}
- /* ok, let's parse further... */
- if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
- fp = NULL;
- continue;
- }
+ ep->next_packet_write_pos++;
+ ep->next_packet_write_pos %= MAX_URBS;
+ spin_unlock_irqrestore(&ep->lock, flags);
+ queue_pending_output_urbs(ep);
- snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
- err = snd_usb_add_audio_endpoint(chip, stream, fp);
- if (err < 0) {
- kfree(fp->rate_table);
- kfree(fp);
- return err;
+ return;
+ }
+
+ /*
+ * process after playback sync complete
+ *
+ * Full speed devices report feedback values in 10.14 format as samples
+ * per frame, high speed devices in 16.16 format as samples per
+ * microframe.
+ *
+ * Because the Audio Class 1 spec was written before USB 2.0, many high
+ * speed devices use a wrong interpretation, some others use an
+ * entirely different format.
+ *
+ * Therefore, we cannot predict what format any particular device uses
+ * and must detect it automatically.
+ */
+
+ if (urb->iso_frame_desc[0].status != 0 ||
+ urb->iso_frame_desc[0].actual_length < 3)
+ return;
+
+ f = le32_to_cpup(urb->transfer_buffer);
+ if (urb->iso_frame_desc[0].actual_length == 3)
+ f &= 0x00ffffff;
+ else
+ f &= 0x0fffffff;
+
+ if (f == 0)
+ return;
+
+ if (unlikely(sender->udh01_fb_quirk)) {
+ /*
+ * The TEAC UD-H01 firmware sometimes changes the feedback value
+ * by +/- 0x1.0000.
+ */
+ if (f < ep->freqn - 0x8000)
+ f += 0x10000;
+ else if (f > ep->freqn + 0x8000)
+ f -= 0x10000;
+ } else if (unlikely(ep->freqshift == INT_MIN)) {
+ /*
+ * The first time we see a feedback value, determine its format
+ * by shifting it left or right until it matches the nominal
+ * frequency value. This assumes that the feedback does not
+ * differ from the nominal value more than +50% or -25%.
+ */
+ shift = 0;
+ while (f < ep->freqn - ep->freqn / 4) {
+ f <<= 1;
+ shift++;
}
- /* try to set the interface... */
- usb_set_interface(chip->dev, iface_no, altno);
- snd_usb_init_pitch(chip, iface_no, alts, fp);
- snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
+ while (f > ep->freqn + ep->freqn / 2) {
+ f >>= 1;
+ shift--;
+ }
+ ep->freqshift = shift;
+ } else if (ep->freqshift >= 0)
+ f <<= ep->freqshift;
+ else
+ f >>= -ep->freqshift;
+
+ if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+ /*
+ * If the frequency looks valid, set it.
+ * This value is referred to in prepare_playback_urb().
+ */
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->freqm = f;
+ spin_unlock_irqrestore(&ep->lock, flags);
+ } else {
+ /*
+ * Out of range; maybe the shift value is wrong.
+ * Reset it so that we autodetect again the next time.
+ */
+ ep->freqshift = INT_MIN;
}
- return 0;
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 64dd0db023b..e61ee5c356a 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -1,11 +1,36 @@
#ifndef __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
-int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip,
- int iface_no);
+#define SND_USB_ENDPOINT_TYPE_DATA 0
+#define SND_USB_ENDPOINT_TYPE_SYNC 1
-int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip,
- int stream,
- struct audioformat *fp);
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int ep_num, int direction, int type);
+
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int period_frames,
+ unsigned int buffer_periods,
+ unsigned int rate,
+ struct audioformat *fmt,
+ struct snd_usb_endpoint *sync_ep);
+
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
+
+int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+ struct snd_usb_endpoint *sender,
+ const struct urb *urb);
#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 30364aba79c..8bcc87cf566 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -30,6 +30,7 @@
#include "helper.h"
#include "debug.h"
#include "clock.h"
+#include "format.h"
/*
* parse the audio format type I descriptor
@@ -42,14 +43,14 @@
*/
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp,
- int format, void *_fmt,
- int protocol)
+ unsigned int format, void *_fmt)
{
int sample_width, sample_bytes;
- u64 pcm_formats;
+ u64 pcm_formats = 0;
- switch (protocol) {
- case UAC_VERSION_1: {
+ switch (fp->protocol) {
+ case UAC_VERSION_1:
+ default: {
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubframeSize;
@@ -61,27 +62,30 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct uac_format_type_i_ext_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubslotSize;
+
+ if (format & UAC2_FORMAT_TYPE_I_RAW_DATA)
+ pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+
format <<= 1;
break;
}
-
- default:
- return -EINVAL;
}
- pcm_formats = 0;
-
- if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
+ if ((pcm_formats == 0) &&
+ (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
/* some devices don't define this correctly... */
- snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
- chip->dev->devnum, fp->iface, fp->altsetting);
+ usb_audio_info(chip, "%u:%d : format type 0 is detected, processed as PCM\n",
+ fp->iface, fp->altsetting);
format = 1 << UAC_FORMAT_TYPE_I_PCM;
}
if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
- if (sample_width > sample_bytes * 8) {
- snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
- chip->dev->devnum, fp->iface, fp->altsetting,
- sample_width, sample_bytes);
+ if (chip->usb_id == USB_ID(0x0582, 0x0016) /* Edirol SD-90 */ &&
+ sample_width == 24 && sample_bytes == 2)
+ sample_bytes = 3;
+ else if (sample_width > sample_bytes * 8) {
+ usb_audio_info(chip, "%u:%d : sample bitwidth %d in over sample bytes %d\n",
+ fp->iface, fp->altsetting,
+ sample_width, sample_bytes);
}
/* check the format byte size */
switch (sample_bytes) {
@@ -104,9 +108,10 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
break;
default:
- snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
- chip->dev->devnum, fp->iface, fp->altsetting,
- sample_width, sample_bytes);
+ usb_audio_info(chip,
+ "%u:%d : unsupported sample bitwidth %d in %d bytes\n",
+ fp->iface, fp->altsetting,
+ sample_width, sample_bytes);
break;
}
}
@@ -128,9 +133,13 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
}
if (format & ~0x3f) {
- snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
- chip->dev->devnum, fp->iface, fp->altsetting, format);
+ usb_audio_info(chip,
+ "%u:%d : unsupported format bits %#x\n",
+ fp->iface, fp->altsetting, format);
}
+
+ pcm_formats |= snd_usb_interface_dsd_format_quirks(chip, fp, sample_bytes);
+
return pcm_formats;
}
@@ -151,9 +160,10 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
int nr_rates = fmt[offset];
if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
- snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
- chip->dev->devnum, fp->iface, fp->altsetting);
- return -1;
+ usb_audio_err(chip,
+ "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+ fp->iface, fp->altsetting);
+ return -EINVAL;
}
if (nr_rates) {
@@ -164,8 +174,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
if (fp->rate_table == NULL) {
- snd_printk(KERN_ERR "cannot malloc\n");
- return -1;
+ usb_audio_err(chip, "cannot malloc\n");
+ return -ENOMEM;
}
fp->nr_rates = 0;
@@ -175,13 +185,17 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
if (!rate)
continue;
/* C-Media CM6501 mislabels its 96 kHz altsetting */
+ /* Terratec Aureon 7.1 USB C-Media 6206, too */
if (rate == 48000 && nr_rates == 1 &&
(chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
- chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
+ chip->usb_id == USB_ID(0x0d8c, 0x0102) ||
+ chip->usb_id == USB_ID(0x0ccd, 0x00b1)) &&
fp->altsetting == 5 && fp->maxpacksize == 392)
rate = 96000;
- /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
- if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
+ /* Creative VF0420/VF0470 Live Cams report 16 kHz instead of 8kHz */
+ if (rate == 16000 &&
+ (chip->usb_id == USB_ID(0x041e, 0x4064) ||
+ chip->usb_id == USB_ID(0x041e, 0x4068)))
rate = 8000;
fp->rate_table[fp->nr_rates] = rate;
@@ -194,7 +208,7 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
}
if (!fp->nr_rates) {
hwc_debug("All rates were zero. Skipping format!\n");
- return -1;
+ return -EINVAL;
}
} else {
/* continuous rates */
@@ -211,7 +225,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
* get to know how many sample rates we have to expect.
* Then fp->rate_table can be allocated and filled.
*/
-static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
+static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
+ struct audioformat *fp, int nr_triplets,
const unsigned char *data)
{
int i, nr_rates = 0;
@@ -222,7 +237,7 @@ static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
int min = combine_quad(&data[2 + 12 * i]);
int max = combine_quad(&data[6 + 12 * i]);
int res = combine_quad(&data[10 + 12 * i]);
- int rate;
+ unsigned int rate;
if ((max < 0) || (min < 0) || (res < 0) || (max < min))
continue;
@@ -249,6 +264,10 @@ static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
fp->rates |= snd_pcm_rate_to_rate_bit(rate);
nr_rates++;
+ if (nr_rates >= MAX_NR_RATES) {
+ usb_audio_err(chip, "invalid uac2 rates\n");
+ break;
+ }
/* avoid endless loop */
if (res == 0)
@@ -264,16 +283,16 @@ static int parse_uac2_sample_rate_range(struct audioformat *fp, int nr_triplets,
* on the audioformat table (audio class v2).
*/
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
- struct audioformat *fp,
- struct usb_host_interface *iface)
+ struct audioformat *fp)
{
struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0;
- int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
+ int clock = snd_usb_clock_find_source(chip, fp->clock, false);
if (clock < 0) {
- snd_printk(KERN_ERR "%s(): unable to find clock source (clock %d)\n",
+ dev_err(&dev->dev,
+ "%s(): unable to find clock source (clock %d)\n",
__func__, clock);
goto err;
}
@@ -283,10 +302,11 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
snd_usb_ctrl_intf(chip) | (clock << 8),
- tmp, sizeof(tmp), 1000);
+ tmp, sizeof(tmp));
if (ret < 0) {
- snd_printk(KERN_ERR "%s(): unable to retrieve number of sample rates (clock %d)\n",
+ dev_err(&dev->dev,
+ "%s(): unable to retrieve number of sample rates (clock %d)\n",
__func__, clock);
goto err;
}
@@ -304,10 +324,11 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
snd_usb_ctrl_intf(chip) | (clock << 8),
- data, data_size, 1000);
+ data, data_size);
if (ret < 0) {
- snd_printk(KERN_ERR "%s(): unable to retrieve sample rate range (clock %d)\n",
+ dev_err(&dev->dev,
+ "%s(): unable to retrieve sample rate range (clock %d)\n",
__func__, clock);
ret = -EINVAL;
goto err_free;
@@ -318,7 +339,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
* will have to deal with. */
kfree(fp->rate_table);
fp->rate_table = NULL;
- fp->nr_rates = parse_uac2_sample_rate_range(fp, nr_triplets, data);
+ fp->nr_rates = parse_uac2_sample_rate_range(chip, fp, nr_triplets, data);
if (fp->nr_rates == 0) {
/* SNDRV_PCM_RATE_CONTINUOUS */
@@ -334,7 +355,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
/* Call the triplet parser again, but this time, fp->rate_table is
* allocated, so the rates will be stored */
- parse_uac2_sample_rate_range(fp, nr_triplets, data);
+ parse_uac2_sample_rate_range(chip, fp, nr_triplets, data);
err_free:
kfree(data);
@@ -346,13 +367,11 @@ err:
* parse the format type I and III descriptors
*/
static int parse_audio_format_i(struct snd_usb_audio *chip,
- struct audioformat *fp, int format,
- struct uac_format_type_i_continuous_descriptor *fmt,
- struct usb_host_interface *iface)
+ struct audioformat *fp, unsigned int format,
+ struct uac_format_type_i_continuous_descriptor *fmt)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(iface);
- int protocol = altsd->bInterfaceProtocol;
- int pcm_format, ret;
+ snd_pcm_format_t pcm_format;
+ int ret;
if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
/* FIXME: the format type is really IECxxx
@@ -371,12 +390,11 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
default:
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
}
- fp->formats = 1uLL << pcm_format;
+ fp->formats = pcm_format_to_bits(pcm_format);
} else {
- fp->formats = parse_audio_format_i_type(chip, fp, format,
- fmt, protocol);
+ fp->formats = parse_audio_format_i_type(chip, fp, format, fmt);
if (!fp->formats)
- return -1;
+ return -EINVAL;
}
/* gather possible sample rates */
@@ -384,21 +402,23 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
* proprietary class specific descriptor.
* audio class v2 uses class specific EP0 range requests for that.
*/
- switch (protocol) {
+ switch (fp->protocol) {
+ default:
case UAC_VERSION_1:
fp->channels = fmt->bNrChannels;
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
break;
case UAC_VERSION_2:
/* fp->channels is already set in this case */
- ret = parse_audio_format_rates_v2(chip, fp, iface);
+ ret = parse_audio_format_rates_v2(chip, fp);
break;
}
if (fp->channels < 1) {
- snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
- chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
- return -1;
+ usb_audio_err(chip,
+ "%u:%d : invalid channels %d\n",
+ fp->iface, fp->altsetting, fp->channels);
+ return -EINVAL;
}
return ret;
@@ -409,12 +429,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/
static int parse_audio_format_ii(struct snd_usb_audio *chip,
struct audioformat *fp,
- int format, void *_fmt,
- struct usb_host_interface *iface)
+ int format, void *_fmt)
{
int brate, framesize, ret;
- struct usb_interface_descriptor *altsd = get_iface_desc(iface);
- int protocol = altsd->bInterfaceProtocol;
switch (format) {
case UAC_FORMAT_TYPE_II_AC3:
@@ -426,20 +443,22 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
fp->formats = SNDRV_PCM_FMTBIT_MPEG;
break;
default:
- snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected. processed as MPEG.\n",
- chip->dev->devnum, fp->iface, fp->altsetting, format);
+ usb_audio_info(chip,
+ "%u:%d : unknown format tag %#x is detected. processed as MPEG.\n",
+ fp->iface, fp->altsetting, format);
fp->formats = SNDRV_PCM_FMTBIT_MPEG;
break;
}
fp->channels = 1;
- switch (protocol) {
+ switch (fp->protocol) {
+ default:
case UAC_VERSION_1: {
struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
brate = le16_to_cpu(fmt->wMaxBitRate);
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
- snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+ usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize;
ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
break;
@@ -448,9 +467,9 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
brate = le16_to_cpu(fmt->wMaxBitRate);
framesize = le16_to_cpu(fmt->wSamplesPerFrame);
- snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+ usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize;
- ret = parse_audio_format_rates_v2(chip, fp, iface);
+ ret = parse_audio_format_rates_v2(chip, fp);
break;
}
}
@@ -458,24 +477,26 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
return ret;
}
-int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
- int format, struct uac_format_type_i_continuous_descriptor *fmt,
- int stream, struct usb_host_interface *iface)
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
+ struct audioformat *fp, unsigned int format,
+ struct uac_format_type_i_continuous_descriptor *fmt,
+ int stream)
{
int err;
switch (fmt->bFormatType) {
case UAC_FORMAT_TYPE_I:
case UAC_FORMAT_TYPE_III:
- err = parse_audio_format_i(chip, fp, format, fmt, iface);
+ err = parse_audio_format_i(chip, fp, format, fmt);
break;
case UAC_FORMAT_TYPE_II:
- err = parse_audio_format_ii(chip, fp, format, fmt, iface);
+ err = parse_audio_format_ii(chip, fp, format, fmt);
break;
default:
- snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
- chip->dev->devnum, fp->iface, fp->altsetting,
- fmt->bFormatType);
+ usb_audio_info(chip,
+ "%u:%d : format type %d is not supported yet\n",
+ fp->iface, fp->altsetting,
+ fmt->bFormatType);
return -ENOTSUPP;
}
fp->fmt_type = fmt->bFormatType;
diff --git a/sound/usb/format.h b/sound/usb/format.h
index 387924f0af8..4b8a01129f2 100644
--- a/sound/usb/format.h
+++ b/sound/usb/format.h
@@ -2,8 +2,8 @@
#define __USBAUDIO_FORMAT_H
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
- struct audioformat *fp, int format,
+ struct audioformat *fp, unsigned int format,
struct uac_format_type_i_continuous_descriptor *fmt,
- int stream, struct usb_host_interface *iface);
+ int stream);
#endif /* __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index d48d6f8f6ac..51ed1ac825f 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -21,6 +21,7 @@
#include "usbaudio.h"
#include "helper.h"
+#include "quirks.h"
/*
* combine bytes and get an integer value
@@ -81,33 +82,51 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
*/
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index, void *data,
- __u16 size, int timeout)
+ __u16 size)
{
int err;
void *buf = NULL;
+ int timeout;
if (size > 0) {
buf = kmemdup(data, size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
}
+
+ if (requesttype & USB_DIR_IN)
+ timeout = USB_CTRL_GET_TIMEOUT;
+ else
+ timeout = USB_CTRL_SET_TIMEOUT;
+
err = usb_control_msg(dev, pipe, request, requesttype,
value, index, buf, size, timeout);
+
if (size > 0) {
memcpy(data, buf, size);
kfree(buf);
}
+
+ snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
+ value, index, data, size);
+
return err;
}
unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *alts)
{
- if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
- get_endpoint(alts, 0)->bInterval >= 1 &&
- get_endpoint(alts, 0)->bInterval <= 4)
- return get_endpoint(alts, 0)->bInterval - 1;
- else
- return 0;
+ switch (snd_usb_get_speed(chip->dev)) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_WIRELESS:
+ case USB_SPEED_SUPER:
+ if (get_endpoint(alts, 0)->bInterval >= 1 &&
+ get_endpoint(alts, 0)->bInterval <= 4)
+ return get_endpoint(alts, 0)->bInterval - 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
}
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
index 09bd943c43b..805c300dd00 100644
--- a/sound/usb/helper.h
+++ b/sound/usb/helper.h
@@ -8,7 +8,7 @@ void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsub
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
- void *data, __u16 size, int timeout);
+ void *data, __u16 size);
unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *alts);
diff --git a/sound/usb/hiface/Makefile b/sound/usb/hiface/Makefile
new file mode 100644
index 00000000000..463b136d1d8
--- /dev/null
+++ b/sound/usb/hiface/Makefile
@@ -0,0 +1,2 @@
+snd-usb-hiface-objs := chip.o pcm.o
+obj-$(CONFIG_SND_USB_HIFACE) += snd-usb-hiface.o
diff --git a/sound/usb/hiface/chip.c b/sound/usb/hiface/chip.c
new file mode 100644
index 00000000000..2670d646bda
--- /dev/null
+++ b/sound/usb/hiface/chip.c
@@ -0,0 +1,297 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+
+#include "chip.h"
+#include "pcm.h"
+
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
+MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{M2Tech,Young},"
+ "{M2Tech,hiFace},"
+ "{M2Tech,North Star},"
+ "{M2Tech,W4S Young},"
+ "{M2Tech,Corrson},"
+ "{M2Tech,AUDIA},"
+ "{M2Tech,SL Audio},"
+ "{M2Tech,Empirical},"
+ "{M2Tech,Rockna},"
+ "{M2Tech,Pathos},"
+ "{M2Tech,Metronome},"
+ "{M2Tech,CAD},"
+ "{M2Tech,Audio Esclusive},"
+ "{M2Tech,Rotel},"
+ "{M2Tech,Eeaudio},"
+ "{The Chord Company,CHORD},"
+ "{AVA Group A/S,Vitus}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+#define DRIVER_NAME "snd-usb-hiface"
+#define CARD_NAME "hiFace"
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+static DEFINE_MUTEX(register_mutex);
+
+struct hiface_vendor_quirk {
+ const char *device_name;
+ u8 extra_freq;
+};
+
+static int hiface_chip_create(struct usb_interface *intf,
+ struct usb_device *device, int idx,
+ const struct hiface_vendor_quirk *quirk,
+ struct hiface_chip **rchip)
+{
+ struct snd_card *card = NULL;
+ struct hiface_chip *chip;
+ int ret;
+ int len;
+
+ *rchip = NULL;
+
+ /* if we are here, card can be registered in alsa. */
+ ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
+ sizeof(*chip), &card);
+ if (ret < 0) {
+ dev_err(&device->dev, "cannot create alsa card.\n");
+ return ret;
+ }
+
+ strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+
+ if (quirk && quirk->device_name)
+ strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname));
+ else
+ strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
+
+ strlcat(card->longname, card->shortname, sizeof(card->longname));
+ len = strlcat(card->longname, " at ", sizeof(card->longname));
+ if (len < sizeof(card->longname))
+ usb_make_path(device, card->longname + len,
+ sizeof(card->longname) - len);
+
+ chip = card->private_data;
+ chip->dev = device;
+ chip->card = card;
+
+ *rchip = chip;
+ return 0;
+}
+
+static int hiface_chip_probe(struct usb_interface *intf,
+ const struct usb_device_id *usb_id)
+{
+ const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
+ int ret;
+ int i;
+ struct hiface_chip *chip;
+ struct usb_device *device = interface_to_usbdev(intf);
+
+ ret = usb_set_interface(device, 0, 0);
+ if (ret != 0) {
+ dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
+ return -EIO;
+ }
+
+ /* check whether the card is already registered */
+ chip = NULL;
+ mutex_lock(&register_mutex);
+
+ for (i = 0; i < SNDRV_CARDS; i++)
+ if (enable[i])
+ break;
+
+ if (i >= SNDRV_CARDS) {
+ dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = hiface_chip_create(intf, device, i, quirk, &chip);
+ if (ret < 0)
+ goto err;
+
+ ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
+ if (ret < 0)
+ goto err_chip_destroy;
+
+ ret = snd_card_register(chip->card);
+ if (ret < 0) {
+ dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
+ goto err_chip_destroy;
+ }
+
+ mutex_unlock(&register_mutex);
+
+ usb_set_intfdata(intf, chip);
+ return 0;
+
+err_chip_destroy:
+ snd_card_free(chip->card);
+err:
+ mutex_unlock(&register_mutex);
+ return ret;
+}
+
+static void hiface_chip_disconnect(struct usb_interface *intf)
+{
+ struct hiface_chip *chip;
+ struct snd_card *card;
+
+ chip = usb_get_intfdata(intf);
+ if (!chip)
+ return;
+
+ card = chip->card;
+
+ /* Make sure that the userspace cannot create new request */
+ snd_card_disconnect(card);
+
+ hiface_pcm_abort(chip);
+ snd_card_free_when_closed(card);
+}
+
+static const struct usb_device_id device_table[] = {
+ {
+ USB_DEVICE(0x04b4, 0x0384),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Young",
+ .extra_freq = 1,
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x930b),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "hiFace",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931b),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "North Star",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "W4S Young",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931d),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Corrson",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931e),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "AUDIA",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x931f),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "SL Audio",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x9320),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Empirical",
+ }
+ },
+ {
+ USB_DEVICE(0x04b4, 0x9321),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Rockna",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9001),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Pathos",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9002),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Metronome",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9006),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "CAD",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x9008),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Audio Esclusive",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Rotel",
+ }
+ },
+ {
+ USB_DEVICE(0x249c, 0x932c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Eeaudio",
+ }
+ },
+ {
+ USB_DEVICE(0x245f, 0x931c),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "CHORD",
+ }
+ },
+ {
+ USB_DEVICE(0x25c6, 0x9002),
+ .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
+ .device_name = "Vitus",
+ }
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static struct usb_driver hiface_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = hiface_chip_probe,
+ .disconnect = hiface_chip_disconnect,
+ .id_table = device_table,
+};
+
+module_usb_driver(hiface_usb_driver);
diff --git a/sound/usb/hiface/chip.h b/sound/usb/hiface/chip.h
new file mode 100644
index 00000000000..189a1371b7c
--- /dev/null
+++ b/sound/usb/hiface/chip.h
@@ -0,0 +1,30 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef HIFACE_CHIP_H
+#define HIFACE_CHIP_H
+
+#include <linux/usb.h>
+#include <sound/core.h>
+
+struct pcm_runtime;
+
+struct hiface_chip {
+ struct usb_device *dev;
+ struct snd_card *card;
+ struct pcm_runtime *pcm;
+};
+#endif /* HIFACE_CHIP_H */
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
new file mode 100644
index 00000000000..2c44139b404
--- /dev/null
+++ b/sound/usb/hiface/pcm.c
@@ -0,0 +1,621 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <sound/pcm.h>
+
+#include "pcm.h"
+#include "chip.h"
+
+#define OUT_EP 0x2
+#define PCM_N_URBS 8
+#define PCM_PACKET_SIZE 4096
+#define PCM_BUFFER_SIZE (2 * PCM_N_URBS * PCM_PACKET_SIZE)
+
+struct pcm_urb {
+ struct hiface_chip *chip;
+
+ struct urb instance;
+ struct usb_anchor submitted;
+ u8 *buffer;
+};
+
+struct pcm_substream {
+ spinlock_t lock;
+ struct snd_pcm_substream *instance;
+
+ bool active;
+ snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */
+ snd_pcm_uframes_t period_off; /* current position in current period */
+};
+
+enum { /* pcm streaming states */
+ STREAM_DISABLED, /* no pcm streaming */
+ STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
+ STREAM_RUNNING, /* pcm streaming running */
+ STREAM_STOPPING
+};
+
+struct pcm_runtime {
+ struct hiface_chip *chip;
+ struct snd_pcm *instance;
+
+ struct pcm_substream playback;
+ bool panic; /* if set driver won't do anymore pcm on device */
+
+ struct pcm_urb out_urbs[PCM_N_URBS];
+
+ struct mutex stream_mutex;
+ u8 stream_state; /* one of STREAM_XXX */
+ u8 extra_freq;
+ wait_queue_head_t stream_wait_queue;
+ bool stream_wait_cond;
+};
+
+static const unsigned int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000,
+ 352800, 384000 };
+static const struct snd_pcm_hw_constraint_list constraints_extra_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hardware pcm_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH,
+
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+
+ .rate_min = 44100,
+ .rate_max = 192000, /* changes in hiface_pcm_open to support extra rates */
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PCM_BUFFER_SIZE,
+ .period_bytes_min = PCM_PACKET_SIZE,
+ .period_bytes_max = PCM_BUFFER_SIZE,
+ .periods_min = 2,
+ .periods_max = 1024
+};
+
+/* message values used to change the sample rate */
+#define HIFACE_SET_RATE_REQUEST 0xb0
+
+#define HIFACE_RATE_44100 0x43
+#define HIFACE_RATE_48000 0x4b
+#define HIFACE_RATE_88200 0x42
+#define HIFACE_RATE_96000 0x4a
+#define HIFACE_RATE_176400 0x40
+#define HIFACE_RATE_192000 0x48
+#define HIFACE_RATE_352800 0x58
+#define HIFACE_RATE_384000 0x68
+
+static int hiface_pcm_set_rate(struct pcm_runtime *rt, unsigned int rate)
+{
+ struct usb_device *device = rt->chip->dev;
+ u16 rate_value;
+ int ret;
+
+ /* We are already sure that the rate is supported here thanks to
+ * ALSA constraints
+ */
+ switch (rate) {
+ case 44100:
+ rate_value = HIFACE_RATE_44100;
+ break;
+ case 48000:
+ rate_value = HIFACE_RATE_48000;
+ break;
+ case 88200:
+ rate_value = HIFACE_RATE_88200;
+ break;
+ case 96000:
+ rate_value = HIFACE_RATE_96000;
+ break;
+ case 176400:
+ rate_value = HIFACE_RATE_176400;
+ break;
+ case 192000:
+ rate_value = HIFACE_RATE_192000;
+ break;
+ case 352800:
+ rate_value = HIFACE_RATE_352800;
+ break;
+ case 384000:
+ rate_value = HIFACE_RATE_384000;
+ break;
+ default:
+ dev_err(&device->dev, "Unsupported rate %d\n", rate);
+ return -EINVAL;
+ }
+
+ /*
+ * USBIO: Vendor 0xb0(wValue=0x0043, wIndex=0x0000)
+ * 43 b0 43 00 00 00 00 00
+ * USBIO: Vendor 0xb0(wValue=0x004b, wIndex=0x0000)
+ * 43 b0 4b 00 00 00 00 00
+ * This control message doesn't have any ack from the
+ * other side
+ */
+ ret = usb_control_msg(device, usb_sndctrlpipe(device, 0),
+ HIFACE_SET_RATE_REQUEST,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ rate_value, 0, NULL, 0, 100);
+ if (ret < 0) {
+ dev_err(&device->dev, "Error setting samplerate %d.\n", rate);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct pcm_substream *hiface_pcm_get_substream(struct snd_pcm_substream
+ *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct device *device = &rt->chip->dev->dev;
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return &rt->playback;
+
+ dev_err(device, "Error getting pcm substream slot.\n");
+ return NULL;
+}
+
+/* call with stream_mutex locked */
+static void hiface_pcm_stream_stop(struct pcm_runtime *rt)
+{
+ int i, time;
+
+ if (rt->stream_state != STREAM_DISABLED) {
+ rt->stream_state = STREAM_STOPPING;
+
+ for (i = 0; i < PCM_N_URBS; i++) {
+ time = usb_wait_anchor_empty_timeout(
+ &rt->out_urbs[i].submitted, 100);
+ if (!time)
+ usb_kill_anchored_urbs(
+ &rt->out_urbs[i].submitted);
+ usb_kill_urb(&rt->out_urbs[i].instance);
+ }
+
+ rt->stream_state = STREAM_DISABLED;
+ }
+}
+
+/* call with stream_mutex locked */
+static int hiface_pcm_stream_start(struct pcm_runtime *rt)
+{
+ int ret = 0;
+ int i;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+
+ /* reset panic state when starting a new stream */
+ rt->panic = false;
+
+ /* submit our out urbs zero init */
+ rt->stream_state = STREAM_STARTING;
+ for (i = 0; i < PCM_N_URBS; i++) {
+ memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE);
+ usb_anchor_urb(&rt->out_urbs[i].instance,
+ &rt->out_urbs[i].submitted);
+ ret = usb_submit_urb(&rt->out_urbs[i].instance,
+ GFP_ATOMIC);
+ if (ret) {
+ hiface_pcm_stream_stop(rt);
+ return ret;
+ }
+ }
+
+ /* wait for first out urb to return (sent in in urb handler) */
+ wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
+ HZ);
+ if (rt->stream_wait_cond) {
+ struct device *device = &rt->chip->dev->dev;
+ dev_dbg(device, "%s: Stream is running wakeup event\n",
+ __func__);
+ rt->stream_state = STREAM_RUNNING;
+ } else {
+ hiface_pcm_stream_stop(rt);
+ return -EIO;
+ }
+ }
+ return ret;
+}
+
+/* The hardware wants word-swapped 32-bit values */
+static void memcpy_swahw32(u8 *dest, u8 *src, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n / 4; i++)
+ ((u32 *)dest)[i] = swahw32(((u32 *)src)[i]);
+}
+
+/* call with substream locked */
+/* returns true if a period elapsed */
+static bool hiface_pcm_playback(struct pcm_substream *sub, struct pcm_urb *urb)
+{
+ struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
+ struct device *device = &urb->chip->dev->dev;
+ u8 *source;
+ unsigned int pcm_buffer_size;
+
+ WARN_ON(alsa_rt->format != SNDRV_PCM_FORMAT_S32_LE);
+
+ pcm_buffer_size = snd_pcm_lib_buffer_bytes(sub->instance);
+
+ if (sub->dma_off + PCM_PACKET_SIZE <= pcm_buffer_size) {
+ dev_dbg(device, "%s: (1) buffer_size %#x dma_offset %#x\n", __func__,
+ (unsigned int) pcm_buffer_size,
+ (unsigned int) sub->dma_off);
+
+ source = alsa_rt->dma_area + sub->dma_off;
+ memcpy_swahw32(urb->buffer, source, PCM_PACKET_SIZE);
+ } else {
+ /* wrap around at end of ring buffer */
+ unsigned int len;
+
+ dev_dbg(device, "%s: (2) buffer_size %#x dma_offset %#x\n", __func__,
+ (unsigned int) pcm_buffer_size,
+ (unsigned int) sub->dma_off);
+
+ len = pcm_buffer_size - sub->dma_off;
+
+ source = alsa_rt->dma_area + sub->dma_off;
+ memcpy_swahw32(urb->buffer, source, len);
+
+ source = alsa_rt->dma_area;
+ memcpy_swahw32(urb->buffer + len, source,
+ PCM_PACKET_SIZE - len);
+ }
+ sub->dma_off += PCM_PACKET_SIZE;
+ if (sub->dma_off >= pcm_buffer_size)
+ sub->dma_off -= pcm_buffer_size;
+
+ sub->period_off += PCM_PACKET_SIZE;
+ if (sub->period_off >= alsa_rt->period_size) {
+ sub->period_off %= alsa_rt->period_size;
+ return true;
+ }
+ return false;
+}
+
+static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
+{
+ struct pcm_urb *out_urb = usb_urb->context;
+ struct pcm_runtime *rt = out_urb->chip->pcm;
+ struct pcm_substream *sub;
+ bool do_period_elapsed = false;
+ unsigned long flags;
+ int ret;
+
+ if (rt->panic || rt->stream_state == STREAM_STOPPING)
+ return;
+
+ if (unlikely(usb_urb->status == -ENOENT || /* unlinked */
+ usb_urb->status == -ENODEV || /* device removed */
+ usb_urb->status == -ECONNRESET || /* unlinked */
+ usb_urb->status == -ESHUTDOWN)) { /* device disabled */
+ goto out_fail;
+ }
+
+ if (rt->stream_state == STREAM_STARTING) {
+ rt->stream_wait_cond = true;
+ wake_up(&rt->stream_wait_queue);
+ }
+
+ /* now send our playback data (if a free out urb was found) */
+ sub = &rt->playback;
+ spin_lock_irqsave(&sub->lock, flags);
+ if (sub->active)
+ do_period_elapsed = hiface_pcm_playback(sub, out_urb);
+ else
+ memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
+
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ if (do_period_elapsed)
+ snd_pcm_period_elapsed(sub->instance);
+
+ ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
+ if (ret < 0)
+ goto out_fail;
+
+ return;
+
+out_fail:
+ rt->panic = true;
+}
+
+static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = NULL;
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+ int ret;
+
+ if (rt->panic)
+ return -EPIPE;
+
+ mutex_lock(&rt->stream_mutex);
+ alsa_rt->hw = pcm_hw;
+
+ if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sub = &rt->playback;
+
+ if (!sub) {
+ struct device *device = &rt->chip->dev->dev;
+ mutex_unlock(&rt->stream_mutex);
+ dev_err(device, "Invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (rt->extra_freq) {
+ alsa_rt->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ alsa_rt->hw.rate_max = 384000;
+
+ /* explicit constraints needed as we added SNDRV_PCM_RATE_KNOT */
+ ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_extra_rates);
+ if (ret < 0) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ }
+
+ sub->instance = alsa_sub;
+ sub->active = false;
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ unsigned long flags;
+
+ if (rt->panic)
+ return 0;
+
+ mutex_lock(&rt->stream_mutex);
+ if (sub) {
+ hiface_pcm_stream_stop(rt);
+
+ /* deactivate substream */
+ spin_lock_irqsave(&sub->lock, flags);
+ sub->instance = NULL;
+ sub->active = false;
+ spin_unlock_irqrestore(&sub->lock, flags);
+
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub,
+ params_buffer_bytes(hw_params));
+}
+
+static int hiface_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
+{
+ return snd_pcm_lib_free_vmalloc_buffer(alsa_sub);
+}
+
+static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
+ int ret;
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ mutex_lock(&rt->stream_mutex);
+
+ sub->dma_off = 0;
+ sub->period_off = 0;
+
+ if (rt->stream_state == STREAM_DISABLED) {
+
+ ret = hiface_pcm_set_rate(rt, alsa_rt->rate);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ ret = hiface_pcm_stream_start(rt);
+ if (ret) {
+ mutex_unlock(&rt->stream_mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&rt->stream_mutex);
+ return 0;
+}
+
+static int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
+{
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+
+ if (rt->panic)
+ return -EPIPE;
+ if (!sub)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock_irq(&sub->lock);
+ sub->active = true;
+ spin_unlock_irq(&sub->lock);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock_irq(&sub->lock);
+ sub->active = false;
+ spin_unlock_irq(&sub->lock);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub)
+{
+ struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
+ struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
+ unsigned long flags;
+ snd_pcm_uframes_t dma_offset;
+
+ if (rt->panic || !sub)
+ return SNDRV_PCM_POS_XRUN;
+
+ spin_lock_irqsave(&sub->lock, flags);
+ dma_offset = sub->dma_off;
+ spin_unlock_irqrestore(&sub->lock, flags);
+ return bytes_to_frames(alsa_sub->runtime, dma_offset);
+}
+
+static struct snd_pcm_ops pcm_ops = {
+ .open = hiface_pcm_open,
+ .close = hiface_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = hiface_pcm_hw_params,
+ .hw_free = hiface_pcm_hw_free,
+ .prepare = hiface_pcm_prepare,
+ .trigger = hiface_pcm_trigger,
+ .pointer = hiface_pcm_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+static int hiface_pcm_init_urb(struct pcm_urb *urb,
+ struct hiface_chip *chip,
+ unsigned int ep,
+ void (*handler)(struct urb *))
+{
+ urb->chip = chip;
+ usb_init_urb(&urb->instance);
+
+ urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL);
+ if (!urb->buffer)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(&urb->instance, chip->dev,
+ usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer,
+ PCM_PACKET_SIZE, handler, urb);
+ init_usb_anchor(&urb->submitted);
+
+ return 0;
+}
+
+void hiface_pcm_abort(struct hiface_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+
+ if (rt) {
+ rt->panic = true;
+
+ mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+ mutex_unlock(&rt->stream_mutex);
+ }
+}
+
+static void hiface_pcm_destroy(struct hiface_chip *chip)
+{
+ struct pcm_runtime *rt = chip->pcm;
+ int i;
+
+ for (i = 0; i < PCM_N_URBS; i++)
+ kfree(rt->out_urbs[i].buffer);
+
+ kfree(chip->pcm);
+ chip->pcm = NULL;
+}
+
+static void hiface_pcm_free(struct snd_pcm *pcm)
+{
+ struct pcm_runtime *rt = pcm->private_data;
+
+ if (rt)
+ hiface_pcm_destroy(rt->chip);
+}
+
+int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
+{
+ int i;
+ int ret;
+ struct snd_pcm *pcm;
+ struct pcm_runtime *rt;
+
+ rt = kzalloc(sizeof(*rt), GFP_KERNEL);
+ if (!rt)
+ return -ENOMEM;
+
+ rt->chip = chip;
+ rt->stream_state = STREAM_DISABLED;
+ if (extra_freq)
+ rt->extra_freq = 1;
+
+ init_waitqueue_head(&rt->stream_wait_queue);
+ mutex_init(&rt->stream_mutex);
+ spin_lock_init(&rt->playback.lock);
+
+ for (i = 0; i < PCM_N_URBS; i++)
+ hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP,
+ hiface_pcm_out_urb_handler);
+
+ ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm);
+ if (ret < 0) {
+ kfree(rt);
+ dev_err(&chip->dev->dev, "Cannot create pcm instance\n");
+ return ret;
+ }
+
+ pcm->private_data = rt;
+ pcm->private_free = hiface_pcm_free;
+
+ strlcpy(pcm->name, "USB-SPDIF Audio", sizeof(pcm->name));
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
+
+ rt->instance = pcm;
+
+ chip->pcm = rt;
+ return 0;
+}
diff --git a/sound/usb/hiface/pcm.h b/sound/usb/hiface/pcm.h
new file mode 100644
index 00000000000..77edd7c12e1
--- /dev/null
+++ b/sound/usb/hiface/pcm.h
@@ -0,0 +1,24 @@
+/*
+ * Linux driver for M2Tech hiFace compatible devices
+ *
+ * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
+ *
+ * Authors: Michael Trimarchi <michael@amarulasolutions.com>
+ * Antonio Ospite <ao2@amarulasolutions.com>
+ *
+ * The driver is based on the work done in TerraTec DMX 6Fire USB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef HIFACE_PCM_H
+#define HIFACE_PCM_H
+
+struct hiface_chip;
+
+int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq);
+void hiface_pcm_abort(struct hiface_chip *chip);
+#endif /* HIFACE_PCM_H */
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 46785643c66..9da74d2e8ee 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/usb/audio.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -54,6 +55,7 @@
#include <sound/asequencer.h>
#include "usbaudio.h"
#include "midi.h"
+#include "power.h"
#include "helper.h"
/*
@@ -114,6 +116,7 @@ struct snd_usb_midi {
struct list_head list;
struct timer_list error_timer;
spinlock_t disc_lock;
+ struct rw_semaphore disc_rwsem;
struct mutex mutex;
u32 usb_id;
int next_midi_device;
@@ -123,8 +126,9 @@ struct snd_usb_midi {
struct snd_usb_midi_in_endpoint *in;
} endpoints[MIDI_MAX_ENDPOINTS];
unsigned long input_triggered;
- unsigned int opened;
+ unsigned int opened[2];
unsigned char disconnected;
+ unsigned char input_running;
struct snd_kcontrol *roland_load_ctl;
};
@@ -187,16 +191,16 @@ static int snd_usbmidi_submit_urb(struct urb* urb, gfp_t flags)
{
int err = usb_submit_urb(urb, flags);
if (err < 0 && err != -ENODEV)
- snd_printk(KERN_ERR "usb_submit_urb: %d\n", err);
+ dev_err(&urb->dev->dev, "usb_submit_urb: %d\n", err);
return err;
}
/*
* Error handling for URB completion functions.
*/
-static int snd_usbmidi_urb_error(int status)
+static int snd_usbmidi_urb_error(const struct urb *urb)
{
- switch (status) {
+ switch (urb->status) {
/* manually unlinked, or device gone */
case -ENOENT:
case -ECONNRESET:
@@ -209,7 +213,7 @@ static int snd_usbmidi_urb_error(int status)
case -EILSEQ:
return -EIO;
default:
- snd_printk(KERN_ERR "urb status %d\n", status);
+ dev_err(&urb->dev->dev, "urb status %d\n", urb->status);
return 0; /* continue */
}
}
@@ -223,7 +227,7 @@ static void snd_usbmidi_input_data(struct snd_usb_midi_in_endpoint* ep, int port
struct usbmidi_in_port* port = &ep->ports[portidx];
if (!port->substream) {
- snd_printd("unexpected port %d!\n", portidx);
+ dev_dbg(&ep->umidi->dev->dev, "unexpected port %d!\n", portidx);
return;
}
if (!test_bit(port->substream->number, &ep->umidi->input_triggered))
@@ -255,7 +259,7 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer,
urb->actual_length);
} else {
- int err = snd_usbmidi_urb_error(urb->status);
+ int err = snd_usbmidi_urb_error(urb);
if (err < 0) {
if (err != -ENODEV) {
ep->error_resubmit = 1;
@@ -285,7 +289,7 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
}
spin_unlock(&ep->buffer_lock);
if (urb->status < 0) {
- int err = snd_usbmidi_urb_error(urb->status);
+ int err = snd_usbmidi_urb_error(urb);
if (err < 0) {
if (err != -ENODEV)
mod_timer(&ep->umidi->error_timer,
@@ -434,7 +438,7 @@ static void snd_usbmidi_maudio_broken_running_status_input(
u8 cin = buffer[i] & 0x0f;
struct usbmidi_in_port *port = &ep->ports[cable];
int length;
-
+
length = snd_usbmidi_cin_length[cin];
if (cin == 0xf && buffer[i + 1] >= 0xf8)
; /* realtime msg: no running status change */
@@ -628,13 +632,13 @@ static struct usb_protocol_ops snd_usbmidi_standard_ops = {
static struct usb_protocol_ops snd_usbmidi_midiman_ops = {
.input = snd_usbmidi_midiman_input,
- .output = snd_usbmidi_standard_output,
+ .output = snd_usbmidi_standard_output,
.output_packet = snd_usbmidi_output_midiman_packet,
};
static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = {
.input = snd_usbmidi_maudio_broken_running_status_input,
- .output = snd_usbmidi_standard_output,
+ .output = snd_usbmidi_standard_output,
.output_packet = snd_usbmidi_output_standard_packet,
};
@@ -784,7 +788,7 @@ static struct usb_protocol_ops snd_usbmidi_novation_ops = {
};
/*
- * "raw" protocol: used by the MOTU FastLane.
+ * "raw" protocol: just move raw MIDI bytes from/to the endpoint
*/
static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep,
@@ -815,6 +819,22 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {
.output = snd_usbmidi_raw_output,
};
+/*
+ * FTDI protocol: raw MIDI bytes, but input packets have two modem status bytes.
+ */
+
+static void snd_usbmidi_ftdi_input(struct snd_usb_midi_in_endpoint* ep,
+ uint8_t* buffer, int buffer_length)
+{
+ if (buffer_length > 2)
+ snd_usbmidi_input_data(ep, 0, buffer + 2, buffer_length - 2);
+}
+
+static struct usb_protocol_ops snd_usbmidi_ftdi_ops = {
+ .input = snd_usbmidi_ftdi_input,
+ .output = snd_usbmidi_raw_output,
+};
+
static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
uint8_t *buffer, int buffer_length)
{
@@ -834,7 +854,14 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
if (!ep->ports[0].active)
return;
- count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
+ switch (snd_usb_get_speed(ep->umidi->dev)) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ count = 1;
+ break;
+ default:
+ count = 2;
+ }
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
@@ -843,8 +870,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
return;
}
- memset(urb->transfer_buffer + count, 0xFD, 9 - count);
- urb->transfer_buffer_length = count;
+ memset(urb->transfer_buffer + count, 0xFD, ep->max_transfer - count);
+ urb->transfer_buffer_length = ep->max_transfer;
}
static struct usb_protocol_ops snd_usbmidi_122l_ops = {
@@ -1007,29 +1034,48 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi)
snd_usbmidi_input_start(&umidi->list);
}
-static void substream_open(struct snd_rawmidi_substream *substream, int open)
+static int substream_open(struct snd_rawmidi_substream *substream, int dir,
+ int open)
{
struct snd_usb_midi* umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl;
+ down_read(&umidi->disc_rwsem);
+ if (umidi->disconnected) {
+ up_read(&umidi->disc_rwsem);
+ return open ? -ENODEV : 0;
+ }
+
mutex_lock(&umidi->mutex);
if (open) {
- if (umidi->opened++ == 0 && umidi->roland_load_ctl) {
- ctl = umidi->roland_load_ctl;
- ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(umidi->card,
+ if (!umidi->opened[0] && !umidi->opened[1]) {
+ if (umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
- update_roland_altsetting(umidi);
+ update_roland_altsetting(umidi);
+ }
}
+ umidi->opened[dir]++;
+ if (umidi->opened[1])
+ snd_usbmidi_input_start(&umidi->list);
} else {
- if (--umidi->opened == 0 && umidi->roland_load_ctl) {
- ctl = umidi->roland_load_ctl;
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(umidi->card,
+ umidi->opened[dir]--;
+ if (!umidi->opened[1])
+ snd_usbmidi_input_stop(&umidi->list);
+ if (!umidi->opened[0] && !umidi->opened[1]) {
+ if (umidi->roland_load_ctl) {
+ ctl = umidi->roland_load_ctl;
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(umidi->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ }
}
}
mutex_unlock(&umidi->mutex);
+ up_read(&umidi->disc_rwsem);
+ return 0;
}
static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
@@ -1049,16 +1095,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream)
snd_BUG();
return -ENXIO;
}
+
substream->runtime->private_data = port;
port->state = STATE_UNKNOWN;
- substream_open(substream, 1);
- return 0;
+ return substream_open(substream, 0, 1);
}
static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream)
{
- substream_open(substream, 0);
- return 0;
+ return substream_open(substream, 0, 0);
}
static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1111,14 +1156,12 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{
- substream_open(substream, 1);
- return 0;
+ return substream_open(substream, 1, 1);
}
static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream)
{
- substream_open(substream, 0);
- return 0;
+ return substream_open(substream, 1, 0);
}
static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1248,7 +1291,7 @@ static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint *ep
*/
static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
struct snd_usb_midi_endpoint_info* ep_info,
- struct snd_usb_midi_endpoint* rep)
+ struct snd_usb_midi_endpoint* rep)
{
struct snd_usb_midi_out_endpoint* ep;
unsigned int i;
@@ -1286,8 +1329,16 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */
case USB_ID(0x15ca, 0x1806): /* Textech USB Midi Cable */
case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */
+ case USB_ID(0xfc08, 0x0101): /* Unknown vendor Cable */
ep->max_transfer = 4;
break;
+ /*
+ * Some devices only work with 9 bytes packet size:
+ */
+ case USB_ID(0x0644, 0x800E): /* Tascam US-122L */
+ case USB_ID(0x0644, 0x800F): /* Tascam US-144 */
+ ep->max_transfer = 9;
+ break;
}
for (i = 0; i < OUTPUT_URBS; ++i) {
buffer = usb_alloc_coherent(umidi->dev,
@@ -1359,9 +1410,12 @@ void snd_usbmidi_disconnect(struct list_head* p)
* a timer may submit an URB. To reliably break the cycle
* a flag under lock must be used
*/
+ down_write(&umidi->disc_rwsem);
spin_lock_irq(&umidi->disc_lock);
umidi->disconnected = 1;
spin_unlock_irq(&umidi->disc_lock);
+ up_write(&umidi->disc_rwsem);
+
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->out)
@@ -1390,6 +1444,7 @@ void snd_usbmidi_disconnect(struct list_head* p)
}
del_timer_sync(&umidi->error_timer);
}
+EXPORT_SYMBOL(snd_usbmidi_disconnect);
static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
{
@@ -1398,12 +1453,11 @@ static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
}
static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi,
- int stream, int number)
+ int stream, int number)
{
- struct list_head* list;
+ struct snd_rawmidi_substream *substream;
- list_for_each(list, &umidi->rmidi->streams[stream].substreams) {
- struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &umidi->rmidi->streams[stream].substreams, list) {
if (substream->number == number)
return substream;
}
@@ -1521,8 +1575,41 @@ static struct port_info {
EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"),
EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"),
EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"),
+ /* BOSS GT-PRO */
+ CONTROL_PORT(0x0582, 0x0089, 0, "%s Control"),
/* Edirol UM-3EX */
CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"),
+ /* Roland VG-99 */
+ CONTROL_PORT(0x0582, 0x00b2, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x00b2, 1, "%s MIDI"),
+ /* Cakewalk Sonar V-Studio 100 */
+ EXTERNAL_PORT(0x0582, 0x00eb, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x00eb, 1, "%s Control"),
+ /* Roland VB-99 */
+ CONTROL_PORT(0x0582, 0x0102, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0102, 1, "%s MIDI"),
+ /* Roland A-PRO */
+ EXTERNAL_PORT(0x0582, 0x010f, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x010f, 1, "%s 1"),
+ CONTROL_PORT(0x0582, 0x010f, 2, "%s 2"),
+ /* Roland SD-50 */
+ ROLAND_SYNTH_PORT(0x0582, 0x0114, 0, "%s Synth", 128),
+ EXTERNAL_PORT(0x0582, 0x0114, 1, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0114, 2, "%s Control"),
+ /* Roland OCTA-CAPTURE */
+ EXTERNAL_PORT(0x0582, 0x0120, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0120, 1, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0121, 0, "%s MIDI"),
+ CONTROL_PORT(0x0582, 0x0121, 1, "%s Control"),
+ /* Roland SPD-SX */
+ CONTROL_PORT(0x0582, 0x0145, 0, "%s Control"),
+ EXTERNAL_PORT(0x0582, 0x0145, 1, "%s MIDI"),
+ /* Roland A-Series */
+ CONTROL_PORT(0x0582, 0x0156, 0, "%s Keyboard"),
+ EXTERNAL_PORT(0x0582, 0x0156, 1, "%s MIDI"),
+ /* Roland INTEGRA-7 */
+ ROLAND_SYNTH_PORT(0x0582, 0x015b, 0, "%s Synth", 128),
+ CONTROL_PORT(0x0582, 0x015b, 1, "%s Control"),
/* M-Audio MidiSport 8x8 */
CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"),
CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"),
@@ -1581,7 +1668,7 @@ static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi,
struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number);
if (!substream) {
- snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number);
+ dev_err(&umidi->dev->dev, "substream %d:%d not found\n", stream, number);
return;
}
@@ -1630,7 +1717,7 @@ static int snd_usbmidi_create_endpoints(struct snd_usb_midi* umidi,
}
}
}
- snd_printdd(KERN_INFO "created %d output and %d input ports\n",
+ dev_dbg(&umidi->dev->dev, "created %d output and %d input ports\n",
out_ports, in_ports);
return 0;
}
@@ -1660,10 +1747,11 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
ms_header->bLength >= 7 &&
ms_header->bDescriptorType == USB_DT_CS_INTERFACE &&
ms_header->bDescriptorSubtype == UAC_HEADER)
- snd_printdd(KERN_INFO "MIDIStreaming version %02x.%02x\n",
+ dev_dbg(&umidi->dev->dev, "MIDIStreaming version %02x.%02x\n",
ms_header->bcdMSC[1], ms_header->bcdMSC[0]);
else
- snd_printk(KERN_WARNING "MIDIStreaming interface descriptor not found\n");
+ dev_warn(&umidi->dev->dev,
+ "MIDIStreaming interface descriptor not found\n");
epidx = 0;
for (i = 0; i < intfd->bNumEndpoints; ++i) {
@@ -1680,7 +1768,8 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
if (usb_endpoint_dir_out(ep)) {
if (endpoints[epidx].out_ep) {
if (++epidx >= MIDI_MAX_ENDPOINTS) {
- snd_printk(KERN_WARNING "too many endpoints\n");
+ dev_warn(&umidi->dev->dev,
+ "too many endpoints\n");
break;
}
}
@@ -1695,12 +1784,13 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
*/
endpoints[epidx].out_interval = 1;
endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
- snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n",
+ dev_dbg(&umidi->dev->dev, "EP %02X: %d jack(s)\n",
ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack);
} else {
if (endpoints[epidx].in_ep) {
if (++epidx >= MIDI_MAX_ENDPOINTS) {
- snd_printk(KERN_WARNING "too many endpoints\n");
+ dev_warn(&umidi->dev->dev,
+ "too many endpoints\n");
break;
}
}
@@ -1710,7 +1800,7 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi* umidi,
else if (snd_usb_get_speed(umidi->dev) == USB_SPEED_LOW)
endpoints[epidx].in_interval = 1;
endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
- snd_printdd(KERN_INFO "EP %02X: %d jack(s)\n",
+ dev_dbg(&umidi->dev->dev, "EP %02X: %d jack(s)\n",
ep->bEndpointAddress, ms_ep->bNumEmbMIDIJack);
}
}
@@ -1722,13 +1812,7 @@ static int roland_load_info(struct snd_kcontrol *kcontrol,
{
static const char *const names[] = { "High Load", "Light Load" };
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 2;
- if (info->value.enumerated.item > 1)
- info->value.enumerated.item = 1;
- strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 2, names);
}
static int roland_load_get(struct snd_kcontrol *kcontrol,
@@ -1784,7 +1868,7 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi* umidi)
(get_endpoint(hostif, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
return;
- snd_printdd(KERN_INFO "switching to altsetting %d with int ep\n",
+ dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
intfd->bAlternateSetting);
usb_set_interface(umidi->dev, intfd->bInterfaceNumber,
intfd->bAlternateSetting);
@@ -1811,7 +1895,7 @@ static int snd_usbmidi_detect_endpoints(struct snd_usb_midi* umidi,
snd_usbmidi_switch_roland_altsetting(umidi);
if (endpoint[0].out_ep || endpoint[0].in_ep)
- return 0;
+ return 0;
intf = umidi->iface;
if (!intf || intf->num_altsetting < 1)
@@ -1849,7 +1933,7 @@ static int snd_usbmidi_detect_per_port_endpoints(struct snd_usb_midi* umidi,
struct snd_usb_midi_endpoint_info* endpoints)
{
int err, i;
-
+
err = snd_usbmidi_detect_endpoints(umidi, endpoints, MIDI_MAX_ENDPOINTS);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
if (endpoints[i].out_ep)
@@ -1900,6 +1984,44 @@ static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi,
}
/*
+ * Detects the endpoints and ports of Roland devices.
+ */
+static int snd_usbmidi_detect_roland(struct snd_usb_midi* umidi,
+ struct snd_usb_midi_endpoint_info* endpoint)
+{
+ struct usb_interface* intf;
+ struct usb_host_interface *hostif;
+ u8* cs_desc;
+
+ intf = umidi->iface;
+ if (!intf)
+ return -ENOENT;
+ hostif = intf->altsetting;
+ /*
+ * Some devices have a descriptor <06 24 F1 02 <inputs> <outputs>>,
+ * some have standard class descriptors, or both kinds, or neither.
+ */
+ for (cs_desc = hostif->extra;
+ cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2;
+ cs_desc += cs_desc[0]) {
+ if (cs_desc[0] >= 6 &&
+ cs_desc[1] == USB_DT_CS_INTERFACE &&
+ cs_desc[2] == 0xf1 &&
+ cs_desc[3] == 0x02) {
+ endpoint->in_cables = (1 << cs_desc[4]) - 1;
+ endpoint->out_cables = (1 << cs_desc[5]) - 1;
+ return snd_usbmidi_detect_endpoints(umidi, endpoint, 1);
+ } else if (cs_desc[0] >= 7 &&
+ cs_desc[1] == USB_DT_CS_INTERFACE &&
+ cs_desc[2] == UAC_HEADER) {
+ return snd_usbmidi_get_ms_info(umidi, endpoint);
+ }
+ }
+
+ return -ENODEV;
+}
+
+/*
* Creates the endpoints and their ports for Midiman devices.
*/
static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi,
@@ -1928,25 +2050,25 @@ static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi,
* input bulk endpoints (at indices 1 and 3) which aren't used.
*/
if (intfd->bNumEndpoints < (endpoint->out_cables > 0x0001 ? 5 : 3)) {
- snd_printdd(KERN_ERR "not enough endpoints\n");
+ dev_dbg(&umidi->dev->dev, "not enough endpoints\n");
return -ENOENT;
}
epd = get_endpoint(hostif, 0);
if (!usb_endpoint_dir_in(epd) || !usb_endpoint_xfer_int(epd)) {
- snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n");
+ dev_dbg(&umidi->dev->dev, "endpoint[0] isn't interrupt\n");
return -ENXIO;
}
epd = get_endpoint(hostif, 2);
if (!usb_endpoint_dir_out(epd) || !usb_endpoint_xfer_bulk(epd)) {
- snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n");
+ dev_dbg(&umidi->dev->dev, "endpoint[2] isn't bulk output\n");
return -ENXIO;
}
if (endpoint->out_cables > 0x0001) {
epd = get_endpoint(hostif, 4);
if (!usb_endpoint_dir_out(epd) ||
!usb_endpoint_xfer_bulk(epd)) {
- snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n");
+ dev_dbg(&umidi->dev->dev, "endpoint[4] isn't bulk output\n");
return -ENXIO;
}
}
@@ -2022,13 +2144,17 @@ void snd_usbmidi_input_stop(struct list_head* p)
unsigned int i, j;
umidi = list_entry(p, struct snd_usb_midi, list);
+ if (!umidi->input_running)
+ return;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->in)
for (j = 0; j < INPUT_URBS; ++j)
usb_kill_urb(ep->in->urbs[j]);
}
+ umidi->input_running = 0;
}
+EXPORT_SYMBOL(snd_usbmidi_input_stop);
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
{
@@ -2052,9 +2178,13 @@ void snd_usbmidi_input_start(struct list_head* p)
int i;
umidi = list_entry(p, struct snd_usb_midi, list);
+ if (umidi->input_running || !umidi->opened[1])
+ return;
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
+ umidi->input_running = 1;
}
+EXPORT_SYMBOL(snd_usbmidi_input_start);
/*
* Creates and registers everything needed for a MIDI streaming interface.
@@ -2079,6 +2209,7 @@ int snd_usbmidi_create(struct snd_card *card,
umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
init_timer(&umidi->error_timer);
spin_lock_init(&umidi->disc_lock);
+ init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex);
umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
le16_to_cpu(umidi->dev->descriptor.idProduct));
@@ -2105,6 +2236,9 @@ int snd_usbmidi_create(struct snd_card *card,
case QUIRK_MIDI_YAMAHA:
err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]);
break;
+ case QUIRK_MIDI_ROLAND:
+ err = snd_usbmidi_detect_roland(umidi, &endpoints[0]);
+ break;
case QUIRK_MIDI_MIDIMAN:
umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops;
memcpy(&endpoints[0], quirk->data,
@@ -2115,7 +2249,7 @@ int snd_usbmidi_create(struct snd_card *card,
umidi->usb_protocol_ops = &snd_usbmidi_novation_ops;
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
- case QUIRK_MIDI_FASTLANE:
+ case QUIRK_MIDI_RAW_BYTES:
umidi->usb_protocol_ops = &snd_usbmidi_raw_ops;
/*
* Interface 1 contains isochronous endpoints, but with the same
@@ -2126,7 +2260,8 @@ int snd_usbmidi_create(struct snd_card *card,
* interface 0, so we have to make sure that the USB core looks
* again at interface 0 by calling usb_set_interface() on it.
*/
- usb_set_interface(umidi->dev, 0, 0);
+ if (umidi->usb_id == USB_ID(0x07fd, 0x0001)) /* MOTU Fastlane */
+ usb_set_interface(umidi->dev, 0, 0);
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
case QUIRK_MIDI_EMAGIC:
@@ -2145,8 +2280,19 @@ int snd_usbmidi_create(struct snd_card *card,
/* endpoint 1 is input-only */
endpoints[1].out_cables = 0;
break;
+ case QUIRK_MIDI_FTDI:
+ umidi->usb_protocol_ops = &snd_usbmidi_ftdi_ops;
+
+ /* set baud rate to 31250 (48 MHz / 16 / 96) */
+ err = usb_control_msg(umidi->dev, usb_sndctrlpipe(umidi->dev, 0),
+ 3, 0x40, 0x60, 0, NULL, 0, 1000);
+ if (err < 0)
+ break;
+
+ err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
+ break;
default:
- snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
+ dev_err(&umidi->dev->dev, "invalid quirk type %d\n", quirk->type);
err = -ENXIO;
break;
}
@@ -2178,14 +2324,9 @@ int snd_usbmidi_create(struct snd_card *card,
return err;
}
- list_add_tail(&umidi->list, midi_list);
+ usb_autopm_get_interface_no_resume(umidi->iface);
- for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
- snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
+ list_add_tail(&umidi->list, midi_list);
return 0;
}
-
EXPORT_SYMBOL(snd_usbmidi_create);
-EXPORT_SYMBOL(snd_usbmidi_input_stop);
-EXPORT_SYMBOL(snd_usbmidi_input_start);
-EXPORT_SYMBOL(snd_usbmidi_disconnect);
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index fb5d68fa7ff..a1bab149df4 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -52,7 +52,7 @@ MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101},{Edirol,UA-1000}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static unsigned int queue_length = 21;
module_param_array(index, int, NULL, 0444);
@@ -459,7 +459,8 @@ static void kill_stream_urbs(struct ua101_stream *stream)
unsigned int i;
for (i = 0; i < stream->queue_length; ++i)
- usb_kill_urb(&stream->urbs[i]->urb);
+ if (stream->urbs[i])
+ usb_kill_urb(&stream->urbs[i]->urb);
}
static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index)
@@ -484,6 +485,9 @@ static void disable_iso_interface(struct ua101 *ua, unsigned int intf_index)
{
struct usb_host_interface *alts;
+ if (!ua->intf[intf_index])
+ return;
+
alts = ua->intf[intf_index]->cur_altsetting;
if (alts->desc.bAlternateSetting != 0) {
int err = usb_set_interface(ua->dev,
@@ -609,14 +613,24 @@ static int start_usb_playback(struct ua101 *ua)
static void abort_alsa_capture(struct ua101 *ua)
{
- if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states))
+ unsigned long flags;
+
+ if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) {
+ snd_pcm_stream_lock_irqsave(ua->capture.substream, flags);
snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(ua->capture.substream, flags);
+ }
}
static void abort_alsa_playback(struct ua101 *ua)
{
- if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
+ unsigned long flags;
+
+ if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) {
+ snd_pcm_stream_lock_irqsave(ua->playback.substream, flags);
snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(ua->playback.substream, flags);
+ }
}
static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream,
@@ -645,7 +659,7 @@ static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream,
err = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1500000 / ua->packets_per_second,
- 8192000);
+ UINT_MAX);
if (err < 0)
return err;
err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24);
@@ -1116,8 +1130,7 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream,
usb_init_urb(&urb->urb);
urb->urb.dev = ua->dev;
urb->urb.pipe = stream->usb_pipe;
- urb->urb.transfer_flags = URB_ISO_ASAP |
- URB_NO_TRANSFER_DMA_MAP;
+ urb->urb.transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->urb.transfer_buffer = addr;
urb->urb.transfer_dma = dma;
urb->urb.transfer_buffer_length = max_packet_size;
@@ -1144,27 +1157,37 @@ static void free_stream_urbs(struct ua101_stream *stream)
{
unsigned int i;
- for (i = 0; i < stream->queue_length; ++i)
+ for (i = 0; i < stream->queue_length; ++i) {
kfree(stream->urbs[i]);
+ stream->urbs[i] = NULL;
+ }
}
static void free_usb_related_resources(struct ua101 *ua,
struct usb_interface *interface)
{
unsigned int i;
+ struct usb_interface *intf;
+ mutex_lock(&ua->mutex);
free_stream_urbs(&ua->capture);
free_stream_urbs(&ua->playback);
+ mutex_unlock(&ua->mutex);
free_stream_buffers(ua, &ua->capture);
free_stream_buffers(ua, &ua->playback);
- for (i = 0; i < ARRAY_SIZE(ua->intf); ++i)
- if (ua->intf[i]) {
- usb_set_intfdata(ua->intf[i], NULL);
- if (ua->intf[i] != interface)
+ for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) {
+ mutex_lock(&ua->mutex);
+ intf = ua->intf[i];
+ ua->intf[i] = NULL;
+ mutex_unlock(&ua->mutex);
+ if (intf) {
+ usb_set_intfdata(intf, NULL);
+ if (intf != interface)
usb_driver_release_interface(&ua101_driver,
- ua->intf[i]);
+ intf);
}
+ }
}
static void ua101_card_free(struct snd_card *card)
@@ -1220,8 +1243,9 @@ static int ua101_probe(struct usb_interface *interface,
mutex_unlock(&devices_mutex);
return -ENOENT;
}
- err = snd_card_create(index[card_index], id[card_index], THIS_MODULE,
- sizeof(*ua), &card);
+ err = snd_card_new(&interface->dev,
+ index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*ua), &card);
if (err < 0) {
mutex_unlock(&devices_mutex);
return err;
@@ -1260,8 +1284,6 @@ static int ua101_probe(struct usb_interface *interface,
}
}
- snd_card_set_dev(card, &interface->dev);
-
err = detect_usb_format(ua);
if (err < 0)
goto probe_error;
@@ -1336,7 +1358,7 @@ static void ua101_disconnect(struct usb_interface *interface)
snd_card_disconnect(ua->card);
/* make sure that there are no pending USB requests */
- __list_for_each(midi, &ua->midi_list)
+ list_for_each(midi, &ua->midi_list)
snd_usbmidi_disconnect(midi);
abort_alsa_playback(ua);
abort_alsa_capture(ua);
@@ -1373,16 +1395,4 @@ static struct usb_driver ua101_driver = {
#endif
};
-static int __init alsa_card_ua101_init(void)
-{
- return usb_register(&ua101_driver);
-}
-
-static void __exit alsa_card_ua101_exit(void)
-{
- usb_deregister(&ua101_driver);
- mutex_destroy(&devices_mutex);
-}
-
-module_init(alsa_card_ua101_init);
-module_exit(alsa_card_ua101_exit);
+module_usb_driver(ua101_driver);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 736d134cc03..0b728d886f0 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -26,6 +26,22 @@
*
*/
+/*
+ * TODOs, for both the mixer and the streaming interfaces:
+ *
+ * - support for UAC2 effect units
+ * - support for graphical equalizers
+ * - RANGE and MEM set commands (UAC2)
+ * - RANGE and MEM interrupt dispatchers (UAC2)
+ * - audio channel clustering (UAC2)
+ * - audio sample rate converter units (UAC2)
+ * - proper handling of clock multipliers (UAC2)
+ * - dispatch clock change notifications (UAC2)
+ * - stop PCM streams which use a clock that became invalid
+ * - stop PCM streams which use a clock selector that has changed
+ * - parse available sample rates again when clock sources changed
+ */
+
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -45,6 +61,7 @@
#include "mixer.h"
#include "helper.h"
#include "mixer_quirks.h"
+#include "power.h"
#define MAX_ID_ELEMS 256
@@ -69,17 +86,7 @@ struct mixer_build {
const struct usbmix_selector_map *selector_map;
};
-enum {
- USB_MIXER_BOOLEAN,
- USB_MIXER_INV_BOOLEAN,
- USB_MIXER_S8,
- USB_MIXER_U8,
- USB_MIXER_S16,
- USB_MIXER_U16,
-};
-
-
-/*E-mu 0202(0404) eXtension Unit(XU) control*/
+/*E-mu 0202/0404/0204 eXtension Unit(XU) control*/
enum {
USB_XU_CLOCK_RATE = 0xe301,
USB_XU_CLOCK_SOURCE = 0xe302,
@@ -145,6 +152,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p,
if (p && p->dB) {
cval->dBmin = p->dB->min;
cval->dBmax = p->dB->max;
+ cval->initialized = 1;
}
}
@@ -154,7 +162,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
{
const struct usbmix_selector_map *p;
- if (! state->selector_map)
+ if (!state->selector_map)
return 0;
for (p = state->selector_map; p->id; p++) {
if (p->id == unitid && index < p->count)
@@ -166,7 +174,8 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
/*
* find an audio control unit with the given unit id
*/
-static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
+static void *find_audio_control_unit(struct mixer_build *state,
+ unsigned char unit)
{
/* we just parse the header */
struct uac_feature_unit_descriptor *hdr = NULL;
@@ -186,7 +195,8 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
/*
* copy a string with the given id
*/
-static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen)
+static int snd_usb_copy_string_desc(struct mixer_build *state,
+ int index, char *buf, int maxlen)
{
int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
buf[len] = 0;
@@ -245,7 +255,7 @@ static int convert_bytes_value(struct usb_mixer_elem_info *cval, int val)
static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
{
- if (! cval->res)
+ if (!cval->res)
cval->res = 1;
if (val < cval->min)
return 0;
@@ -259,7 +269,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
{
if (val < 0)
return cval->min;
- if (! cval->res)
+ if (!cval->res)
cval->res = 1;
val *= cval->res;
val += cval->min;
@@ -273,33 +283,50 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
* retrieve a mixer value
*/
-static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
+ int validx, int *value_ret)
{
+ struct snd_usb_audio *chip = cval->mixer->chip;
unsigned char buf[2];
int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
int timeout = 10;
+ int idx = 0, err;
+ err = snd_usb_autoresume(cval->mixer->chip);
+ if (err < 0)
+ return -EIO;
+
+ down_read(&chip->shutdown_rwsem);
while (timeout-- > 0) {
- if (snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
- request,
+ if (chip->shutdown)
+ break;
+ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- validx, cval->mixer->ctrlif | (cval->id << 8),
- buf, val_len, 100) >= val_len) {
+ validx, idx, buf, val_len) >= val_len) {
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
- return 0;
+ err = 0;
+ goto out;
}
}
- snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
- request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
- return -EINVAL;
+ usb_audio_dbg(chip,
+ "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+ request, validx, idx, cval->val_type);
+ err = -EINVAL;
+
+ out:
+ up_read(&chip->shutdown_rwsem);
+ snd_usb_autosuspend(cval->mixer->chip);
+ return err;
}
-static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
+ int validx, int *value_ret)
{
- unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
+ struct snd_usb_audio *chip = cval->mixer->chip;
+ unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */
unsigned char *val;
- int ret, size;
+ int idx = 0, ret, size;
__u8 bRequest;
if (request == UAC_GET_CUR) {
@@ -312,16 +339,27 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
memset(buf, 0, sizeof(buf));
- ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
- bRequest,
+ ret = snd_usb_autoresume(chip) ? -EIO : 0;
+ if (ret)
+ goto error;
+
+ down_read(&chip->shutdown_rwsem);
+ if (chip->shutdown) {
+ ret = -ENODEV;
+ } else {
+ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- validx, cval->mixer->ctrlif | (cval->id << 8),
- buf, size, 1000);
+ validx, idx, buf, size);
+ }
+ up_read(&chip->shutdown_rwsem);
+ snd_usb_autosuspend(chip);
if (ret < 0) {
- snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
- request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+error:
+ usb_audio_err(chip,
+ "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+ request, validx, idx, cval->val_type);
return ret;
}
@@ -349,14 +387,18 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
return 0;
}
-static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value(struct usb_mixer_elem_info *cval, int request,
+ int validx, int *value_ret)
{
+ validx += cval->idx_off;
+
return (cval->mixer->protocol == UAC_VERSION_1) ?
get_ctl_value_v1(cval, request, validx, value_ret) :
get_ctl_value_v2(cval, request, validx, value_ret);
}
-static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value)
+static int get_cur_ctl_value(struct usb_mixer_elem_info *cval,
+ int validx, int *value)
{
return get_ctl_value(cval, UAC_GET_CUR, validx, value);
}
@@ -365,7 +407,9 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
int channel, int *value)
{
- return get_ctl_value(cval, UAC_GET_CUR, (cval->control << 8) | channel, value);
+ return get_ctl_value(cval, UAC_GET_CUR,
+ (cval->control << 8) | channel,
+ value);
}
static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
@@ -380,8 +424,9 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
err = get_cur_mix_raw(cval, channel, value);
if (err < 0) {
if (!cval->mixer->ignore_ctl_error)
- snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n",
- cval->control, channel, err);
+ usb_audio_dbg(cval->mixer->chip,
+ "cannot get current value for control %d ch %d: err = %d\n",
+ cval->control, channel, err);
return err;
}
cval->cached |= 1 << channel;
@@ -389,7 +434,6 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
return 0;
}
-
/*
* set a mixer value
*/
@@ -397,8 +441,11 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set)
{
+ struct snd_usb_audio *chip = cval->mixer->chip;
unsigned char buf[2];
- int val_len, timeout = 10;
+ int idx = 0, val_len, err, timeout = 10;
+
+ validx += cval->idx_off;
if (cval->mixer->protocol == UAC_VERSION_1) {
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -408,7 +455,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
/* FIXME */
if (request != UAC_SET_CUR) {
- snd_printdd(KERN_WARNING "RANGE setting not yet supported\n");
+ usb_audio_dbg(chip, "RANGE setting not yet supported\n");
return -EINVAL;
}
@@ -418,20 +465,34 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
value_set = convert_bytes_value(cval, value_set);
buf[0] = value_set & 0xff;
buf[1] = (value_set >> 8) & 0xff;
- while (timeout-- > 0)
- if (snd_usb_ctl_msg(cval->mixer->chip->dev,
- usb_sndctrlpipe(cval->mixer->chip->dev, 0),
- request,
+ err = snd_usb_autoresume(chip);
+ if (err < 0)
+ return -EIO;
+ down_read(&chip->shutdown_rwsem);
+ while (timeout-- > 0) {
+ if (chip->shutdown)
+ break;
+ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ if (snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- validx, cval->mixer->ctrlif | (cval->id << 8),
- buf, val_len, 100) >= 0)
- return 0;
- snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
- request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
- return -EINVAL;
+ validx, idx, buf, val_len) >= 0) {
+ err = 0;
+ goto out;
+ }
+ }
+ usb_audio_dbg(chip, "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
+ request, validx, idx, cval->val_type, buf[0], buf[1]);
+ err = -EINVAL;
+
+ out:
+ up_read(&chip->shutdown_rwsem);
+ snd_usb_autosuspend(chip);
+ return err;
}
-static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
+static int set_cur_ctl_value(struct usb_mixer_elem_info *cval,
+ int validx, int value)
{
return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value);
}
@@ -445,13 +506,15 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
cval->ch_readonly & (1 << (channel - 1));
if (read_only) {
- snd_printdd(KERN_INFO "%s(): channel %d of control %d is read_only\n",
+ usb_audio_dbg(cval->mixer->chip,
+ "%s(): channel %d of control %d is read_only\n",
__func__, channel, cval->control);
return 0;
}
- err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
- value);
+ err = snd_usb_mixer_set_ctl_value(cval,
+ UAC_SET_CUR, (cval->control << 8) | channel,
+ value);
if (err < 0)
return err;
cval->cached |= 1 << channel;
@@ -462,7 +525,7 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
/*
* TLV callback for mixer volume controls
*/
-static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
@@ -488,13 +551,13 @@ static int parse_audio_unit(struct mixer_build *state, int unitid);
* check if the input/output channel routing is enabled on the given bitmap.
* used for mixer unit parser
*/
-static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs)
+static int check_matrix_bitmap(unsigned char *bmap,
+ int ich, int och, int num_outs)
{
int idx = ich * num_outs + och;
return bmap[idx >> 3] & (0x80 >> (idx & 7));
}
-
/*
* add an alsa control element
* search and increment the index until an empty slot is found.
@@ -502,24 +565,25 @@ static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_ou
* if failed, give up and free the control instance.
*/
-static int add_control_to_empty(struct mixer_build *state, struct snd_kcontrol *kctl)
+int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
{
struct usb_mixer_elem_info *cval = kctl->private_data;
int err;
- while (snd_ctl_find_id(state->chip->card, &kctl->id))
+ while (snd_ctl_find_id(mixer->chip->card, &kctl->id))
kctl->id.index++;
- if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) {
- snd_printd(KERN_ERR "cannot add control (err = %d)\n", err);
+ if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) {
+ usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n",
+ err);
return err;
}
cval->elem_id = &kctl->id;
- cval->next_id_elem = state->mixer->id_elems[cval->id];
- state->mixer->id_elems[cval->id] = cval;
+ cval->next_id_elem = mixer->id_elems[cval->id];
+ mixer->id_elems[cval->id] = cval;
return 0;
}
-
/*
* get a terminal name string
*/
@@ -573,7 +637,8 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
struct iterm_name_combo *names;
if (iterm->name)
- return snd_usb_copy_string_desc(state, iterm->name, name, maxlen);
+ return snd_usb_copy_string_desc(state, iterm->name,
+ name, maxlen);
/* virtual type - not a real terminal */
if (iterm->type >> 16) {
@@ -581,13 +646,17 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
return 0;
switch (iterm->type >> 16) {
case UAC_SELECTOR_UNIT:
- strcpy(name, "Selector"); return 8;
- case UAC_PROCESSING_UNIT_V1:
- strcpy(name, "Process Unit"); return 12;
- case UAC_EXTENSION_UNIT_V1:
- strcpy(name, "Ext Unit"); return 8;
+ strcpy(name, "Selector");
+ return 8;
+ case UAC1_PROCESSING_UNIT:
+ strcpy(name, "Process Unit");
+ return 12;
+ case UAC1_EXTENSION_UNIT:
+ strcpy(name, "Ext Unit");
+ return 8;
case UAC_MIXER_UNIT:
- strcpy(name, "Mixer"); return 5;
+ strcpy(name, "Mixer");
+ return 5;
default:
return sprintf(name, "Unit %d", iterm->id);
}
@@ -595,29 +664,35 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
switch (iterm->type & 0xff00) {
case 0x0100:
- strcpy(name, "PCM"); return 3;
+ strcpy(name, "PCM");
+ return 3;
case 0x0200:
- strcpy(name, "Mic"); return 3;
+ strcpy(name, "Mic");
+ return 3;
case 0x0400:
- strcpy(name, "Headset"); return 7;
+ strcpy(name, "Headset");
+ return 7;
case 0x0500:
- strcpy(name, "Phone"); return 5;
+ strcpy(name, "Phone");
+ return 5;
}
- for (names = iterm_names; names->type; names++)
+ for (names = iterm_names; names->type; names++) {
if (names->type == iterm->type) {
strcpy(name, names->name);
return strlen(names->name);
}
+ }
+
return 0;
}
-
/*
* parse the source unit recursively until it reaches to a terminal
* or a branched unit.
*/
-static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
+static int check_input_term(struct mixer_build *state, int id,
+ struct usb_audio_term *term)
{
int err;
void *p1;
@@ -665,16 +740,29 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
case UAC2_CLOCK_SELECTOR: {
struct uac_selector_unit_descriptor *d = p1;
/* call recursively to retrieve the channel info */
- if (check_input_term(state, d->baSourceID[0], term) < 0)
- return -ENODEV;
+ err = check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
term->type = d->bDescriptorSubtype << 16; /* virtual type */
term->id = id;
term->name = uac_selector_unit_iSelector(d);
return 0;
}
- case UAC_PROCESSING_UNIT_V1:
- case UAC_EXTENSION_UNIT_V1: {
+ case UAC1_PROCESSING_UNIT:
+ case UAC1_EXTENSION_UNIT:
+ /* UAC2_PROCESSING_UNIT_V2 */
+ /* UAC2_EFFECT_UNIT */
+ case UAC2_EXTENSION_UNIT_V2: {
struct uac_processing_unit_descriptor *d = p1;
+
+ if (state->mixer->protocol == UAC_VERSION_2 &&
+ hdr[2] == UAC2_EFFECT_UNIT) {
+ /* UAC2/UAC1 unit IDs overlap here in an
+ * uncompatible way. Ignore this unit for now.
+ */
+ return 0;
+ }
+
if (d->bNrInPins) {
id = d->baSourceID[0];
break; /* continue to parse */
@@ -699,7 +787,6 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
return -ENODEV;
}
-
/*
* Feature Unit
*/
@@ -727,7 +814,6 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ "Phase Inverter Control", USB_MIXER_BOOLEAN },
};
-
/* private_free callback */
static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
{
@@ -735,15 +821,117 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
kctl->private_data = NULL;
}
-
/*
* interface to ALSA control for feature/mixer units
*/
+/* volume control quirks */
+static void volume_control_quirks(struct usb_mixer_elem_info *cval,
+ struct snd_kcontrol *kctl)
+{
+ struct snd_usb_audio *chip = cval->mixer->chip;
+ switch (chip->usb_id) {
+ case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
+ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
+ if (strcmp(kctl->id.name, "Effect Duration") == 0) {
+ cval->min = 0x0000;
+ cval->max = 0xffff;
+ cval->res = 0x00e6;
+ break;
+ }
+ if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
+ strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
+ cval->min = 0x00;
+ cval->max = 0xff;
+ break;
+ }
+ if (strstr(kctl->id.name, "Effect Return") != NULL) {
+ cval->min = 0xb706;
+ cval->max = 0xff7b;
+ cval->res = 0x0073;
+ break;
+ }
+ if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
+ (strstr(kctl->id.name, "Effect Send") != NULL)) {
+ cval->min = 0xb5fb; /* -73 dB = 0xb6ff */
+ cval->max = 0xfcfe;
+ cval->res = 0x0073;
+ }
+ break;
+
+ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+ if (strcmp(kctl->id.name, "Effect Duration") == 0) {
+ usb_audio_info(chip,
+ "set quirk for FTU Effect Duration\n");
+ cval->min = 0x0000;
+ cval->max = 0x7f00;
+ cval->res = 0x0100;
+ break;
+ }
+ if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
+ strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
+ usb_audio_info(chip,
+ "set quirks for FTU Effect Feedback/Volume\n");
+ cval->min = 0x00;
+ cval->max = 0x7f;
+ break;
+ }
+ break;
+
+ case USB_ID(0x0471, 0x0101):
+ case USB_ID(0x0471, 0x0104):
+ case USB_ID(0x0471, 0x0105):
+ case USB_ID(0x0672, 0x1041):
+ /* quirk for UDA1321/N101.
+ * note that detection between firmware 2.1.1.7 (N101)
+ * and later 2.1.1.21 is not very clear from datasheets.
+ * I hope that the min value is -15360 for newer firmware --jk
+ */
+ if (!strcmp(kctl->id.name, "PCM Playback Volume") &&
+ cval->min == -15616) {
+ usb_audio_info(chip,
+ "set volume quirk for UDA1321/N101 chip\n");
+ cval->max = -256;
+ }
+ break;
+
+ case USB_ID(0x046d, 0x09a4):
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set volume quirk for QuickCam E3500\n");
+ cval->min = 6080;
+ cval->max = 8768;
+ cval->res = 192;
+ }
+ break;
+
+ case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */
+ case USB_ID(0x046d, 0x0808):
+ case USB_ID(0x046d, 0x0809):
+ case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */
+ case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */
+ case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */
+ case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
+ case USB_ID(0x046d, 0x0991):
+ /* Most audio usb devices lie about volume resolution.
+ * Most Logitech webcams have res = 384.
+ * Proboly there is some logitech magic behind this number --fishor
+ */
+ if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+ usb_audio_info(chip,
+ "set resolution quirk: cval->res = 384\n");
+ cval->res = 384;
+ }
+ break;
+ }
+}
+
/*
* retrieve the minimum and maximum values for the specified control
*/
-static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
+static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
+ int default_min, struct snd_kcontrol *kctl)
{
/* for failsafe */
cval->min = default_min;
@@ -766,22 +954,28 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
}
if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
- snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
- cval->id, cval->mixer->ctrlif, cval->control, cval->id);
+ usb_audio_err(cval->mixer->chip,
+ "%d:%d: cannot get min/max values for control %d (id %d)\n",
+ cval->id, snd_usb_ctrl_intf(cval->mixer->chip),
+ cval->control, cval->id);
return -EINVAL;
}
- if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
+ if (get_ctl_value(cval, UAC_GET_RES,
+ (cval->control << 8) | minchn,
+ &cval->res) < 0) {
cval->res = 1;
} else {
int last_valid_res = cval->res;
while (cval->res > 1) {
if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES,
- (cval->control << 8) | minchn, cval->res / 2) < 0)
+ (cval->control << 8) | minchn,
+ cval->res / 2) < 0)
break;
cval->res /= 2;
}
- if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0)
+ if (get_ctl_value(cval, UAC_GET_RES,
+ (cval->control << 8) | minchn, &cval->res) < 0)
cval->res = last_valid_res;
}
if (cval->res == 0)
@@ -819,6 +1013,9 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
cval->initialized = 1;
}
+ if (kctl)
+ volume_control_quirks(cval, kctl);
+
/* USB descriptions contain the dB scale in 1/256 dB unit
* while ALSA TLV contains in 1/100 dB unit
*/
@@ -839,9 +1036,11 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
return 0;
}
+#define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL)
/* get a feature/mixer unit info */
-static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
@@ -856,8 +1055,17 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
} else {
- if (! cval->initialized)
- get_min_max(cval, 0);
+ if (!cval->initialized) {
+ get_min_max_with_quirks(cval, 0, kcontrol);
+ if (cval->initialized && cval->dBmin >= cval->dBmax) {
+ kcontrol->vd[0].access &=
+ ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
+ snd_ctl_notify(cval->mixer->chip->card,
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &kcontrol->id);
+ }
+ }
uinfo->value.integer.min = 0;
uinfo->value.integer.max =
(cval->max - cval->min + cval->res - 1) / cval->res;
@@ -866,7 +1074,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
/* get the current value from feature/mixer unit */
-static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int c, cnt, val, err;
@@ -897,7 +1106,8 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
/* put the current value to feature/mixer unit */
-static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int c, cnt, val, oval, err;
@@ -951,16 +1161,48 @@ static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
.put = NULL,
};
+/*
+ * This symbol is exported in order to allow the mixer quirks to
+ * hook up to the standard feature unit control mechanism
+ */
+struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl;
/*
* build a feature control
*/
-
static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
{
return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
}
+/*
+ * A lot of headsets/headphones have a "Speaker" mixer. Make sure we
+ * rename it to "Headphone". We determine if something is a headphone
+ * similar to how udev determines form factor.
+ */
+static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
+ struct snd_card *card)
+{
+ const char *names_to_check[] = {
+ "Headset", "headset", "Headphone", "headphone", NULL};
+ const char **s;
+ bool found = false;
+
+ if (strcmp("Speaker", kctl->id.name))
+ return;
+
+ for (s = names_to_check; *s; s++)
+ if (strstr(card->shortname, *s)) {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return;
+
+ strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name));
+}
+
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
unsigned int ctl_mask, int control,
struct usb_audio_term *iterm, int unitid,
@@ -973,6 +1215,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
+ unsigned int range;
control++; /* change from zero-based to 1-based value */
@@ -986,10 +1229,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (! cval) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!cval)
return;
- }
cval->mixer = state->mixer;
cval->id = unitid;
cval->control = control;
@@ -1007,19 +1248,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
cval->ch_readonly = readonly_mask;
}
- /* get min/max values */
- get_min_max(cval, 0);
-
- /* if all channels in the mask are marked read-only, make the control
+ /*
+ * If all channels in the mask are marked read-only, make the control
* read-only. set_cur_mix_value() will check the mask again and won't
- * issue write commands to read-only channels. */
+ * issue write commands to read-only channels.
+ */
if (cval->channels == readonly_mask)
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
else
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
- if (! kctl) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!kctl) {
+ usb_audio_err(state->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
}
@@ -1027,114 +1267,101 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
mapped_name = len != 0;
- if (! len && nameid)
+ if (!len && nameid)
len = snd_usb_copy_string_desc(state, nameid,
kctl->id.name, sizeof(kctl->id.name));
switch (control) {
case UAC_FU_MUTE:
case UAC_FU_VOLUME:
- /* determine the control name. the rule is:
+ /*
+ * determine the control name. the rule is:
* - if a name id is given in descriptor, use it.
* - if the connected input can be determined, then use the name
* of terminal type.
* - if the connected output can be determined, use it.
* - otherwise, anonymous name.
*/
- if (! len) {
- len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1);
- if (! len)
- len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1);
- if (! len)
- len = snprintf(kctl->id.name, sizeof(kctl->id.name),
+ if (!len) {
+ len = get_term_name(state, iterm, kctl->id.name,
+ sizeof(kctl->id.name), 1);
+ if (!len)
+ len = get_term_name(state, &state->oterm,
+ kctl->id.name,
+ sizeof(kctl->id.name), 1);
+ if (!len)
+ len = snprintf(kctl->id.name,
+ sizeof(kctl->id.name),
"Feature %d", unitid);
}
- /* determine the stream direction:
+
+ if (!mapped_name)
+ check_no_speaker_on_headset(kctl, state->mixer->chip->card);
+
+ /*
+ * determine the stream direction:
* if the connected output is USB stream, then it's likely a
* capture stream. otherwise it should be playback (hopefully :)
*/
- if (! mapped_name && ! (state->oterm.type >> 16)) {
- if ((state->oterm.type & 0xff00) == 0x0100) {
+ if (!mapped_name && !(state->oterm.type >> 16)) {
+ if ((state->oterm.type & 0xff00) == 0x0100)
len = append_ctl_name(kctl, " Capture");
- } else {
+ else
len = append_ctl_name(kctl, " Playback");
- }
}
append_ctl_name(kctl, control == UAC_FU_MUTE ?
" Switch" : " Volume");
- if (control == UAC_FU_VOLUME) {
- kctl->tlv.c = mixer_vol_tlv;
- kctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
- check_mapped_dB(map, cval);
- }
break;
-
default:
- if (! len)
+ if (!len)
strlcpy(kctl->id.name, audio_feature_info[control-1].name,
sizeof(kctl->id.name));
break;
}
- /* volume control quirks */
- switch (state->chip->usb_id) {
- case USB_ID(0x0471, 0x0101):
- case USB_ID(0x0471, 0x0104):
- case USB_ID(0x0471, 0x0105):
- case USB_ID(0x0672, 0x1041):
- /* quirk for UDA1321/N101.
- * note that detection between firmware 2.1.1.7 (N101)
- * and later 2.1.1.21 is not very clear from datasheets.
- * I hope that the min value is -15360 for newer firmware --jk
- */
- if (!strcmp(kctl->id.name, "PCM Playback Volume") &&
- cval->min == -15616) {
- snd_printk(KERN_INFO
- "set volume quirk for UDA1321/N101 chip\n");
- cval->max = -256;
- }
- break;
-
- case USB_ID(0x046d, 0x09a4):
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
- snd_printk(KERN_INFO
- "set volume quirk for QuickCam E3500\n");
- cval->min = 6080;
- cval->max = 8768;
- cval->res = 192;
- }
- break;
+ /* get min/max values */
+ get_min_max_with_quirks(cval, 0, kctl);
- case USB_ID(0x046d, 0x0809):
- case USB_ID(0x046d, 0x0991):
- /* Most audio usb devices lie about volume resolution.
- * Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
- */
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
- snd_printk(KERN_INFO
- "set resolution quirk: cval->res = 384\n");
- cval->res = 384;
+ if (control == UAC_FU_VOLUME) {
+ check_mapped_dB(map, cval);
+ if (cval->dBmin < cval->dBmax || !cval->initialized) {
+ kctl->tlv.c = snd_usb_mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
- break;
-
}
- snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
- cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
- add_control_to_empty(state, kctl);
+ range = (cval->max - cval->min) / cval->res;
+ /*
+ * Are there devices with volume range more than 255? I use a bit more
+ * to be sure. 384 is a resolution magic number found on Logitech
+ * devices. It will definitively catch all buggy Logitech devices.
+ */
+ if (range > 384) {
+ usb_audio_warn(state->chip,
+ "Warning! Unlikely big volume range (=%u), "
+ "cval->res is probably wrong.",
+ range);
+ usb_audio_warn(state->chip, "[%d] FU [%s] ch = %d, "
+ "val = %d/%d/%d", cval->id,
+ kctl->id.name, cval->channels,
+ cval->min, cval->max, cval->res);
+ }
+
+ usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
+ cval->id, kctl->id.name, cval->channels,
+ cval->min, cval->max, cval->res);
+ snd_usb_mixer_add_control(state->mixer, kctl);
}
-
-
/*
* parse a feature unit
*
- * most of controlls are defined here.
+ * most of controls are defined here.
*/
-static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr)
+static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
+ void *_ftr)
{
int channels, i, j;
struct usb_audio_term iterm;
@@ -1145,18 +1372,31 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
if (state->mixer->protocol == UAC_VERSION_1) {
csize = hdr->bControlSize;
+ if (!csize) {
+ usb_audio_dbg(state->chip,
+ "unit %u: invalid bControlSize == 0\n",
+ unitid);
+ return -EINVAL;
+ }
channels = (hdr->bLength - 7) / csize - 1;
bmaControls = hdr->bmaControls;
+ if (hdr->bLength < 7 + csize) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
} else {
struct uac2_feature_unit_descriptor *ftr = _ftr;
csize = 4;
channels = (hdr->bLength - 6) / 4 - 1;
bmaControls = ftr->bmaControls;
- }
-
- if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) {
- snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid);
- return -EINVAL;
+ if (hdr->bLength < 6 + csize) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
}
/* parse the source unit */
@@ -1164,18 +1404,26 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
return err;
/* determine the input source type and name */
- if (check_input_term(state, hdr->bSourceID, &iterm) < 0)
- return -EINVAL;
+ err = check_input_term(state, hdr->bSourceID, &iterm);
+ if (err < 0)
+ return err;
master_bits = snd_usb_combine_bytes(bmaControls, csize);
/* master configuration quirks */
switch (state->chip->usb_id) {
case USB_ID(0x08bb, 0x2702):
- snd_printk(KERN_INFO
- "usbmixer: master volume quirk for PCM2702 chip\n");
+ usb_audio_info(state->chip,
+ "usbmixer: master volume quirk for PCM2702 chip\n");
/* disable non-functional volume control */
master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
break;
+ case USB_ID(0x1130, 0xf211):
+ usb_audio_info(state->chip,
+ "usbmixer: volume control quirk for Tenx TP6911 Audio Headset\n");
+ /* disable non-functional volume control */
+ channels = 0;
+ break;
+
}
if (channels > 0)
first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
@@ -1187,31 +1435,36 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
for (i = 0; i < 10; i++) {
unsigned int ch_bits = 0;
for (j = 0; j < channels; j++) {
- unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+ unsigned int mask;
+
+ mask = snd_usb_combine_bytes(bmaControls +
+ csize * (j+1), csize);
if (mask & (1 << i))
ch_bits |= (1 << j);
}
/* audio class v1 controls are never read-only */
- if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
- build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0);
+
+ /*
+ * The first channel must be set
+ * (for ease of programming).
+ */
+ if (ch_bits & 1)
+ build_feature_ctl(state, _ftr, ch_bits, i,
+ &iterm, unitid, 0);
if (master_bits & (1 << i))
- build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
+ build_feature_ctl(state, _ftr, 0, i, &iterm,
+ unitid, 0);
}
} else { /* UAC_VERSION_2 */
- for (i = 0; i < 30/2; i++) {
- /* From the USB Audio spec v2.0:
- bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
- each containing a set of bit pairs. If a Control is present,
- it must be Host readable. If a certain Control is not
- present then the bit pair must be set to 0b00.
- If a Control is present but read-only, the bit pair must be
- set to 0b01. If a Control is also Host programmable, the bit
- pair must be set to 0b11. The value 0b10 is not allowed. */
+ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
for (j = 0; j < channels; j++) {
- unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+ unsigned int mask;
+
+ mask = snd_usb_combine_bytes(bmaControls +
+ csize * (j+1), csize);
if (uac2_control_is_readable(mask, i)) {
ch_bits |= (1 << j);
if (!uac2_control_is_writeable(mask, i))
@@ -1219,12 +1472,22 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
}
}
- /* NOTE: build_feature_ctl() will mark the control read-only if all channels
- * are marked read-only in the descriptors. Otherwise, the control will be
- * reported as writeable, but the driver will not actually issue a write
- * command for read-only channels */
- if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
- build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only);
+ /*
+ * NOTE: build_feature_ctl() will mark the control
+ * read-only if all channels are marked read-only in
+ * the descriptors. Otherwise, the control will be
+ * reported as writeable, but the driver will not
+ * actually issue a write command for read-only
+ * channels.
+ */
+
+ /*
+ * The first channel must be set
+ * (for ease of programming).
+ */
+ if (ch_bits & 1)
+ build_feature_ctl(state, _ftr, ch_bits, i,
+ &iterm, unitid, ch_read_only);
if (uac2_control_is_readable(master_bits, i))
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
!uac2_control_is_writeable(master_bits, i));
@@ -1234,7 +1497,6 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
return 0;
}
-
/*
* Mixer Unit
*/
@@ -1245,7 +1507,6 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
* the callbacks are identical with feature unit.
* input channel number (zero based) is given in control field instead.
*/
-
static void build_mixer_unit_ctl(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc,
int in_pin, int in_ch, int unitid,
@@ -1262,7 +1523,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (! cval)
+ if (!cval)
return;
cval->mixer = state->mixer;
@@ -1270,7 +1531,9 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
cval->control = in_ch + 1; /* based on 1 */
cval->val_type = USB_MIXER_S16;
for (i = 0; i < num_outs; i++) {
- if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) {
+ __u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
+
+ if (check_matrix_bitmap(c, in_ch, i, num_outs)) {
cval->cmask |= (1 << i);
cval->channels++;
}
@@ -1280,43 +1543,48 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
get_min_max(cval, 0);
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
- if (! kctl) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!kctl) {
+ usb_audio_err(state->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
}
kctl->private_free = usb_mixer_elem_free;
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
- if (! len)
- len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
- if (! len)
+ if (!len)
+ len = get_term_name(state, iterm, kctl->id.name,
+ sizeof(kctl->id.name), 0);
+ if (!len)
len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1);
append_ctl_name(kctl, " Volume");
- snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
+ usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n",
cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
- add_control_to_empty(state, kctl);
+ snd_usb_mixer_add_control(state->mixer, kctl);
}
-
/*
* parse a mixer unit
*/
-static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
+ void *raw_desc)
{
struct uac_mixer_unit_descriptor *desc = raw_desc;
struct usb_audio_term iterm;
int input_pins, num_ins, num_outs;
int pin, ich, err;
- if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) {
- snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid);
+ if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
+ !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
+ usb_audio_err(state->chip,
+ "invalid MIXER UNIT descriptor %d\n",
+ unitid);
return -EINVAL;
}
/* no bmControls field (e.g. Maya44) -> ignore */
if (desc->bLength <= 10 + input_pins) {
- snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid);
+ usb_audio_dbg(state->chip, "MU %d has no bmControls field\n",
+ unitid);
return 0;
}
@@ -1325,17 +1593,19 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
for (pin = 0; pin < input_pins; pin++) {
err = parse_audio_unit(state, desc->baSourceID[pin]);
if (err < 0)
- return err;
+ continue;
err = check_input_term(state, desc->baSourceID[pin], &iterm);
if (err < 0)
return err;
num_ins += iterm.channels;
- for (; ich < num_ins; ++ich) {
+ for (; ich < num_ins; ich++) {
int och, ich_has_controls = 0;
- for (och = 0; och < num_outs; ++och) {
- if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol),
- ich, och, num_outs)) {
+ for (och = 0; och < num_outs; och++) {
+ __u8 *c = uac_mixer_unit_bmControls(desc,
+ state->mixer->protocol);
+
+ if (check_matrix_bitmap(c, ich, och, num_outs)) {
ich_has_controls = 1;
break;
}
@@ -1348,13 +1618,13 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
return 0;
}
-
/*
* Processing Unit / Extension Unit
*/
/* get callback for processing/extension unit */
-static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int err, val;
@@ -1372,7 +1642,8 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
/* put callback for processing/extension unit */
-static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int val, oval, err;
@@ -1401,7 +1672,6 @@ static struct snd_kcontrol_new mixer_procunit_ctl = {
.put = mixer_ctl_procunit_put,
};
-
/*
* predefined data for processing units
*/
@@ -1492,10 +1762,13 @@ static struct procunit_info extunits[] = {
{ USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info },
{ 0 }
};
+
/*
* build a processing/extension unit
*/
-static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name)
+static int build_audio_procunit(struct mixer_build *state, int unitid,
+ void *raw_desc, struct procunit_info *list,
+ char *name)
{
struct uac_processing_unit_descriptor *desc = raw_desc;
int num_ins = desc->bNrInPins;
@@ -1515,7 +1788,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
if (desc->bLength < 13 || desc->bLength < 13 + num_ins ||
desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
- snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
+ usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
return -EINVAL;
}
@@ -1528,22 +1801,20 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
for (info = list; info && info->type; info++)
if (info->type == type)
break;
- if (! info || ! info->type)
+ if (!info || !info->type)
info = &default_info;
for (valinfo = info->values; valinfo->control; valinfo++) {
__u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol);
- if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
+ if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
continue;
map = find_map(state, unitid, valinfo->control);
if (check_ignored_ctl(map))
continue;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (! cval) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!cval)
return -ENOMEM;
- }
cval->mixer = state->mixer;
cval->id = unitid;
cval->control = valinfo->control;
@@ -1560,7 +1831,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
cval->initialized = 1;
} else {
if (type == USB_XU_CLOCK_RATE) {
- /* E-Mu USB 0404/0202/TrackerPre
+ /*
+ * E-Mu USB 0404/0202/TrackerPre/0204
* samplerate control quirk
*/
cval->min = 0;
@@ -1572,76 +1844,81 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
}
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
- if (! kctl) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!kctl) {
kfree(cval);
return -ENOMEM;
}
kctl->private_free = usb_mixer_elem_free;
- if (check_mapped_name(map, kctl->id.name,
- sizeof(kctl->id.name)))
+ if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) {
/* nothing */ ;
- else if (info->name)
+ } else if (info->name) {
strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
- else {
+ } else {
nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
len = 0;
if (nameid)
- len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
- if (! len)
+ len = snd_usb_copy_string_desc(state, nameid,
+ kctl->id.name,
+ sizeof(kctl->id.name));
+ if (!len)
strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
}
append_ctl_name(kctl, " ");
append_ctl_name(kctl, valinfo->suffix);
- snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
- cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
- if ((err = add_control_to_empty(state, kctl)) < 0)
+ usb_audio_dbg(state->chip,
+ "[%d] PU [%s] ch = %d, val = %d/%d\n",
+ cval->id, kctl->id.name, cval->channels,
+ cval->min, cval->max);
+
+ err = snd_usb_mixer_add_control(state->mixer, kctl);
+ if (err < 0)
return err;
}
return 0;
}
-
-static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_processing_unit(struct mixer_build *state, int unitid,
+ void *raw_desc)
{
- return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit");
+ return build_audio_procunit(state, unitid, raw_desc,
+ procunits, "Processing Unit");
}
-static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_extension_unit(struct mixer_build *state, int unitid,
+ void *raw_desc)
{
- /* Note that we parse extension units with processing unit descriptors.
- * That's ok as the layout is the same */
- return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit");
+ /*
+ * Note that we parse extension units with processing unit descriptors.
+ * That's ok as the layout is the same.
+ */
+ return build_audio_procunit(state, unitid, raw_desc,
+ extunits, "Extension Unit");
}
-
/*
* Selector Unit
*/
-/* info callback for selector unit
+/*
+ * info callback for selector unit
* use an enumerator type for routing
*/
-static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
- char **itemlist = (char **)kcontrol->private_value;
+ const char **itemlist = (const char **)kcontrol->private_value;
if (snd_BUG_ON(!itemlist))
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = cval->max;
- if ((int)uinfo->value.enumerated.item >= cval->max)
- uinfo->value.enumerated.item = cval->max - 1;
- strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, cval->max, itemlist);
}
/* get callback for selector unit */
-static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int val, err;
@@ -1660,7 +1937,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
/* put callback for selector unit */
-static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int val, oval, err;
@@ -1689,8 +1967,8 @@ static struct snd_kcontrol_new mixer_selectunit_ctl = {
.put = mixer_ctl_selector_put,
};
-
-/* private free callback.
+/*
+ * private free callback.
* free both private_data and private_value
*/
static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
@@ -1715,7 +1993,8 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
/*
* parse a selector unit
*/
-static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
+ void *raw_desc)
{
struct uac_selector_unit_descriptor *desc = raw_desc;
unsigned int i, nameid, len;
@@ -1726,7 +2005,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
char **namelist;
if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) {
- snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid);
+ usb_audio_err(state->chip,
+ "invalid SELECTOR UNIT descriptor %d\n", unitid);
return -EINVAL;
}
@@ -1743,10 +2023,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
return 0;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (! cval) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ if (!cval)
return -ENOMEM;
- }
cval->mixer = state->mixer;
cval->id = unitid;
cval->val_type = USB_MIXER_U8;
@@ -1762,8 +2040,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
cval->control = 0;
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
- if (! namelist) {
- snd_printk(KERN_ERR "cannot malloc\n");
+ if (!namelist) {
kfree(cval);
return -ENOMEM;
}
@@ -1772,8 +2049,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
struct usb_audio_term iterm;
len = 0;
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
- if (! namelist[i]) {
- snd_printk(KERN_ERR "cannot malloc\n");
+ if (!namelist[i]) {
while (i--)
kfree(namelist[i]);
kfree(namelist);
@@ -1785,12 +2061,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
if (! len)
- sprintf(namelist[i], "Input %d", i);
+ sprintf(namelist[i], "Input %u", i);
}
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) {
- snd_printk(KERN_ERR "cannot malloc kcontrol\n");
+ usb_audio_err(state->chip, "cannot malloc kcontrol\n");
kfree(namelist);
kfree(cval);
return -ENOMEM;
@@ -1803,11 +2079,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
if (len)
;
else if (nameid)
- snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+ snd_usb_copy_string_desc(state, nameid, kctl->id.name,
+ sizeof(kctl->id.name));
else {
len = get_term_name(state, &state->oterm,
kctl->id.name, sizeof(kctl->id.name), 0);
- if (! len)
+ if (!len)
strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name));
if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
@@ -1818,15 +2095,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
append_ctl_name(kctl, " Playback Source");
}
- snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
+ usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
cval->id, kctl->id.name, desc->bNrInPins);
- if ((err = add_control_to_empty(state, kctl)) < 0)
+ if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0)
return err;
return 0;
}
-
/*
* parse an audio unit recursively
*/
@@ -1840,7 +2116,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
p1 = find_audio_control_unit(state, unitid);
if (!p1) {
- snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid);
+ usb_audio_err(state->chip, "unit %d not found!\n", unitid);
return -EINVAL;
}
@@ -1855,20 +2131,23 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return parse_audio_selector_unit(state, unitid, p1);
case UAC_FEATURE_UNIT:
return parse_audio_feature_unit(state, unitid, p1);
- case UAC_PROCESSING_UNIT_V1:
+ case UAC1_PROCESSING_UNIT:
/* UAC2_EFFECT_UNIT has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_processing_unit(state, unitid, p1);
else
return 0; /* FIXME - effect units not implemented yet */
- case UAC_EXTENSION_UNIT_V1:
+ case UAC1_EXTENSION_UNIT:
/* UAC2_PROCESSING_UNIT_V2 has the same value */
if (state->mixer->protocol == UAC_VERSION_1)
return parse_audio_extension_unit(state, unitid, p1);
else /* UAC_VERSION_2 */
return parse_audio_processing_unit(state, unitid, p1);
+ case UAC2_EXTENSION_UNIT_V2:
+ return parse_audio_extension_unit(state, unitid, p1);
default:
- snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
return -EINVAL;
}
}
@@ -1902,15 +2181,13 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
struct mixer_build state;
int err;
const struct usbmix_ctl_map *map;
- struct usb_host_interface *hostif;
void *p;
- hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
memset(&state, 0, sizeof(state));
state.chip = mixer->chip;
state.mixer = mixer;
- state.buffer = hostif->extra;
- state.buflen = hostif->extralen;
+ state.buffer = mixer->hostif->extra;
+ state.buflen = mixer->hostif->extralen;
/* check the mapping table */
for (map = usbmix_ctl_maps; map->id; map++) {
@@ -1923,35 +2200,42 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
}
p = NULL;
- while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
+ while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
+ mixer->hostif->extralen,
+ p, UAC_OUTPUT_TERMINAL)) != NULL) {
if (mixer->protocol == UAC_VERSION_1) {
- struct uac_output_terminal_descriptor_v1 *desc = p;
+ struct uac1_output_terminal_descriptor *desc = p;
if (desc->bLength < sizeof(*desc))
continue; /* invalid descriptor? */
- set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
state.oterm.type = le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
- if (err < 0)
+ if (err < 0 && err != -EINVAL)
return err;
} else { /* UAC_VERSION_2 */
struct uac2_output_terminal_descriptor *desc = p;
if (desc->bLength < sizeof(*desc))
continue; /* invalid descriptor? */
- set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
state.oterm.id = desc->bTerminalID;
state.oterm.type = le16_to_cpu(desc->wTerminalType);
state.oterm.name = desc->iTerminal;
err = parse_audio_unit(&state, desc->bSourceID);
- if (err < 0)
+ if (err < 0 && err != -EINVAL)
return err;
- /* for UAC2, use the same approach to also add the clock selectors */
+ /*
+ * For UAC2, use the same approach to also add the
+ * clock selectors
+ */
err = parse_audio_unit(&state, desc->bCSourceID);
- if (err < 0)
+ if (err < 0 && err != -EINVAL)
return err;
}
}
@@ -1997,7 +2281,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_iprintf(buffer,
"USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n",
- chip->usb_id, mixer->ctrlif,
+ chip->usb_id, snd_usb_ctrl_intf(chip),
mixer->ignore_ctl_error);
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
@@ -2017,8 +2301,9 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
__u8 channel = value & 0xff;
if (channel >= MAX_CHANNELS) {
- snd_printk(KERN_DEBUG "%s(): bogus channel number %d\n",
- __func__, channel);
+ usb_audio_dbg(mixer->chip,
+ "%s(): bogus channel number %d\n",
+ __func__, channel);
return;
}
@@ -2047,8 +2332,9 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
break;
default:
- snd_printk(KERN_DEBUG "unknown attribute %d in interrupt\n",
- attribute);
+ usb_audio_dbg(mixer->chip,
+ "unknown attribute %d in interrupt\n",
+ attribute);
break;
} /* switch */
}
@@ -2058,8 +2344,9 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
{
struct usb_mixer_interface *mixer = urb->context;
int len = urb->actual_length;
+ int ustatus = urb->status;
- if (urb->status != 0)
+ if (ustatus != 0)
goto requeue;
if (mixer->protocol == UAC_VERSION_1) {
@@ -2068,7 +2355,7 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
for (status = urb->transfer_buffer;
len >= sizeof(*status);
len -= sizeof(*status), status++) {
- snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n",
+ dev_dbg(&urb->dev->dev, "status interrupt: %02x %02x\n",
status->bStatusType,
status->bOriginator);
@@ -2100,7 +2387,9 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
}
requeue:
- if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
+ if (ustatus != -ENOENT &&
+ ustatus != -ECONNRESET &&
+ ustatus != -ESHUTDOWN) {
urb->dev = mixer->chip->dev;
usb_submit_urb(urb, GFP_ATOMIC);
}
@@ -2109,17 +2398,15 @@ requeue:
/* create the handler for the optional status interrupt endpoint */
static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
{
- struct usb_host_interface *hostif;
struct usb_endpoint_descriptor *ep;
void *transfer_buffer;
int buffer_length;
unsigned int epnum;
- hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
/* we need one interrupt input endpoint */
- if (get_iface_desc(hostif)->bNumEndpoints < 1)
+ if (get_iface_desc(mixer->hostif)->bNumEndpoints < 1)
return 0;
- ep = get_endpoint(hostif, 0);
+ ep = get_endpoint(mixer->hostif, 0);
if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep))
return 0;
@@ -2149,7 +2436,6 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
};
struct usb_mixer_interface *mixer;
struct snd_info_entry *entry;
- struct usb_host_interface *host_iface;
int err;
strcpy(chip->card->mixername, "USB Mixer");
@@ -2158,7 +2444,6 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
if (!mixer)
return -ENOMEM;
mixer->chip = chip;
- mixer->ctrlif = ctrlif;
mixer->ignore_ctl_error = ignore_error;
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems),
GFP_KERNEL);
@@ -2167,8 +2452,16 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
return -ENOMEM;
}
- host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
- mixer->protocol = get_iface_desc(host_iface)->bInterfaceProtocol;
+ mixer->hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
+ switch (get_iface_desc(mixer->hostif)->bInterfaceProtocol) {
+ case UAC_VERSION_1:
+ default:
+ mixer->protocol = UAC_VERSION_1;
+ break;
+ case UAC_VERSION_2:
+ mixer->protocol = UAC_VERSION_2;
+ break;
+ }
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
(err = snd_usb_mixer_status_create(mixer)) < 0)
@@ -2176,7 +2469,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
snd_usb_mixer_apply_create_quirk(mixer);
- err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
+ err = snd_device_new(chip->card, SNDRV_DEV_CODEC, mixer, &dev_ops);
if (err < 0)
goto _error;
@@ -2200,3 +2493,82 @@ void snd_usb_mixer_disconnect(struct list_head *p)
usb_kill_urb(mixer->urb);
usb_kill_urb(mixer->rc_urb);
}
+
+#ifdef CONFIG_PM
+/* stop any bus activity of a mixer */
+static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
+{
+ usb_kill_urb(mixer->urb);
+ usb_kill_urb(mixer->rc_urb);
+}
+
+static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ if (mixer->urb) {
+ err = usb_submit_urb(mixer->urb, GFP_NOIO);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
+{
+ snd_usb_mixer_inactivate(mixer);
+ return 0;
+}
+
+static int restore_mixer_value(struct usb_mixer_elem_info *cval)
+{
+ int c, err, idx;
+
+ if (cval->cmask) {
+ idx = 0;
+ for (c = 0; c < MAX_CHANNELS; c++) {
+ if (!(cval->cmask & (1 << c)))
+ continue;
+ if (cval->cached & (1 << c)) {
+ err = set_cur_mix_value(cval, c + 1, idx,
+ cval->cache_val[idx]);
+ if (err < 0)
+ return err;
+ }
+ idx++;
+ }
+ } else {
+ /* master */
+ if (cval->cached) {
+ err = set_cur_mix_value(cval, 0, 0, *cval->cache_val);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
+{
+ struct usb_mixer_elem_info *cval;
+ int id, err;
+
+ /* FIXME: any mixer quirks? */
+
+ if (reset_resume) {
+ /* restore cached mixer values */
+ for (id = 0; id < MAX_ID_ELEMS; id++) {
+ for (cval = mixer->id_elems[id]; cval;
+ cval = cval->next_id_elem) {
+ err = restore_mixer_value(cval);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ return snd_usb_mixer_activate(mixer);
+}
+#endif
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index a7cf1007fbb..73b1f649447 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -3,7 +3,7 @@
struct usb_mixer_interface {
struct snd_usb_audio *chip;
- unsigned int ctrlif;
+ struct usb_host_interface *hostif;
struct list_head list;
unsigned int ignore_ctl_error;
struct urb *urb;
@@ -25,7 +25,16 @@ struct usb_mixer_interface {
u8 xonar_u1_status;
};
-#define MAX_CHANNELS 10 /* max logical channels */
+#define MAX_CHANNELS 16 /* max logical channels */
+
+enum {
+ USB_MIXER_BOOLEAN,
+ USB_MIXER_INV_BOOLEAN,
+ USB_MIXER_S8,
+ USB_MIXER_U8,
+ USB_MIXER_S16,
+ USB_MIXER_U16,
+};
struct usb_mixer_elem_info {
struct usb_mixer_interface *mixer;
@@ -34,6 +43,7 @@ struct usb_mixer_elem_info {
unsigned int id;
unsigned int control; /* CS or ICN (high byte) */
unsigned int cmask; /* channel mask bitmap: 0 = master */
+ unsigned int idx_off; /* Control index offset */
unsigned int ch_readonly;
unsigned int master_readonly;
int channels;
@@ -54,4 +64,15 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set);
+int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl);
+
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv);
+
+#ifdef CONFIG_PM
+int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer);
+int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume);
+#endif
+
#endif /* __USBMIXER_H */
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index f1324c42383..d1d72ff5034 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -179,6 +179,15 @@ static struct usbmix_name_map audigy2nx_map[] = {
{ 0 } /* terminator */
};
+static struct usbmix_selector_map c400_selectors[] = {
+ {
+ .id = 0x80,
+ .count = 2,
+ .names = (const char*[]) {"Internal", "SPDIF"}
+ },
+ { 0 } /* terminator */
+};
+
static struct usbmix_selector_map audigy2nx_selectors[] = {
{
.id = 14, /* Capture Source */
@@ -288,6 +297,15 @@ static struct usbmix_name_map scratch_live_map[] = {
{ 0 } /* terminator */
};
+static struct usbmix_name_map ebox44_map[] = {
+ { 4, NULL }, /* FU */
+ { 6, NULL }, /* MU */
+ { 7, NULL }, /* FU */
+ { 10, NULL }, /* FU */
+ { 11, NULL }, /* MU */
+ { 0 }
+};
+
/* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+"
* most importand difference is SU[8], it should be set to "Capture Source"
* to make alsamixer and PA working properly.
@@ -304,6 +322,17 @@ static struct usbmix_name_map hercules_usb51_map[] = {
{ 0 } /* terminator */
};
+/* Plantronics Gamecom 780 has a broken volume control, better to disable it */
+static struct usbmix_name_map gamecom780_map[] = {
+ { 9, NULL }, /* FU, speaker out */
+ {}
+};
+
+static const struct usbmix_name_map kef_x300a_map[] = {
+ { 10, NULL }, /* firmware locks up (?) when we try to access this FU */
+ { 0 }
+};
+
/*
* Control map entries
*/
@@ -332,6 +361,18 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = audigy2nx_map,
.selector_map = audigy2nx_selectors,
},
+ { /* Logitech, Inc. QuickCam Pro for Notebooks */
+ .id = USB_ID(0x046d, 0x0991),
+ .ignore_ctl_error = 1,
+ },
+ { /* Logitech, Inc. QuickCam E 3500 */
+ .id = USB_ID(0x046d, 0x09a4),
+ .ignore_ctl_error = 1,
+ },
+ { /* Plantronics GameCom 780 */
+ .id = USB_ID(0x047f, 0xc010),
+ .map = gamecom780_map,
+ },
{
/* Hercules DJ Console (Windows Edition) */
.id = USB_ID(0x06f8, 0xb000),
@@ -350,6 +391,14 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = hercules_usb51_map,
},
{
+ .id = USB_ID(0x0763, 0x2030),
+ .selector_map = c400_selectors,
+ },
+ {
+ .id = USB_ID(0x0763, 0x2031),
+ .selector_map = c400_selectors,
+ },
+ {
.id = USB_ID(0x08bb, 0x2702),
.map = linex_map,
.ignore_ctl_error = 1,
@@ -371,6 +420,14 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = scratch_live_map,
.ignore_ctl_error = 1,
},
+ {
+ .id = USB_ID(0x200c, 0x1018),
+ .map = ebox44_map,
+ },
+ {
+ .id = USB_ID(0x27ac, 0x1000),
+ .map = kef_x300a_map,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index e7df1e5e3f2..f119a41ed9a 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -9,6 +9,8 @@
* Alan Cox (alan@lxorguk.ukuu.org.uk)
* Thomas Sailer (sailer@ife.ee.ethz.ch)
*
+ * Audio Advantage Micro II support added by:
+ * Przemek Rudy (prudy1@o2.pl)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,6 +32,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/hwdep.h>
@@ -40,6 +43,119 @@
#include "mixer_quirks.h"
#include "helper.h"
+extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
+
+struct std_mono_table {
+ unsigned int unitid, control, cmask;
+ int val_type;
+ const char *name;
+ snd_kcontrol_tlv_rw_t *tlv_callback;
+};
+
+/* private_free callback */
+static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+ kfree(kctl->private_data);
+ kctl->private_data = NULL;
+}
+
+/* This function allows for the creation of standard UAC controls.
+ * See the quirks for M-Audio FTUs or Ebox-44.
+ * If you don't want to set a TLV callback pass NULL.
+ *
+ * Since there doesn't seem to be a devices that needs a multichannel
+ * version, we keep it mono for simplicity.
+ */
+static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ unsigned int idx_off,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+ int err;
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ cval->id = unitid;
+ cval->mixer = mixer;
+ cval->val_type = val_type;
+ cval->channels = 1;
+ cval->control = control;
+ cval->cmask = cmask;
+ cval->idx_off = idx_off;
+
+ /* get_min_max() is called only for integer volumes later,
+ * so provide a short-cut for booleans */
+ cval->min = 0;
+ cval->max = 1;
+ cval->res = 0;
+ cval->dBmin = 0;
+ cval->dBmax = 0;
+
+ /* Create control */
+ kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ /* Set name */
+ snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+ kctl->private_free = usb_mixer_elem_free;
+
+ /* set TLV */
+ if (tlv_callback) {
+ kctl->tlv.c = tlv_callback;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
+ /* Add control to mixer */
+ err = snd_usb_mixer_add_control(mixer, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
+ unsigned int unitid,
+ unsigned int control,
+ unsigned int cmask,
+ int val_type,
+ const char *name,
+ snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+ return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask,
+ val_type, 0 /* Offset */, name, tlv_callback);
+}
+
+/*
+ * Create a set of standard UAC controls from a table
+ */
+static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
+ struct std_mono_table *t)
+{
+ int err;
+
+ while (t->name != NULL) {
+ err = snd_create_std_mono_ctl(mixer, t->unitid, t->control,
+ t->cmask, t->val_type, t->name, t->tlv_callback);
+ if (err < 0)
+ return err;
+ t++;
+ }
+
+ return 0;
+}
+
/*
* Sound Blaster remote control configuration
*
@@ -60,6 +176,8 @@ static const struct rc_config {
{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
+ { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
+ { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
@@ -182,10 +300,29 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (value > 1)
return -EINVAL;
changed = value != mixer->audigy2nx_leds[index];
- err = snd_usb_ctl_msg(mixer->chip->dev,
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042))
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0);
+ /* USB X-Fi S51 Pro */
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
+ err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- value, index + 2, NULL, 0, 100);
+ !value, 0, NULL, 0);
+ else
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ value, index + 2, NULL, 0);
+ out:
+ up_read(&mixer->chip->shutdown_rwsem);
if (err < 0)
return err;
mixer->audigy2nx_leds[index] = value;
@@ -224,8 +361,16 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
int i, err;
for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+ /* USB X-Fi S51 doesn't have a CMSS LED */
+ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
+ continue;
+ /* USB X-Fi S51 Pro doesn't have one either */
+ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
+ continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
err = snd_ctl_add(mixer->chip->card,
@@ -271,11 +416,16 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
for (i = 0; jacks[i].name; ++i) {
snd_iprintf(buffer, "%s: ", jacks[i].name);
- err = snd_usb_ctl_msg(mixer->chip->dev,
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ err = 0;
+ else
+ err = snd_usb_ctl_msg(mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0,
- jacks[i].unitid << 8, buf, 3, 100);
+ jacks[i].unitid << 8, buf, 3);
+ up_read(&mixer->chip->shutdown_rwsem);
if (err == 3 && (buf[0] == 3 || buf[0] == 6))
snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
else
@@ -283,6 +433,91 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
}
}
+/* EMU0204 */
+static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *texts[2] = {"1/2",
+ "3/4"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = kcontrol->private_value;
+ return 0;
+}
+
+static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ unsigned int value = ucontrol->value.enumerated.item[0];
+ int err, changed;
+ unsigned char buf[2];
+
+ if (value > 1)
+ return -EINVAL;
+
+ buf[0] = 0x01;
+ buf[1] = value ? 0x02 : 0x01;
+
+ changed = value != kcontrol->private_value;
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown) {
+ err = -ENODEV;
+ goto out;
+ }
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ 0x0400, 0x0e00, buf, 2);
+ out:
+ up_read(&mixer->chip->shutdown_rwsem);
+ if (err < 0)
+ return err;
+ kcontrol->private_value = value;
+ return changed;
+}
+
+
+static struct snd_kcontrol_new snd_emu0204_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Front Jack Channels",
+ .info = snd_emu0204_ch_switch_info,
+ .get = snd_emu0204_ch_switch_get,
+ .put = snd_emu0204_ch_switch_put,
+ .private_value = 0,
+ },
+};
+
+static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
+{
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) {
+ err = snd_ctl_add(mixer->chip->card,
+ snd_ctl_new1(&snd_emu0204_controls[i], mixer));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+/* ASUS Xonar U1 / U3 controls */
+
static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -305,10 +540,15 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
else
new_status = old_status & ~0x02;
changed = new_status != old_status;
- err = snd_usb_ctl_msg(mixer->chip->dev,
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ err = -ENODEV;
+ else
+ err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- 50, 0, &new_status, 1, 100);
+ 50, 0, &new_status, 1);
+ up_read(&mixer->chip->shutdown_rwsem);
if (err < 0)
return err;
mixer->xonar_u1_status = new_status;
@@ -335,6 +575,527 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
return 0;
}
+/* Native Instruments device quirks */
+
+#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
+
+static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ struct usb_device *dev = mixer->chip->dev;
+ u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
+ u16 wIndex = kcontrol->private_value & 0xffff;
+ u8 tmp;
+ int ret;
+
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ ret = -ENODEV;
+ else
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, wIndex,
+ &tmp, sizeof(tmp), 1000);
+ up_read(&mixer->chip->shutdown_rwsem);
+
+ if (ret < 0) {
+ dev_err(&dev->dev,
+ "unable to issue vendor read request (ret = %d)", ret);
+ return ret;
+ }
+
+ ucontrol->value.integer.value[0] = tmp;
+
+ return 0;
+}
+
+static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ struct usb_device *dev = mixer->chip->dev;
+ u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
+ u16 wIndex = kcontrol->private_value & 0xffff;
+ u16 wValue = ucontrol->value.integer.value[0];
+ int ret;
+
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ ret = -ENODEV;
+ else
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ wValue, wIndex,
+ NULL, 0, 1000);
+ up_read(&mixer->chip->shutdown_rwsem);
+
+ if (ret < 0) {
+ dev_err(&dev->dev,
+ "unable to issue vendor write request (ret = %d)", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
+ {
+ .name = "Direct Thru Channel A",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x03),
+ },
+ {
+ .name = "Direct Thru Channel B",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x05),
+ },
+ {
+ .name = "Phono Input Channel A",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x03),
+ },
+ {
+ .name = "Phono Input Channel B",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x05),
+ },
+};
+
+static struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
+ {
+ .name = "Direct Thru Channel A",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x03),
+ },
+ {
+ .name = "Direct Thru Channel B",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x05),
+ },
+ {
+ .name = "Direct Thru Channel C",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x07),
+ },
+ {
+ .name = "Direct Thru Channel D",
+ .private_value = _MAKE_NI_CONTROL(0x01, 0x09),
+ },
+ {
+ .name = "Phono Input Channel A",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x03),
+ },
+ {
+ .name = "Phono Input Channel B",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x05),
+ },
+ {
+ .name = "Phono Input Channel C",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x07),
+ },
+ {
+ .name = "Phono Input Channel D",
+ .private_value = _MAKE_NI_CONTROL(0x02, 0x09),
+ },
+};
+
+static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
+ const struct snd_kcontrol_new *kc,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct snd_kcontrol_new template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = snd_nativeinstruments_control_get,
+ .put = snd_nativeinstruments_control_put,
+ .info = snd_ctl_boolean_mono_info,
+ };
+
+ for (i = 0; i < count; i++) {
+ struct snd_kcontrol *c;
+
+ template.name = kc[i].name;
+ template.private_value = kc[i].private_value;
+
+ c = snd_ctl_new1(&template, mixer);
+ err = snd_ctl_add(mixer->chip->card, c);
+
+ if (err < 0)
+ break;
+ }
+
+ return err;
+}
+
+/* M-Audio FastTrack Ultra quirks */
+/* FTU Effect switch (also used by C400/C600) */
+struct snd_ftu_eff_switch_priv_val {
+ struct usb_mixer_interface *mixer;
+ int cached_value;
+ int is_cached;
+ int bUnitID;
+ int validx;
+};
+
+static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *texts[8] = {"Room 1",
+ "Room 2",
+ "Room 3",
+ "Hall 1",
+ "Hall 2",
+ "Plate",
+ "Delay",
+ "Echo"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 8;
+ if (uinfo->value.enumerated.item > 7)
+ uinfo->value.enumerated.item = 7;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_usb_audio *chip;
+ struct usb_mixer_interface *mixer;
+ struct snd_ftu_eff_switch_priv_val *pval;
+ int err;
+ unsigned char value[2];
+ int id, validx;
+
+ const int val_len = 2;
+
+ value[0] = 0x00;
+ value[1] = 0x00;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+
+ if (pval->is_cached) {
+ ucontrol->value.enumerated.item[0] = pval->cached_value;
+ return 0;
+ }
+
+ mixer = (struct usb_mixer_interface *) pval->mixer;
+ if (snd_BUG_ON(!mixer))
+ return -EINVAL;
+
+ chip = (struct snd_usb_audio *) mixer->chip;
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
+
+ id = pval->bUnitID;
+ validx = pval->validx;
+
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ err = -ENODEV;
+ else
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ up_read(&mixer->chip->shutdown_rwsem);
+ if (err < 0)
+ return err;
+
+ ucontrol->value.enumerated.item[0] = value[0];
+ pval->cached_value = value[0];
+ pval->is_cached = 1;
+
+ return 0;
+}
+
+static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_usb_audio *chip;
+ struct snd_ftu_eff_switch_priv_val *pval;
+
+ struct usb_mixer_interface *mixer;
+ int changed, cur_val, err, new_val;
+ unsigned char value[2];
+ int id, validx;
+
+ const int val_len = 2;
+
+ changed = 0;
+
+ pval = (struct snd_ftu_eff_switch_priv_val *)
+ kctl->private_value;
+ cur_val = pval->cached_value;
+ new_val = ucontrol->value.enumerated.item[0];
+
+ mixer = (struct usb_mixer_interface *) pval->mixer;
+ if (snd_BUG_ON(!mixer))
+ return -EINVAL;
+
+ chip = (struct snd_usb_audio *) mixer->chip;
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
+
+ id = pval->bUnitID;
+ validx = pval->validx;
+
+ if (!pval->is_cached) {
+ /* Read current value */
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ err = -ENODEV;
+ else
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ up_read(&mixer->chip->shutdown_rwsem);
+ if (err < 0)
+ return err;
+
+ cur_val = value[0];
+ pval->cached_value = cur_val;
+ pval->is_cached = 1;
+ }
+ /* update value if needed */
+ if (cur_val != new_val) {
+ value[0] = new_val;
+ value[1] = 0;
+ down_read(&mixer->chip->shutdown_rwsem);
+ if (mixer->chip->shutdown)
+ err = -ENODEV;
+ else
+ err = snd_usb_ctl_msg(chip->dev,
+ usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+ value, val_len);
+ up_read(&mixer->chip->shutdown_rwsem);
+ if (err < 0)
+ return err;
+
+ pval->cached_value = new_val;
+ pval->is_cached = 1;
+ changed = 1;
+ }
+
+ return changed;
+}
+
+static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
+ int validx, int bUnitID)
+{
+ static struct snd_kcontrol_new template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Effect Program Switch",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ftu_eff_switch_info,
+ .get = snd_ftu_eff_switch_get,
+ .put = snd_ftu_eff_switch_put
+ };
+
+ int err;
+ struct snd_kcontrol *kctl;
+ struct snd_ftu_eff_switch_priv_val *pval;
+
+ pval = kzalloc(sizeof(*pval), GFP_KERNEL);
+ if (!pval)
+ return -ENOMEM;
+
+ pval->cached_value = 0;
+ pval->is_cached = 0;
+ pval->mixer = mixer;
+ pval->bUnitID = bUnitID;
+ pval->validx = validx;
+
+ template.private_value = (unsigned long) pval;
+ kctl = snd_ctl_new1(&template, mixer->chip);
+ if (!kctl) {
+ kfree(pval);
+ return -ENOMEM;
+ }
+
+ err = snd_ctl_add(mixer->chip->card, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* Create volume controls for FTU devices*/
+static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
+{
+ char name[64];
+ unsigned int control, cmask;
+ int in, out, err;
+
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+
+ for (out = 0; out < 8; out++) {
+ control = out + 1;
+ for (in = 0; in < 8; in++) {
+ cmask = 1 << in;
+ snprintf(name, sizeof(name),
+ "AIn%d - Out%d Capture Volume",
+ in + 1, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ for (in = 8; in < 16; in++) {
+ cmask = 1 << in;
+ snprintf(name, sizeof(name),
+ "DIn%d - Out%d Playback Volume",
+ in - 7, out + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 2;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Duration";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 3;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Feedback Volume";
+ const unsigned int id = 6;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 4;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, NULL);
+}
+
+static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 7;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 7;
+
+ for (ch = 0; ch < 4; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Return %d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
+{
+ unsigned int cmask;
+ int err, ch;
+ char name[48];
+
+ const unsigned int id = 5;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 9;
+
+ for (ch = 0; ch < 8; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send AIn%d Volume", ch + 1);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ for (ch = 8; ch < 16; ++ch) {
+ cmask = 1 << ch;
+ snprintf(name, sizeof(name),
+ "Effect Send DIn%d Volume", ch - 7);
+ err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+ val_type, name,
+ snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_ftu_create_volume_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_switch(mixer, 1, 6);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_volume_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_duration_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_feedback_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_return_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_send_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id)
{
@@ -354,32 +1115,565 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
}
}
-int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+/* M-Audio Fast Track C400/C600 */
+/* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */
+static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
+{
+ char name[64];
+ unsigned int cmask, offset;
+ int out, chan, err;
+ int num_outs = 0;
+ int num_ins = 0;
+
+ const unsigned int id = 0x40;
+ const int val_type = USB_MIXER_S16;
+ const int control = 1;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x0763, 0x2030):
+ num_outs = 6;
+ num_ins = 4;
+ break;
+ case USB_ID(0x0763, 0x2031):
+ num_outs = 8;
+ num_ins = 6;
+ break;
+ }
+
+ for (chan = 0; chan < num_outs + num_ins; chan++) {
+ for (out = 0; out < num_outs; out++) {
+ if (chan < num_outs) {
+ snprintf(name, sizeof(name),
+ "PCM%d-Out%d Playback Volume",
+ chan + 1, out + 1);
+ } else {
+ snprintf(name, sizeof(name),
+ "In%d-Out%d Playback Volume",
+ chan - num_outs + 1, out + 1);
+ }
+
+ cmask = (out == 0) ? 0 : 1 << (out - 1);
+ offset = chan * num_outs;
+ err = snd_create_std_mono_ctl_offset(mixer, id, control,
+ cmask, val_type, offset, name,
+ &snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Volume";
+ const unsigned int id = 0x43;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 3;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Duration";
+ const unsigned int id = 0x43;
+ const int val_type = USB_MIXER_S16;
+ const unsigned int control = 4;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+ static const char name[] = "Effect Feedback Volume";
+ const unsigned int id = 0x43;
+ const int val_type = USB_MIXER_U8;
+ const unsigned int control = 5;
+ const unsigned int cmask = 0;
+
+ return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+ name, NULL);
+}
+
+static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
+{
+ char name[64];
+ unsigned int cmask;
+ int chan, err;
+ int num_outs = 0;
+ int num_ins = 0;
+
+ const unsigned int id = 0x42;
+ const int val_type = USB_MIXER_S16;
+ const int control = 1;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x0763, 0x2030):
+ num_outs = 6;
+ num_ins = 4;
+ break;
+ case USB_ID(0x0763, 0x2031):
+ num_outs = 8;
+ num_ins = 6;
+ break;
+ }
+
+ for (chan = 0; chan < num_outs + num_ins; chan++) {
+ if (chan < num_outs) {
+ snprintf(name, sizeof(name),
+ "Effect Send DOut%d",
+ chan + 1);
+ } else {
+ snprintf(name, sizeof(name),
+ "Effect Send AIn%d",
+ chan - num_outs + 1);
+ }
+
+ cmask = (chan == 0) ? 0 : 1 << (chan - 1);
+ err = snd_create_std_mono_ctl(mixer, id, control,
+ cmask, val_type, name,
+ &snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer)
+{
+ char name[64];
+ unsigned int cmask;
+ int chan, err;
+ int num_outs = 0;
+ int offset = 0;
+
+ const unsigned int id = 0x40;
+ const int val_type = USB_MIXER_S16;
+ const int control = 1;
+
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x0763, 0x2030):
+ num_outs = 6;
+ offset = 0x3c;
+ /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
+ break;
+ case USB_ID(0x0763, 0x2031):
+ num_outs = 8;
+ offset = 0x70;
+ /* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */
+ break;
+ }
+
+ for (chan = 0; chan < num_outs; chan++) {
+ snprintf(name, sizeof(name),
+ "Effect Return %d",
+ chan + 1);
+
+ cmask = (chan == 0) ? 0 :
+ 1 << (chan + (chan % 2) * num_outs - 1);
+ err = snd_create_std_mono_ctl_offset(mixer, id, control,
+ cmask, val_type, offset, name,
+ &snd_usb_mixer_vol_tlv);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int snd_c400_create_mixer(struct usb_mixer_interface *mixer)
{
int err;
+
+ err = snd_c400_create_vol_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_c400_create_effect_vol_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_c400_create_effect_ret_vol_ctls(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_ftu_create_effect_switch(mixer, 2, 0x43);
+ if (err < 0)
+ return err;
+
+ err = snd_c400_create_effect_volume_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_c400_create_effect_duration_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ err = snd_c400_create_effect_feedback_ctl(mixer);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * The mixer units for Ebox-44 are corrupt, and even where they
+ * are valid they presents mono controls as L and R channels of
+ * stereo. So we provide a good mixer here.
+ */
+static struct std_mono_table ebox44_table[] = {
+ {
+ .unitid = 4,
+ .control = 1,
+ .cmask = 0x0,
+ .val_type = USB_MIXER_INV_BOOLEAN,
+ .name = "Headphone Playback Switch"
+ },
+ {
+ .unitid = 4,
+ .control = 2,
+ .cmask = 0x1,
+ .val_type = USB_MIXER_S16,
+ .name = "Headphone A Mix Playback Volume"
+ },
+ {
+ .unitid = 4,
+ .control = 2,
+ .cmask = 0x2,
+ .val_type = USB_MIXER_S16,
+ .name = "Headphone B Mix Playback Volume"
+ },
+
+ {
+ .unitid = 7,
+ .control = 1,
+ .cmask = 0x0,
+ .val_type = USB_MIXER_INV_BOOLEAN,
+ .name = "Output Playback Switch"
+ },
+ {
+ .unitid = 7,
+ .control = 2,
+ .cmask = 0x1,
+ .val_type = USB_MIXER_S16,
+ .name = "Output A Playback Volume"
+ },
+ {
+ .unitid = 7,
+ .control = 2,
+ .cmask = 0x2,
+ .val_type = USB_MIXER_S16,
+ .name = "Output B Playback Volume"
+ },
+
+ {
+ .unitid = 10,
+ .control = 1,
+ .cmask = 0x0,
+ .val_type = USB_MIXER_INV_BOOLEAN,
+ .name = "Input Capture Switch"
+ },
+ {
+ .unitid = 10,
+ .control = 2,
+ .cmask = 0x1,
+ .val_type = USB_MIXER_S16,
+ .name = "Input A Capture Volume"
+ },
+ {
+ .unitid = 10,
+ .control = 2,
+ .cmask = 0x2,
+ .val_type = USB_MIXER_S16,
+ .name = "Input B Capture Volume"
+ },
+
+ {}
+};
+
+/* Audio Advantage Micro II findings:
+ *
+ * Mapping spdif AES bits to vendor register.bit:
+ * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
+ * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
+ * AES2: [0 0 0 0 0 0 0 0]
+ * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
+ * (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
+ *
+ * power on values:
+ * r2: 0x10
+ * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
+ * just after it to 0xa0, presumably it disables/mutes some analog
+ * parts when there is no audio.)
+ * r9: 0x28
+ *
+ * Optical transmitter on/off:
+ * vendor register.bit: 9.1
+ * 0 - on (0x28 register value)
+ * 1 - off (0x2a register value)
+ *
+ */
+static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ unsigned int ep;
+ unsigned char data[3];
+ int rate;
+
+ ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
+ ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = 0x00;
+
+ /* use known values for that card: interface#1 altsetting#1 */
+ iface = usb_ifnum_to_if(mixer->chip->dev, 1);
+ alts = &iface->altsetting[1];
+ ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_rcvctrlpipe(mixer->chip->dev, 0),
+ UAC_GET_CUR,
+ USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
+ UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
+ ep,
+ data,
+ sizeof(data));
+ if (err < 0)
+ goto end;
+
+ rate = data[0] | (data[1] << 8) | (data[2] << 16);
+ ucontrol->value.iec958.status[3] = (rate == 48000) ?
+ IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
+
+ err = 0;
+end:
+ return err;
+}
+
+static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ u8 reg;
+ unsigned long priv_backup = kcontrol->private_value;
+
+ reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) |
+ (ucontrol->value.iec958.status[0] & 0x0f);
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 2,
+ NULL,
+ 0);
+ if (err < 0)
+ goto end;
+
+ kcontrol->private_value &= 0xfffff0f0;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f);
+
+ reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ?
+ 0xa0 : 0x20;
+ reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f;
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 3,
+ NULL,
+ 0);
+ if (err < 0)
+ goto end;
+
+ kcontrol->private_value &= 0xffff0fff;
+ kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
+
+ /* The frequency bits in AES3 cannot be set via register access. */
+
+ /* Silently ignore any bits from the request that cannot be set. */
+
+ err = (priv_backup != kcontrol->private_value);
+end:
+ return err;
+}
+
+static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0x0f;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0x00;
+ ucontrol->value.iec958.status[3] = 0x00;
+
+ return 0;
+}
+
+static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
+
+ return 0;
+}
+
+static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ int err;
+ u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
+
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0),
+ UAC_SET_CUR,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ reg,
+ 9,
+ NULL,
+ 0);
+
+ if (!err) {
+ err = (reg != (kcontrol->private_value & 0x0ff));
+ if (err)
+ kcontrol->private_value = reg;
+ }
+
+ return err;
+}
+
+static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = snd_microii_spdif_info,
+ .get = snd_microii_spdif_default_get,
+ .put = snd_microii_spdif_default_put,
+ .private_value = 0x00000100UL,/* reset value */
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+ .info = snd_microii_spdif_info,
+ .get = snd_microii_spdif_mask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_microii_spdif_switch_get,
+ .put = snd_microii_spdif_switch_put,
+ .private_value = 0x00000028UL,/* reset value */
+ }
+};
+
+static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
+ err = snd_ctl_add(mixer->chip->card,
+ snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+{
+ int err = 0;
struct snd_info_entry *entry;
if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
return err;
- if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
- mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
- mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
- if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
- return err;
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x041e, 0x3020):
+ case USB_ID(0x041e, 0x3040):
+ case USB_ID(0x041e, 0x3042):
+ case USB_ID(0x041e, 0x30df):
+ case USB_ID(0x041e, 0x3048):
+ err = snd_audigy2nx_controls_create(mixer);
+ if (err < 0)
+ break;
if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry))
snd_info_set_text_ops(entry, mixer,
snd_audigy2nx_proc_read);
- }
+ break;
- if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
- mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
- err = snd_xonar_u1_controls_create(mixer);
+ /* EMU0204 */
+ case USB_ID(0x041e, 0x3f19):
+ err = snd_emu0204_controls_create(mixer);
if (err < 0)
- return err;
+ break;
+ break;
+
+ case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
+ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
+ err = snd_c400_create_mixer(mixer);
+ break;
+
+ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+ err = snd_ftu_create_mixer(mixer);
+ break;
+
+ case USB_ID(0x0b05, 0x1739): /* ASUS Xonar U1 */
+ case USB_ID(0x0b05, 0x1743): /* ASUS Xonar U1 (2) */
+ case USB_ID(0x0b05, 0x17a0): /* ASUS Xonar U3 */
+ err = snd_xonar_u1_controls_create(mixer);
+ break;
+
+ case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
+ err = snd_microii_controls_create(mixer);
+ break;
+
+ case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
+ err = snd_nativeinstruments_create_mixer(mixer,
+ snd_nativeinstruments_ta6_mixers,
+ ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
+ break;
+
+ case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
+ err = snd_nativeinstruments_create_mixer(mixer,
+ snd_nativeinstruments_ta10_mixers,
+ ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
+ break;
+
+ case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
+ /* detection is disabled in mixer_maps.c */
+ err = snd_create_std_mono_table(mixer, ebox44_table);
+ break;
}
- return 0;
+ return err;
}
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
@@ -405,7 +1699,7 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
break;
default:
- snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+ usb_audio_dbg(mixer->chip, "memory change in unknown unit %d\n", unitid);
break;
}
}
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 456829882f4..c62a1659106 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -16,6 +16,8 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
@@ -28,10 +30,46 @@
#include "card.h"
#include "quirks.h"
#include "debug.h"
-#include "urb.h"
+#include "endpoint.h"
#include "helper.h"
#include "pcm.h"
#include "clock.h"
+#include "power.h"
+
+#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
+#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
+
+/* return the estimated delay based on USB frame counters */
+snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
+ unsigned int rate)
+{
+ int current_frame_number;
+ int frame_diff;
+ int est_delay;
+
+ if (!subs->last_delay)
+ return 0; /* short path */
+
+ current_frame_number = usb_get_current_frame_number(subs->dev);
+ /*
+ * HCD implementations use different widths, use lower 8 bits.
+ * The delay will be managed up to 256ms, which is more than
+ * enough
+ */
+ frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
+
+ /* Approximation based on number of samples per USB frame (ms),
+ some truncation for 44.1 but the estimate is good enough */
+ est_delay = frame_diff * rate / 1000;
+ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ est_delay = subs->last_delay - est_delay;
+ else
+ est_delay = subs->last_delay + est_delay;
+
+ if (est_delay < 0)
+ est_delay = 0;
+ return est_delay;
+}
/*
* return the current pcm pointer. just based on the hwptr_done value.
@@ -42,8 +80,12 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
unsigned int hwptr_done;
subs = (struct snd_usb_substream *)substream->runtime->private_data;
+ if (subs->stream->chip->shutdown)
+ return SNDRV_PCM_POS_XRUN;
spin_lock(&subs->lock);
hwptr_done = subs->hwptr_done;
+ substream->runtime->delay = snd_usb_pcm_delay(subs,
+ substream->runtime->rate);
spin_unlock(&subs->lock);
return hwptr_done / (substream->runtime->frame_bits >> 3);
}
@@ -51,26 +93,24 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
/*
* find a matching audio format
*/
-static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
- unsigned int rate, unsigned int channels)
+static struct audioformat *find_format(struct snd_usb_substream *subs)
{
- struct list_head *p;
+ struct audioformat *fp;
struct audioformat *found = NULL;
int cur_attr = 0, attr;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
- if (!(fp->formats & (1uLL << format)))
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (!(fp->formats & pcm_format_to_bits(subs->pcm_format)))
continue;
- if (fp->channels != channels)
+ if (fp->channels != subs->channels)
continue;
- if (rate < fp->rate_min || rate > fp->rate_max)
+ if (subs->cur_rate < fp->rate_min ||
+ subs->cur_rate > fp->rate_max)
continue;
if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
unsigned int i;
for (i = 0; i < fp->nr_rates; i++)
- if (fp->rate_table[i] == rate)
+ if (fp->rate_table[i] == subs->cur_rate)
break;
if (i >= fp->nr_rates)
continue;
@@ -125,9 +165,9 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
- dev->devnum, iface, ep);
+ data, sizeof(data))) < 0) {
+ usb_audio_err(chip, "%d:%d: cannot set enable PITCH\n",
+ iface, ep);
return err;
}
@@ -140,18 +180,15 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface,
{
struct usb_device *dev = chip->dev;
unsigned char data[1];
- unsigned int ep;
int err;
- ep = get_endpoint(alts, 0)->bEndpointAddress;
-
data[0] = 1;
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
UAC2_EP_CS_PITCH << 8, 0,
- data, sizeof(data), 1000)) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH (v2)\n",
- dev->devnum, iface, fmt->altsetting);
+ data, sizeof(data))) < 0) {
+ usb_audio_err(chip, "%d:%d: cannot set enable PITCH (v2)\n",
+ iface, fmt->altsetting);
return err;
}
@@ -165,21 +202,246 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt)
{
- struct usb_interface_descriptor *altsd = get_iface_desc(alts);
-
/* if endpoint doesn't have pitch control, bail out */
if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
return 0;
- switch (altsd->bInterfaceProtocol) {
+ switch (fmt->protocol) {
case UAC_VERSION_1:
+ default:
return init_pitch_v1(chip, iface, alts, fmt);
case UAC_VERSION_2:
return init_pitch_v2(chip, iface, alts, fmt);
}
+}
- return -EINVAL;
+static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
+{
+ int err;
+
+ if (!subs->data_endpoint)
+ return -EINVAL;
+
+ if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
+
+ dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep);
+
+ ep->data_subs = subs;
+ err = snd_usb_endpoint_start(ep, can_sleep);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+ return err;
+ }
+ }
+
+ if (subs->sync_endpoint &&
+ !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+ struct snd_usb_endpoint *ep = subs->sync_endpoint;
+
+ if (subs->data_endpoint->iface != subs->sync_endpoint->iface ||
+ subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) {
+ err = usb_set_interface(subs->dev,
+ subs->sync_endpoint->iface,
+ subs->sync_endpoint->altsetting);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+ dev_err(&subs->dev->dev,
+ "%d:%d: cannot set interface (%d)\n",
+ subs->sync_endpoint->iface,
+ subs->sync_endpoint->altsetting, err);
+ return -EIO;
+ }
+ }
+
+ dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep);
+
+ ep->sync_slave = subs->data_endpoint;
+ err = snd_usb_endpoint_start(ep, can_sleep);
+ if (err < 0) {
+ clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void stop_endpoints(struct snd_usb_substream *subs, bool wait)
+{
+ if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags))
+ snd_usb_endpoint_stop(subs->sync_endpoint);
+
+ if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags))
+ snd_usb_endpoint_stop(subs->data_endpoint);
+
+ if (wait) {
+ snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
+ snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);
+ }
+}
+
+static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
+ unsigned int altsetting,
+ struct usb_host_interface **alts,
+ unsigned int *ep)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+
+ iface = usb_ifnum_to_if(dev, ifnum);
+ if (!iface || iface->num_altsetting < altsetting + 1)
+ return -ENOENT;
+ *alts = &iface->altsetting[altsetting];
+ altsd = get_iface_desc(*alts);
+ if (altsd->bAlternateSetting != altsetting ||
+ altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
+ (altsd->bInterfaceSubClass != 2 &&
+ altsd->bInterfaceProtocol != 2 ) ||
+ altsd->bNumEndpoints < 1)
+ return -ENOENT;
+ epd = get_endpoint(*alts, 0);
+ if (!usb_endpoint_is_isoc_in(epd) ||
+ (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
+ USB_ENDPOINT_USAGE_IMPLICIT_FB)
+ return -ENOENT;
+ *ep = epd->bEndpointAddress;
+ return 0;
+}
+
+static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
+ struct usb_device *dev,
+ struct usb_interface_descriptor *altsd,
+ unsigned int attr)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ unsigned int ep;
+
+ /* Implicit feedback sync EPs consumers are always playback EPs */
+ if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ switch (subs->stream->chip->usb_id) {
+ case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
+ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 3);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+ break;
+ case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+ case USB_ID(0x0763, 0x2081):
+ ep = 0x81;
+ iface = usb_ifnum_to_if(dev, 2);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+ }
+ if (attr == USB_ENDPOINT_SYNC_ASYNC &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ altsd->bInterfaceProtocol == 2 &&
+ altsd->bNumEndpoints == 1 &&
+ USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
+ search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
+ altsd->bAlternateSetting,
+ &alts, &ep) >= 0) {
+ goto add_sync_ep;
+ }
+
+ /* No quirk */
+ return 0;
+
+add_sync_ep:
+ subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, ep, !subs->direction,
+ SND_USB_ENDPOINT_TYPE_DATA);
+ if (!subs->sync_endpoint)
+ return -EINVAL;
+
+ subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+ return 0;
+}
+
+static int set_sync_endpoint(struct snd_usb_substream *subs,
+ struct audioformat *fmt,
+ struct usb_device *dev,
+ struct usb_host_interface *alts,
+ struct usb_interface_descriptor *altsd)
+{
+ int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int ep, attr;
+ bool implicit_fb;
+ int err;
+
+ /* we need a sync pipe in async OUT or adaptive IN mode */
+ /* check the number of EP, since some devices have broken
+ * descriptors which fool us. if it has only one EP,
+ * assume it as adaptive-out or sync-in.
+ */
+ attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+ err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr);
+ if (err < 0)
+ return err;
+
+ if (altsd->bNumEndpoints < 2)
+ return 0;
+
+ if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) ||
+ (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
+ return 0;
+
+ /* check sync-pipe endpoint */
+ /* ... and check descriptor size before accessing bSynchAddress
+ because there is a version of the SB Audigy 2 NX firmware lacking
+ the audio fields in the endpoint descriptors */
+ if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
+ (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ get_endpoint(alts, 1)->bSynchAddress != 0)) {
+ dev_err(&dev->dev,
+ "%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
+ fmt->iface, fmt->altsetting,
+ get_endpoint(alts, 1)->bmAttributes,
+ get_endpoint(alts, 1)->bLength,
+ get_endpoint(alts, 1)->bSynchAddress);
+ return -EINVAL;
+ }
+ ep = get_endpoint(alts, 1)->bEndpointAddress;
+ if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+ ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
+ (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+ dev_err(&dev->dev,
+ "%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
+ fmt->iface, fmt->altsetting,
+ is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
+ return -EINVAL;
+ }
+
+ implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+ == USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+ subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, ep, !subs->direction,
+ implicit_fb ?
+ SND_USB_ENDPOINT_TYPE_DATA :
+ SND_USB_ENDPOINT_TYPE_SYNC);
+ if (!subs->sync_endpoint)
+ return -EINVAL;
+
+ subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+ return 0;
}
/*
@@ -191,8 +453,6 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct usb_interface *iface;
- unsigned int ep, attr;
- int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
int err;
iface = usb_ifnum_to_if(dev, fmt->iface);
@@ -208,9 +468,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
/* close the old interface */
if (subs->interface >= 0 && subs->interface != fmt->iface) {
- if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
- dev->devnum, fmt->iface, fmt->altsetting);
+ err = usb_set_interface(subs->dev, subs->interface, 0);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "%d:%d: return to setting 0 failed (%d)\n",
+ fmt->iface, fmt->altsetting, err);
return -EIO;
}
subs->interface = -1;
@@ -218,98 +480,192 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
}
/* set interface */
- if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
- if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
- dev->devnum, fmt->iface, fmt->altsetting);
+ if (subs->interface != fmt->iface ||
+ subs->altset_idx != fmt->altset_idx) {
+ err = usb_set_interface(dev, fmt->iface, fmt->altsetting);
+ if (err < 0) {
+ dev_err(&dev->dev,
+ "%d:%d: usb_set_interface failed (%d)\n",
+ fmt->iface, fmt->altsetting, err);
return -EIO;
}
- snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
+ dev_dbg(&dev->dev, "setting usb interface %d:%d\n",
+ fmt->iface, fmt->altsetting);
subs->interface = fmt->iface;
subs->altset_idx = fmt->altset_idx;
+
+ snd_usb_set_interface_quirk(dev);
}
- /* create a data pipe */
- ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
- if (is_playback)
- subs->datapipe = usb_sndisocpipe(dev, ep);
- else
- subs->datapipe = usb_rcvisocpipe(dev, ep);
- subs->datainterval = fmt->datainterval;
- subs->syncpipe = subs->syncinterval = 0;
- subs->maxpacksize = fmt->maxpacksize;
- subs->fill_max = 0;
+ subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+ alts, fmt->endpoint, subs->direction,
+ SND_USB_ENDPOINT_TYPE_DATA);
- /* we need a sync pipe in async OUT or adaptive IN mode */
- /* check the number of EP, since some devices have broken
- * descriptors which fool us. if it has only one EP,
- * assume it as adaptive-out or sync-in.
- */
- attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
- if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
- (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
- altsd->bNumEndpoints >= 2) {
- /* check sync-pipe endpoint */
- /* ... and check descriptor size before accessing bSynchAddress
- because there is a version of the SB Audigy 2 NX firmware lacking
- the audio fields in the endpoint descriptors */
- if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
- (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- get_endpoint(alts, 1)->bSynchAddress != 0)) {
- snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
- dev->devnum, fmt->iface, fmt->altsetting);
- return -EINVAL;
- }
- ep = get_endpoint(alts, 1)->bEndpointAddress;
- if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
- (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
- snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
- dev->devnum, fmt->iface, fmt->altsetting);
- return -EINVAL;
- }
- ep &= USB_ENDPOINT_NUMBER_MASK;
- if (is_playback)
- subs->syncpipe = usb_rcvisocpipe(dev, ep);
- else
- subs->syncpipe = usb_sndisocpipe(dev, ep);
- if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
- get_endpoint(alts, 1)->bRefresh >= 1 &&
- get_endpoint(alts, 1)->bRefresh <= 9)
- subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
- else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
- subs->syncinterval = 1;
- else if (get_endpoint(alts, 1)->bInterval >= 1 &&
- get_endpoint(alts, 1)->bInterval <= 16)
- subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
- else
- subs->syncinterval = 3;
- }
+ if (!subs->data_endpoint)
+ return -EINVAL;
- /* always fill max packet size */
- if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
- subs->fill_max = 1;
+ err = set_sync_endpoint(subs, fmt, dev, alts, altsd);
+ if (err < 0)
+ return err;
- if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
+ err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
+ if (err < 0)
return err;
subs->cur_audiofmt = fmt;
snd_usb_set_format_quirk(subs, fmt);
-#if 0
- printk(KERN_DEBUG
- "setting done: format = %d, rate = %d..%d, channels = %d\n",
- fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
- printk(KERN_DEBUG
- " datapipe = 0x%0x, syncpipe = 0x%0x\n",
- subs->datapipe, subs->syncpipe);
-#endif
-
return 0;
}
/*
+ * Return the score of matching two audioformats.
+ * Veto the audioformat if:
+ * - It has no channels for some reason.
+ * - Requested PCM format is not supported.
+ * - Requested sample rate is not supported.
+ */
+static int match_endpoint_audioformats(struct snd_usb_substream *subs,
+ struct audioformat *fp,
+ struct audioformat *match, int rate,
+ snd_pcm_format_t pcm_format)
+{
+ int i;
+ int score = 0;
+
+ if (fp->channels < 1) {
+ dev_dbg(&subs->dev->dev,
+ "%s: (fmt @%p) no channels\n", __func__, fp);
+ return 0;
+ }
+
+ if (!(fp->formats & pcm_format_to_bits(pcm_format))) {
+ dev_dbg(&subs->dev->dev,
+ "%s: (fmt @%p) no match for format %d\n", __func__,
+ fp, pcm_format);
+ return 0;
+ }
+
+ for (i = 0; i < fp->nr_rates; i++) {
+ if (fp->rate_table[i] == rate) {
+ score++;
+ break;
+ }
+ }
+ if (!score) {
+ dev_dbg(&subs->dev->dev,
+ "%s: (fmt @%p) no match for rate %d\n", __func__,
+ fp, rate);
+ return 0;
+ }
+
+ if (fp->channels == match->channels)
+ score++;
+
+ dev_dbg(&subs->dev->dev,
+ "%s: (fmt @%p) score %d\n", __func__, fp, score);
+
+ return score;
+}
+
+/*
+ * Configure the sync ep using the rate and pcm format of the data ep.
+ */
+static int configure_sync_endpoint(struct snd_usb_substream *subs)
+{
+ int ret;
+ struct audioformat *fp;
+ struct audioformat *sync_fp = NULL;
+ int cur_score = 0;
+ int sync_period_bytes = subs->period_bytes;
+ struct snd_usb_substream *sync_subs =
+ &subs->stream->substream[subs->direction ^ 1];
+
+ if (subs->sync_endpoint->type != SND_USB_ENDPOINT_TYPE_DATA ||
+ !subs->stream)
+ return snd_usb_endpoint_set_params(subs->sync_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ 0, 0,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ NULL);
+
+ /* Try to find the best matching audioformat. */
+ list_for_each_entry(fp, &sync_subs->fmt_list, list) {
+ int score = match_endpoint_audioformats(subs,
+ fp, subs->cur_audiofmt,
+ subs->cur_rate, subs->pcm_format);
+
+ if (score > cur_score) {
+ sync_fp = fp;
+ cur_score = score;
+ }
+ }
+
+ if (unlikely(sync_fp == NULL)) {
+ dev_err(&subs->dev->dev,
+ "%s: no valid audioformat for sync ep %x found\n",
+ __func__, sync_subs->ep_num);
+ return -EINVAL;
+ }
+
+ /*
+ * Recalculate the period bytes if channel number differ between
+ * data and sync ep audioformat.
+ */
+ if (sync_fp->channels != subs->channels) {
+ sync_period_bytes = (subs->period_bytes / subs->channels) *
+ sync_fp->channels;
+ dev_dbg(&subs->dev->dev,
+ "%s: adjusted sync ep period bytes (%d -> %d)\n",
+ __func__, subs->period_bytes, sync_period_bytes);
+ }
+
+ ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+ subs->pcm_format,
+ sync_fp->channels,
+ sync_period_bytes,
+ 0, 0,
+ subs->cur_rate,
+ sync_fp,
+ NULL);
+
+ return ret;
+}
+
+/*
+ * configure endpoint params
+ *
+ * called during initial setup and upon resume
+ */
+static int configure_endpoint(struct snd_usb_substream *subs)
+{
+ int ret;
+
+ /* format changed */
+ stop_endpoints(subs, true);
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ subs->period_frames,
+ subs->buffer_periods,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ subs->sync_endpoint);
+ if (ret < 0)
+ return ret;
+
+ if (subs->sync_endpoint)
+ ret = configure_sync_endpoint(subs);
+
+ return ret;
+}
+
+/*
* hw_params callback
*
* allocate a buffer and set the given audio format.
@@ -324,52 +680,42 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
{
struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
- unsigned int channels, rate, format;
- int ret, changed;
+ int ret;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
- format = params_format(hw_params);
- rate = params_rate(hw_params);
- channels = params_channels(hw_params);
- fmt = find_format(subs, format, rate, channels);
+ subs->pcm_format = params_format(hw_params);
+ subs->period_bytes = params_period_bytes(hw_params);
+ subs->period_frames = params_period_size(hw_params);
+ subs->buffer_periods = params_periods(hw_params);
+ subs->channels = params_channels(hw_params);
+ subs->cur_rate = params_rate(hw_params);
+
+ fmt = find_format(subs);
if (!fmt) {
- snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
- format, rate, channels);
+ dev_dbg(&subs->dev->dev,
+ "cannot set format: format = %#x, rate = %d, channels = %d\n",
+ subs->pcm_format, subs->cur_rate, subs->channels);
return -EINVAL;
}
- changed = subs->cur_audiofmt != fmt ||
- subs->period_bytes != params_period_bytes(hw_params) ||
- subs->cur_rate != rate;
- if ((ret = set_format(subs, fmt)) < 0)
+ down_read(&subs->stream->chip->shutdown_rwsem);
+ if (subs->stream->chip->shutdown)
+ ret = -ENODEV;
+ else
+ ret = set_format(subs, fmt);
+ up_read(&subs->stream->chip->shutdown_rwsem);
+ if (ret < 0)
return ret;
- if (subs->cur_rate != rate) {
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- iface = usb_ifnum_to_if(subs->dev, fmt->iface);
- alts = &iface->altsetting[fmt->altset_idx];
- ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate);
- if (ret < 0)
- return ret;
- subs->cur_rate = rate;
- }
-
- if (changed) {
- /* format changed */
- snd_usb_release_substream_urbs(subs, 0);
- /* influenced: period_bytes, channels, rate, format, */
- ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
- params_rate(hw_params),
- snd_pcm_format_physical_width(params_format(hw_params)) *
- params_channels(hw_params));
- }
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
+ subs->need_setup_ep = true;
- return ret;
+ return 0;
}
/*
@@ -384,8 +730,13 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
- if (!subs->stream->chip->shutdown)
- snd_usb_release_substream_urbs(subs, 0);
+ down_read(&subs->stream->chip->shutdown_rwsem);
+ if (!subs->stream->chip->shutdown) {
+ stop_endpoints(subs, true);
+ snd_usb_endpoint_deactivate(subs->sync_endpoint);
+ snd_usb_endpoint_deactivate(subs->data_endpoint);
+ }
+ up_read(&subs->stream->chip->shutdown_rwsem);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
@@ -398,23 +749,70 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = runtime->private_data;
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ int ret;
if (! subs->cur_audiofmt) {
- snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
+ dev_err(&subs->dev->dev, "no format is specified!\n");
return -ENXIO;
}
+ down_read(&subs->stream->chip->shutdown_rwsem);
+ if (subs->stream->chip->shutdown) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+ if (snd_BUG_ON(!subs->data_endpoint)) {
+ ret = -EIO;
+ goto unlock;
+ }
+
+ snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
+ snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);
+
+ ret = set_format(subs, subs->cur_audiofmt);
+ if (ret < 0)
+ goto unlock;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ ret = snd_usb_init_sample_rate(subs->stream->chip,
+ subs->cur_audiofmt->iface,
+ alts,
+ subs->cur_audiofmt,
+ subs->cur_rate);
+ if (ret < 0)
+ goto unlock;
+
+ if (subs->need_setup_ep) {
+ ret = configure_endpoint(subs);
+ if (ret < 0)
+ goto unlock;
+ subs->need_setup_ep = false;
+ }
+
/* some unit conversions in runtime */
- subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
- subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+ subs->data_endpoint->maxframesize =
+ bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
+ subs->data_endpoint->curframesize =
+ bytes_to_frames(runtime, subs->data_endpoint->curpacksize);
/* reset the pointer */
subs->hwptr_done = 0;
subs->transfer_done = 0;
- subs->phase = 0;
+ subs->last_delay = 0;
+ subs->last_frame_number = 0;
runtime->delay = 0;
- return snd_usb_substream_prepare(subs, runtime);
+ /* for playback, submit the URBs now; otherwise, the first hwptr_done
+ * updates for all URBs would happen at the same time when starting */
+ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = start_endpoints(subs, true);
+
+ unlock:
+ up_read(&subs->stream->chip->shutdown_rwsem);
+ return ret;
}
static struct snd_pcm_hardware snd_usb_hardware =
@@ -467,7 +865,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
return 0;
}
/* check whether the period time is >= the data packet interval */
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+ if (subs->speed != USB_SPEED_FULL) {
ptime = 125 * (1 << fp->datainterval);
if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max);
@@ -481,7 +879,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax;
int changed;
@@ -489,9 +887,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
@@ -535,7 +931,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
unsigned int rmin, rmax;
int changed;
@@ -543,9 +939,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
@@ -588,7 +982,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
u64 fbits;
u32 oldbits[2];
@@ -596,9 +990,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
fbits = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
fbits |= fp->formats;
@@ -636,7 +1028,7 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params,
min_datainterval = min(min_datainterval, fp->datainterval);
}
if (min_datainterval == 0xff) {
- hwc_debug(" --> get emtpy\n");
+ hwc_debug(" --> get empty\n");
it->empty = 1;
return -EINVAL;
}
@@ -662,9 +1054,13 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
struct snd_usb_substream *subs)
{
struct audioformat *fp;
+ int *rate_list;
int count = 0, needs_knot = 0;
int err;
+ kfree(subs->rate_list.list);
+ subs->rate_list.list = NULL;
+
list_for_each_entry(fp, &subs->fmt_list, list) {
if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
return 0;
@@ -675,14 +1071,17 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
if (!needs_knot)
return 0;
+ subs->rate_list.list = rate_list =
+ kmalloc(sizeof(int) * count, GFP_KERNEL);
+ if (!subs->rate_list.list)
+ return -ENOMEM;
subs->rate_list.count = count;
- subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
subs->rate_list.mask = 0;
count = 0;
list_for_each_entry(fp, &subs->fmt_list, list) {
int i;
for (i = 0; i < fp->nr_rates; i++)
- subs->rate_list.list[count++] = fp->rate_table[i];
+ rate_list[count++] = fp->rate_table[i];
}
err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&subs->rate_list);
@@ -699,7 +1098,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{
- struct list_head *p;
+ struct audioformat *fp;
unsigned int pt, ptmin;
int param_period_time_if_needed;
int err;
@@ -713,9 +1112,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
runtime->hw.rates = 0;
ptmin = UINT_MAX;
/* check min/max rates and channels */
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
runtime->hw.rates |= fp->rates;
if (runtime->hw.rate_min > fp->rate_min)
runtime->hw.rate_min = fp->rate_min;
@@ -733,9 +1130,12 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
pt = 125 * (1 << fp->datainterval);
ptmin = min(ptmin, pt);
}
+ err = snd_usb_autoresume(subs->stream->chip);
+ if (err < 0)
+ return err;
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
- if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+ if (subs->speed == USB_SPEED_FULL)
/* full speed devices have fixed data packet interval */
ptmin = 1000;
if (ptmin == 1000)
@@ -750,21 +1150,21 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
- return err;
+ goto rep_err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_RATE,
param_period_time_if_needed,
-1)) < 0)
- return err;
+ goto rep_err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_format, subs,
SNDRV_PCM_HW_PARAM_RATE,
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
- return err;
+ goto rep_err;
if (param_period_time_if_needed >= 0) {
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
@@ -774,11 +1174,15 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
SNDRV_PCM_HW_PARAM_RATE,
-1);
if (err < 0)
- return err;
+ goto rep_err;
}
if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
- return err;
+ goto rep_err;
return 0;
+
+rep_err:
+ snd_usb_autosuspend(subs->stream->chip);
+ return err;
}
static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
@@ -792,6 +1196,13 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
runtime->hw = snd_usb_hardware;
runtime->private_data = subs;
subs->pcm_substream = substream;
+ /* runtime PM is also done there */
+
+ /* initialize DSD/DOP context */
+ subs->dsd_dop.byte_idx = 0;
+ subs->dsd_dop.channel = 0;
+ subs->dsd_dop.marker = 1;
+
return setup_hw_info(runtime, subs);
}
@@ -800,14 +1211,313 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_usb_substream *subs = &as->substream[direction];
+ stop_endpoints(subs, true);
+
if (!as->chip->shutdown && subs->interface >= 0) {
usb_set_interface(subs->dev, subs->interface, 0);
subs->interface = -1;
}
+
subs->pcm_substream = NULL;
+ snd_usb_autosuspend(subs->stream->chip);
+
return 0;
}
+/* Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static void retire_capture_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ unsigned int stride, frames, bytes, oldptr;
+ int i, period_elapsed = 0;
+ unsigned long flags;
+ unsigned char *cp;
+ int current_frame_number;
+
+ /* read frame number here, update pointer in critical section */
+ current_frame_number = usb_get_current_frame_number(subs->dev);
+
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj;
+ if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
+ dev_dbg(&subs->dev->dev, "frame %d active: %d\n",
+ i, urb->iso_frame_desc[i].status);
+ // continue;
+ }
+ bytes = urb->iso_frame_desc[i].actual_length;
+ frames = bytes / stride;
+ if (!subs->txfr_quirk)
+ bytes = frames * stride;
+ if (bytes % (runtime->sample_bits >> 3) != 0) {
+ int oldbytes = bytes;
+ bytes = frames * stride;
+ dev_warn(&subs->dev->dev,
+ "Corrected urb data len. %d->%d\n",
+ oldbytes, bytes);
+ }
+ /* update the current pointer */
+ spin_lock_irqsave(&subs->lock, flags);
+ oldptr = subs->hwptr_done;
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+ frames = (bytes + (oldptr % stride)) / stride;
+ subs->transfer_done += frames;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ }
+ /* capture delay is by construction limited to one URB,
+ * reset delays here
+ */
+ runtime->delay = subs->last_delay = 0;
+
+ /* realign last_frame_number */
+ subs->last_frame_number = current_frame_number;
+ subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
+
+ spin_unlock_irqrestore(&subs->lock, flags);
+ /* copy a data chunk */
+ if (oldptr + bytes > runtime->buffer_size * stride) {
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - oldptr;
+ memcpy(runtime->dma_area + oldptr, cp, bytes1);
+ memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+ } else {
+ memcpy(runtime->dma_area + oldptr, cp, bytes);
+ }
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
+ struct urb *urb, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ unsigned int stride = runtime->frame_bits >> 3;
+ unsigned int dst_idx = 0;
+ unsigned int src_idx = subs->hwptr_done;
+ unsigned int wrap = runtime->buffer_size * stride;
+ u8 *dst = urb->transfer_buffer;
+ u8 *src = runtime->dma_area;
+ u8 marker[] = { 0x05, 0xfa };
+
+ /*
+ * The DSP DOP format defines a way to transport DSD samples over
+ * normal PCM data endpoints. It requires stuffing of marker bytes
+ * (0x05 and 0xfa, alternating per sample frame), and then expects
+ * 2 additional bytes of actual payload. The whole frame is stored
+ * LSB.
+ *
+ * Hence, for a stereo transport, the buffer layout looks like this,
+ * where L refers to left channel samples and R to right.
+ *
+ * L1 L2 0x05 R1 R2 0x05 L3 L4 0xfa R3 R4 0xfa
+ * L5 L6 0x05 R5 R6 0x05 L7 L8 0xfa R7 R8 0xfa
+ * .....
+ *
+ */
+
+ while (bytes--) {
+ if (++subs->dsd_dop.byte_idx == 3) {
+ /* frame boundary? */
+ dst[dst_idx++] = marker[subs->dsd_dop.marker];
+ src_idx += 2;
+ subs->dsd_dop.byte_idx = 0;
+
+ if (++subs->dsd_dop.channel % runtime->channels == 0) {
+ /* alternate the marker */
+ subs->dsd_dop.marker++;
+ subs->dsd_dop.marker %= ARRAY_SIZE(marker);
+ subs->dsd_dop.channel = 0;
+ }
+ } else {
+ /* stuff the DSD payload */
+ int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap;
+
+ if (subs->cur_audiofmt->dsd_bitrev)
+ dst[dst_idx++] = bitrev8(src[idx]);
+ else
+ dst[dst_idx++] = src[idx];
+
+ subs->hwptr_done++;
+ }
+ }
+}
+
+static void prepare_playback_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
+ struct snd_urb_ctx *ctx = urb->context;
+ unsigned int counts, frames, bytes;
+ int i, stride, period_elapsed = 0;
+ unsigned long flags;
+
+ stride = runtime->frame_bits >> 3;
+
+ frames = 0;
+ urb->number_of_packets = 0;
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->frame_limit += ep->max_urb_frames;
+ for (i = 0; i < ctx->packets; i++) {
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
+ /* set up descriptor */
+ urb->iso_frame_desc[i].offset = frames * ep->stride;
+ urb->iso_frame_desc[i].length = counts * ep->stride;
+ frames += counts;
+ urb->number_of_packets++;
+ subs->transfer_done += counts;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ subs->frame_limit = 0;
+ period_elapsed = 1;
+ if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+ if (subs->transfer_done > 0) {
+ /* FIXME: fill-max mode is not
+ * supported yet */
+ frames -= subs->transfer_done;
+ counts -= subs->transfer_done;
+ urb->iso_frame_desc[i].length =
+ counts * ep->stride;
+ subs->transfer_done = 0;
+ }
+ i++;
+ if (i < ctx->packets) {
+ /* add a transfer delimiter */
+ urb->iso_frame_desc[i].offset =
+ frames * ep->stride;
+ urb->iso_frame_desc[i].length = 0;
+ urb->number_of_packets++;
+ }
+ break;
+ }
+ }
+ /* finish at the period boundary or after enough frames */
+ if ((period_elapsed ||
+ subs->transfer_done >= subs->frame_limit) &&
+ !snd_usb_endpoint_implicit_feedback_sink(ep))
+ break;
+ }
+ bytes = frames * ep->stride;
+
+ if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
+ subs->cur_audiofmt->dsd_dop)) {
+ fill_playback_urb_dsd_dop(subs, urb, bytes);
+ } else if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U8 &&
+ subs->cur_audiofmt->dsd_bitrev)) {
+ /* bit-reverse the bytes */
+ u8 *buf = urb->transfer_buffer;
+ for (i = 0; i < bytes; i++) {
+ int idx = (subs->hwptr_done + i)
+ % (runtime->buffer_size * stride);
+ buf[i] = bitrev8(runtime->dma_area[idx]);
+ }
+
+ subs->hwptr_done += bytes;
+ } else {
+ /* usual PCM */
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+
+ subs->hwptr_done += bytes;
+ }
+
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+
+ /* update delay with exact number of samples queued */
+ runtime->delay = subs->last_delay;
+ runtime->delay += frames;
+ subs->last_delay = runtime->delay;
+
+ /* realign last_frame_number */
+ subs->last_frame_number = usb_get_current_frame_number(subs->dev);
+ subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
+
+ spin_unlock_irqrestore(&subs->lock, flags);
+ urb->transfer_buffer_length = bytes;
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static void retire_playback_urb(struct snd_usb_substream *subs,
+ struct urb *urb)
+{
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
+ int processed = urb->transfer_buffer_length / ep->stride;
+ int est_delay;
+
+ /* ignore the delay accounting when procssed=0 is given, i.e.
+ * silent payloads are procssed before handling the actual data
+ */
+ if (!processed)
+ return;
+
+ spin_lock_irqsave(&subs->lock, flags);
+ if (!subs->last_delay)
+ goto out; /* short path */
+
+ est_delay = snd_usb_pcm_delay(subs, runtime->rate);
+ /* update delay with exact number of samples played */
+ if (processed > subs->last_delay)
+ subs->last_delay = 0;
+ else
+ subs->last_delay -= processed;
+ runtime->delay = subs->last_delay;
+
+ /*
+ * Report when delay estimate is off by more than 2ms.
+ * The error should be lower than 2ms since the estimate relies
+ * on two reads of a counter updated every ms.
+ */
+ if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
+ dev_dbg_ratelimited(&subs->dev->dev,
+ "delay: estimated %d, actual %d\n",
+ est_delay, subs->last_delay);
+
+ if (!subs->running) {
+ /* update last_frame_number for delay counting here since
+ * prepare_playback_urb won't be called during pause
+ */
+ subs->last_frame_number =
+ usb_get_current_frame_number(subs->dev) & 0xff;
+ }
+
+ out:
+ spin_unlock_irqrestore(&subs->lock, flags);
+}
+
static int snd_usb_playback_open(struct snd_pcm_substream *substream)
{
return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
@@ -828,6 +1538,65 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream)
return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
}
+static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
+ subs->data_endpoint->retire_data_urb = retire_playback_urb;
+ subs->running = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stop_endpoints(subs, false);
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->data_endpoint->prepare_data_urb = NULL;
+ /* keep retire_data_urb for delay calculation */
+ subs->data_endpoint->retire_data_urb = retire_playback_urb;
+ subs->running = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int err;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = start_endpoints(subs, false);
+ if (err < 0)
+ return err;
+
+ subs->data_endpoint->retire_data_urb = retire_capture_urb;
+ subs->running = 1;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stop_endpoints(subs, false);
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->data_endpoint->retire_data_urb = NULL;
+ subs->running = 0;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->data_endpoint->retire_data_urb = retire_capture_urb;
+ subs->running = 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static struct snd_pcm_ops snd_usb_playback_ops = {
.open = snd_usb_playback_open,
.close = snd_usb_playback_close,
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 1c931b68f3b..df7a003682a 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -1,14 +1,14 @@
#ifndef __USBAUDIO_PCM_H
#define __USBAUDIO_PCM_H
+snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
+ unsigned int rate);
+
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts,
struct audioformat *fmt);
-int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
- struct usb_host_interface *alts,
- struct audioformat *fmt, int rate);
#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/power.h b/sound/usb/power.h
new file mode 100644
index 00000000000..48ee51dcb71
--- /dev/null
+++ b/sound/usb/power.h
@@ -0,0 +1,17 @@
+#ifndef __USBAUDIO_POWER_H
+#define __USBAUDIO_POWER_H
+
+#ifdef CONFIG_PM
+int snd_usb_autoresume(struct snd_usb_audio *chip);
+void snd_usb_autosuspend(struct snd_usb_audio *chip);
+#else
+static inline int snd_usb_autoresume(struct snd_usb_audio *chip)
+{
+ return 0;
+}
+static inline void snd_usb_autosuspend(struct snd_usb_audio *chip)
+{
+}
+#endif
+
+#endif /* __USBAUDIO_POWER_H */
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index f5e3f356b95..5f761ab34c0 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -25,6 +25,7 @@
#include "usbaudio.h"
#include "helper.h"
#include "card.h"
+#include "endpoint.h"
#include "proc.h"
/* convert our full speed USB rate into sampling rate in Hz */
@@ -72,20 +73,19 @@ void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
*/
static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
- struct list_head *p;
+ struct audioformat *fp;
static char *sync_types[4] = {
"NONE", "ASYNC", "ADAPTIVE", "SYNC"
};
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
snd_pcm_format_t fmt;
- fp = list_entry(p, struct audioformat, list);
+
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
snd_iprintf(buffer, " Format:");
for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt)
- if (fp->formats & (1uLL << fmt))
+ if (fp->formats & pcm_format_to_bits(fmt))
snd_iprintf(buffer, " %s",
snd_pcm_format_name(fmt));
snd_iprintf(buffer, "\n");
@@ -107,7 +107,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
snd_iprintf(buffer, "\n");
}
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+ if (subs->speed != USB_SPEED_FULL)
snd_iprintf(buffer, " Data packet interval: %d us\n",
125 * (1 << fp->datainterval));
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
@@ -115,23 +115,33 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
}
+static void proc_dump_ep_status(struct snd_usb_substream *subs,
+ struct snd_usb_endpoint *data_ep,
+ struct snd_usb_endpoint *sync_ep,
+ struct snd_info_buffer *buffer)
+{
+ if (!data_ep)
+ return;
+ snd_iprintf(buffer, " Packet Size = %d\n", data_ep->curpacksize);
+ snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
+ subs->speed == USB_SPEED_FULL
+ ? get_full_speed_hz(data_ep->freqm)
+ : get_high_speed_hz(data_ep->freqm),
+ data_ep->freqm >> 16, data_ep->freqm & 0xffff);
+ if (sync_ep && data_ep->freqshift != INT_MIN) {
+ int res = 16 - data_ep->freqshift;
+ snd_iprintf(buffer, " Feedback Format = %d.%d\n",
+ (sync_ep->syncmaxsize > 3 ? 32 : 24) - res, res);
+ }
+}
+
static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
if (subs->running) {
- unsigned int i;
snd_iprintf(buffer, " Status: Running\n");
snd_iprintf(buffer, " Interface = %d\n", subs->interface);
snd_iprintf(buffer, " Altset = %d\n", subs->altset_idx);
- snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs);
- for (i = 0; i < subs->nurbs; i++)
- snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
- snd_iprintf(buffer, "]\n");
- snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize);
- snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
- snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
- ? get_full_speed_hz(subs->freqm)
- : get_high_speed_hz(subs->freqm),
- subs->freqm >> 16, subs->freqm & 0xffff);
+ proc_dump_ep_status(subs, subs->data_endpoint, subs->sync_endpoint, buffer);
} else {
snd_iprintf(buffer, " Status: Stop\n");
}
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index f8797f61a24..f652b10ce90 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -39,13 +39,36 @@
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
-/* Creative/Toshiba Multimedia Center SB-0500 */
+/* FTDI devices */
{
- USB_DEVICE(0x041e, 0x3048),
+ USB_DEVICE(0x0403, 0xb8d8),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Toshiba",
- .product_name = "SB-0500",
- .ifnum = QUIRK_NO_INTERFACE
+ /* .vendor_name = "STARR LABS", */
+ /* .product_name = "Starr Labs MIDI USB device", */
+ .ifnum = 0,
+ .type = QUIRK_MIDI_FTDI
+ }
+},
+
+{
+ /* Creative BT-D1 */
+ USB_DEVICE(0x041e, 0x0005),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 2,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x03,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .attributes = 0,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ }
}
},
@@ -58,6 +81,15 @@
.ifnum = QUIRK_NO_INTERFACE
}
},
+/* Creative/Toshiba Multimedia Center SB-0500 */
+{
+ USB_DEVICE(0x041e, 0x3048),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Toshiba",
+ .product_name = "SB-0500",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
{
/* E-Mu 0202 USB */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
@@ -79,6 +111,49 @@
.idProduct = 0x3f0a,
.bInterfaceClass = USB_CLASS_AUDIO,
},
+{
+ /* E-Mu 0204 USB */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x041e,
+ .idProduct = 0x3f19,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
+
+/*
+ * HP Wireless Audio
+ * When not ignored, causes instability issues for some users, forcing them to
+ * blacklist the entire module.
+ */
+{
+ USB_DEVICE(0x0424, 0xb832),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Standard Microsystems Corp.",
+ .product_name = "HP Wireless Audio",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ /* Mixer */
+ {
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* Playback */
+ {
+ .ifnum = 1,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* Capture */
+ {
+ .ifnum = 2,
+ .type = QUIRK_IGNORE_INTERFACE,
+ },
+ /* HID Device, .ifnum = 3 */
+ {
+ .ifnum = -1,
+ }
+ }
+ }
+},
/*
* Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface
@@ -139,7 +214,13 @@
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL
},
{
- USB_DEVICE(0x046d, 0x0990),
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x046d,
+ .idProduct = 0x0990,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.vendor_name = "Logitech, Inc.",
.product_name = "QuickCam Pro 9000",
@@ -240,9 +321,125 @@ YAMAHA_DEVICE(0x104f, NULL),
YAMAHA_DEVICE(0x1050, NULL),
YAMAHA_DEVICE(0x1051, NULL),
YAMAHA_DEVICE(0x1052, NULL),
+YAMAHA_INTERFACE(0x1053, 0, NULL),
+YAMAHA_INTERFACE(0x1054, 0, NULL),
+YAMAHA_DEVICE(0x1055, NULL),
+YAMAHA_DEVICE(0x1056, NULL),
+YAMAHA_DEVICE(0x1057, NULL),
+YAMAHA_DEVICE(0x1058, NULL),
+YAMAHA_DEVICE(0x1059, NULL),
+YAMAHA_DEVICE(0x105a, NULL),
+YAMAHA_DEVICE(0x105b, NULL),
+YAMAHA_DEVICE(0x105c, NULL),
+YAMAHA_DEVICE(0x105d, NULL),
+{
+ USB_DEVICE(0x0499, 0x1503),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "MOX6/MOX8", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_YAMAHA
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ USB_DEVICE(0x0499, 0x1507),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "THR10", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_YAMAHA
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ USB_DEVICE(0x0499, 0x150a),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "THR5A", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_YAMAHA
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ USB_DEVICE(0x0499, 0x150c),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "THR10C", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_YAMAHA
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
YAMAHA_DEVICE(0x2000, "DGP-7"),
YAMAHA_DEVICE(0x2001, "DGP-5"),
YAMAHA_DEVICE(0x2002, NULL),
+YAMAHA_DEVICE(0x2003, NULL),
YAMAHA_DEVICE(0x5000, "CS1D"),
YAMAHA_DEVICE(0x5001, "DSP1D"),
YAMAHA_DEVICE(0x5002, "DME32"),
@@ -263,6 +460,17 @@ YAMAHA_DEVICE(0x7000, "DTX"),
YAMAHA_DEVICE(0x7010, "UB99"),
#undef YAMAHA_DEVICE
#undef YAMAHA_INTERFACE
+/* this catches most recent vendor-specific Yamaha devices */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x0499,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUTODETECT
+ }
+},
/*
* Roland/RolandED/Edirol/BOSS devices
@@ -693,11 +901,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 1,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,
@@ -938,7 +1146,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Roland M-1000 support */
{
/*
* Has ID 0x0038 when not in "Advanced Driver" mode;
@@ -1053,7 +1260,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol M-100FX support */
{
/* has ID 0x004e when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x004c),
@@ -1136,30 +1342,39 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* has ID 0x0067 when not in "Advanced Driver" mode */
- USB_DEVICE(0x0582, 0x0065),
+ /* has ID 0x0066 when not in "Advanced Driver" mode */
+ USB_DEVICE(0x0582, 0x0064),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "PCR-1",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0003
+ /* .vendor_name = "EDIROL", */
+ /* .product_name = "PCR-1", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
}
}
},
{
- /* has ID 0x006b when not in "Advanced Driver" mode */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x006a),
+ /* has ID 0x0067 when not in "Advanced Driver" mode */
+ USB_DEVICE(0x0582, 0x0065),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SP-606",
- .ifnum = 3,
+ /* .vendor_name = "EDIROL", */
+ /* .product_name = "PCR-1", */
+ .ifnum = 0,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const struct snd_usb_midi_endpoint_info) {
.out_cables = 0x0001,
- .in_cables = 0x0001
+ .in_cables = 0x0003
}
}
},
@@ -1250,8 +1465,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Roland V-SYNTH XT support */
- /* TODO: add BOSS GT-PRO support */
{
/* has ID 0x008c when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x008b),
@@ -1266,42 +1479,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol PC-80 support */
-{
- USB_DEVICE(0x0582, 0x0096),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "UA-1EX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
- }
-},
-{
- USB_DEVICE(0x0582, 0x009a),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "UM-3EX",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x000f,
- .in_cables = 0x000f
- }
- }
-},
{
/*
* This quirk is for the "Advanced Driver" mode. If off, the UA-4FX
@@ -1332,26 +1509,10 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
- /* TODO: add Edirol MD-P1 support */
{
- USB_DEVICE(0x582, 0x00a6),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "Juno-G",
- .ifnum = 0,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
- }
-},
-{
- /* Roland SH-201 */
- USB_DEVICE(0x0582, 0x00ad),
+ /* Edirol M-16DX */
+ USB_DEVICE(0x0582, 0x00c4),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SH-201",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
@@ -1378,29 +1539,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* Roland SonicCell */
- USB_DEVICE(0x0582, 0x00c2),
+ /* Advanced modes of the Edirol UA-25EX.
+ * For the standard mode, UA-25EX has ID 0582:00e7, which
+ * offers only 16-bit PCM at 44.1 kHz and no MIDI.
+ */
+ USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e6),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "Roland",
- .product_name = "SonicCell",
+ .vendor_name = "EDIROL",
+ .product_name = "UA-25EX",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = 2,
- .type = QUIRK_MIDI_FIXED_ENDPOINT,
- .data = & (const struct snd_usb_midi_endpoint_info) {
- .out_cables = 0x0001,
- .in_cables = 0x0001
- }
+ .type = QUIRK_AUDIO_EDIROL_UAXX
},
{
.ifnum = -1
@@ -1409,25 +1569,61 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- /* Edirol M-16DX */
- /* FIXME: This quirk gives a good-working capture stream but the
- * playback seems problematic because of lacking of sync
- * with capture stream. It needs to sync with the capture
- * clock. As now, you'll get frequent sound distortions
- * via the playback.
- */
- USB_DEVICE(0x0582, 0x00c4),
+ /* Edirol UM-3G */
+ USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .ifnum = 0,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0007,
+ .in_cables = 0x0007
+ }
+ }
+},
+{
+ /* only 44.1 kHz works at the moment */
+ USB_DEVICE(0x0582, 0x0120),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "OCTO-CAPTURE", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 10,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x05,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 12,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x85,
+ .ep_attr = 0x25,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 2,
@@ -1438,25 +1634,63 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 4,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
.ifnum = -1
}
}
}
},
{
- /* BOSS GT-10 */
- USB_DEVICE(0x0582, 0x00da),
+ /* only 44.1 kHz works at the moment */
+ USB_DEVICE(0x0582, 0x012f),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "QUAD-CAPTURE", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x05,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 6,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .endpoint = 0x85,
+ .ep_attr = 0x25,
+ .rates = SNDRV_PCM_RATE_44100,
+ .rate_min = 44100,
+ .rate_max = 44100,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) { 44100 }
+ }
},
{
.ifnum = 2,
@@ -1467,34 +1701,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- .ifnum = -1
- }
- }
- }
-},
-{
- /* Advanced modes of the Edirol UA-25EX.
- * For the standard mode, UA-25EX has ID 0582:00e7, which
- * offers only 16-bit PCM at 44.1 kHz and no MIDI.
- */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e6),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- .vendor_name = "EDIROL",
- .product_name = "UA-25EX",
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_EDIROL_UAXX
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_EDIROL_UAXX
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
},
{
- .ifnum = 2,
- .type = QUIRK_AUDIO_EDIROL_UAXX
+ .ifnum = 4,
+ .type = QUIRK_IGNORE_INTERFACE
},
{
.ifnum = -1
@@ -1502,27 +1714,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
+/* this catches most recent vendor-specific Roland devices */
{
- /* has ID 0x00ea when not in Advanced Driver mode */
- USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9),
- .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
- /* .vendor_name = "Roland", */
- /* .product_name = "UA-1G", */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = 0x0582,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
.ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = (const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 0,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
- },
- {
- .ifnum = -1
- }
- }
+ .type = QUIRK_AUTODETECT
}
},
@@ -1827,24 +2027,206 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2080),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "M-Audio", */
+ /* .product_name = "Fast Track C400", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ /* Playback */
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 6,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ },
+ .clock = 0x80,
+ }
+ },
+ /* Capture */
+ {
+ .ifnum = 3,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 3,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ },
+ .clock = 0x80,
+ }
+ },
+ /* MIDI */
+ {
+ .ifnum = -1 /* Interface = 4 */
+ }
+ }
+ }
+},
+{
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2031),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "M-Audio", */
+ /* .product_name = "Fast Track C600", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ /* Playback */
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ },
+ .clock = 0x80,
+ }
+ },
+ /* Capture */
+ {
+ .ifnum = 3,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 6,
+ .iface = 3,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ },
+ .clock = 0x80,
+ }
+ },
+ /* MIDI */
+ {
+ .ifnum = -1 /* Interface = 4 */
+ }
+ }
+ }
+},
+{
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
- /* .product_name = "Fast Track Ultra 8", */
+ /* .product_name = "Fast Track Ultra", */
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
/* interface 3 (MIDI) is standard compliant */
{
@@ -1854,7 +2236,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE(0x0763, 0x2081),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
@@ -1863,15 +2245,55 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
},
{
.ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = 0x09,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
{
.ifnum = 2,
- .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 8,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x81,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
},
/* interface 3 (MIDI) is standard compliant */
{
@@ -1919,7 +2341,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_MIDI_FASTLANE
+ .type = QUIRK_MIDI_RAW_BYTES
},
{
.ifnum = 1,
@@ -1973,6 +2395,27 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* KORG devices */
+{
+ USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "KORG, Inc.",
+ /* .product_name = "PANDORA PX5D", */
+ .ifnum = 3,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ }
+},
+
+{
+ USB_DEVICE_VENDOR_SPEC(0x0944, 0x0201),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "KORG, Inc.",
+ /* .product_name = "ToneLab ST", */
+ .ifnum = 3,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ }
+},
+
/* AKAI devices */
{
USB_DEVICE(0x09e8, 0x0062),
@@ -2068,6 +2511,95 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
+ USB_DEVICE(0x1235, 0x000e),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Novation", */
+ /* .product_name = "Launchpad", */
+ .ifnum = 0,
+ .type = QUIRK_MIDI_RAW_BYTES
+ }
+},
+{
+ USB_DEVICE(0x1235, 0x0010),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Focusrite",
+ .product_name = "Saffire 6 USB",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .nr_rates = 2,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000
+ }
+ }
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_MIDI_RAW_BYTES
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ USB_DEVICE(0x1235, 0x0018),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Novation",
+ .product_name = "Twitch",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .nr_rates = 2,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000
+ }
+ }
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_MIDI_RAW_BYTES
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.vendor_name = "Novation",
@@ -2076,6 +2608,57 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_MIDI_NOVATION
}
},
+{
+ /*
+ * Focusrite Scarlett 18i6
+ *
+ * Avoid mixer creation, which otherwise fails because some of
+ * the interface descriptor subtypes for interface 0 are
+ * unknown. That should be fixed or worked-around but this at
+ * least allows the device to be used successfully with a DAW
+ * and an external mixer. See comments below about other
+ * ignored interfaces.
+ */
+ USB_DEVICE(0x1235, 0x8004),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Focusrite",
+ .product_name = "Scarlett 18i6",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = & (const struct snd_usb_audio_quirk[]) {
+ {
+ /* InterfaceSubClass 1 (Control Device) */
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ /* InterfaceSubClass 1 (Control Device) */
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 4,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE
+ },
+ {
+ /* InterfaceSubClass 1 (Device Firmware Update) */
+ .ifnum = 5,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Access Music devices */
{
@@ -2115,6 +2698,32 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* Native Instruments MK2 series */
+{
+ /* Komplete Audio 6 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1000,
+},
+{
+ /* Traktor Audio 6 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1010,
+},
+{
+ /* Traktor Audio 10 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1020,
+},
+
+/* KeithMcMillen Stringport */
+{
+ USB_DEVICE(0x1f38, 0x0001),
+ .bInterfaceClass = USB_CLASS_AUDIO,
+},
+
/* Miditech devices */
{
USB_DEVICE(0x4752, 0x0011),
@@ -2152,7 +2761,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7201),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x7210),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2166,7 +2775,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7202),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x7217),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2180,7 +2789,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7203),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x721b),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2194,7 +2803,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7204),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x721e),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2208,7 +2817,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7205),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x721f),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2222,7 +2831,21 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7250),
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x7240),
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Hauppauge",
+ .product_name = "HVR-850",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+ }
+},
+{
+ USB_DEVICE_VENDOR_SPEC(0x2040, 0x7280),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2236,7 +2859,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
{
- USB_DEVICE_VENDOR_SPEC(0x2040, 0x7230),
+ USB_DEVICE_VENDOR_SPEC(0x0fd9, 0x0008),
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2244,7 +2867,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
.vendor_name = "Hauppauge",
- .product_name = "HVR-850",
+ .product_name = "HVR-950Q",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_AUDIO_ALIGN_TRANSFER,
}
@@ -2276,7 +2899,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
.endpoint = 0x02,
.ep_attr = 0x01,
- .maxpacksize = 0x130,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.rate_min = 44100,
@@ -2295,6 +2917,231 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+/* DIGIDESIGN MBOX 2 */
+{
+ USB_DEVICE(0x0dba, 0x3000),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Digidesign",
+ .product_name = "Mbox 2",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3BE,
+ .channels = 2,
+ .iface = 2,
+ .altsetting = 2,
+ .altset_idx = 1,
+ .attributes = 0x00,
+ .endpoint = 0x03,
+ .ep_attr = USB_ENDPOINT_SYNC_ASYNC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 4,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3BE,
+ .channels = 2,
+ .iface = 4,
+ .altsetting = 2,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x85,
+ .ep_attr = USB_ENDPOINT_SYNC_SYNC,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .nr_rates = 1,
+ .rate_table = (unsigned int[]) {
+ 48000
+ }
+ }
+ },
+ {
+ .ifnum = 5,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 6,
+ .type = QUIRK_MIDI_MIDIMAN,
+ .data = &(const struct snd_usb_midi_endpoint_info) {
+ .out_ep = 0x02,
+ .out_cables = 0x0001,
+ .in_ep = 0x81,
+ .in_interval = 0x01,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
+ /* Tascam US122 MKII - playback-only support */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0644,
+ .idProduct = 0x8021,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "TASCAM",
+ .product_name = "US122 MKII",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 2,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x02,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 44100,
+ .rate_max = 96000,
+ .nr_rates = 4,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200, 96000
+ }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
+/* Microsoft XboxLive Headset/Xbox Communicator */
+{
+ USB_DEVICE(0x045e, 0x0283),
+ .bInterfaceClass = USB_CLASS_PER_INTERFACE,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Microsoft",
+ .product_name = "XboxLive Headset/Xbox Communicator",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ /* playback */
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 0,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x04,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 22050,
+ .rate_max = 22050
+ }
+ },
+ {
+ /* capture */
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 1,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x85,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 16000,
+ .rate_max = 16000
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
+/* Reloop Play */
+{
+ USB_DEVICE(0x200c, 0x100b),
+ .bInterfaceClass = USB_CLASS_PER_INTERFACE,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_SYNC_ADAPTIVE,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .nr_rates = 2,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000
+ }
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
{
/*
* Some USB MIDI devices don't have an audio control interface,
@@ -2310,4 +3157,16 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
},
+{
+ /*
+ * The original product_name is "USB Sound Device", however this name
+ * is also used by the CM106 based cards, so make it unique.
+ */
+ USB_DEVICE(0x0d8c, 0x0103),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .product_name = "Audio Advantage MicroII",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+
#undef USB_DEVICE_VENDOR_SPEC
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index b45e54c09ba..7c57f2268dd 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -18,7 +18,9 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
@@ -32,6 +34,8 @@
#include "helper.h"
#include "endpoint.h"
#include "pcm.h"
+#include "clock.h"
+#include "stream.h"
/*
* handle the quirks for the contained interfaces
@@ -104,9 +108,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
alts = &iface->altsetting[0];
altsd = get_iface_desc(alts);
- err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber);
+ err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
if (err < 0) {
- snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
+ usb_audio_err(chip, "cannot setup if %d: error %d\n",
altsd->bInterfaceNumber, err);
return err;
}
@@ -125,27 +129,32 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
{
struct audioformat *fp;
struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
int stream, err;
unsigned *rate_table = NULL;
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
- if (! fp) {
- snd_printk(KERN_ERR "cannot memdup\n");
+ if (!fp) {
+ usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+ if (fp->nr_rates > MAX_NR_RATES) {
+ kfree(fp);
+ return -EINVAL;
+ }
if (fp->nr_rates > 0) {
- rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
+ rate_table = kmemdup(fp->rate_table,
+ sizeof(int) * fp->nr_rates, GFP_KERNEL);
if (!rate_table) {
kfree(fp);
return -ENOMEM;
}
- memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
fp->rate_table = rate_table;
}
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
- err = snd_usb_add_audio_endpoint(chip, stream, fp);
+ err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
kfree(fp);
kfree(rate_table);
@@ -158,14 +167,225 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
return -EINVAL;
}
alts = &iface->altsetting[fp->altset_idx];
- fp->datainterval = snd_usb_parse_datainterval(chip, alts);
- fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ altsd = get_iface_desc(alts);
+ fp->protocol = altsd->bInterfaceProtocol;
+
+ if (fp->datainterval == 0)
+ fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+ if (fp->maxpacksize == 0)
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
usb_set_interface(chip->dev, fp->iface, 0);
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return 0;
}
+static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+ struct uac1_as_header_descriptor *ashd;
+ struct uac_format_type_i_discrete_descriptor *fmtd;
+
+ /*
+ * Most Roland/Yamaha audio streaming interfaces have more or less
+ * standard descriptors, but older devices might lack descriptors, and
+ * future ones might change, so ensure that we fail silently if the
+ * interface doesn't look exactly right.
+ */
+
+ /* must have a non-zero altsetting for streaming */
+ if (iface->num_altsetting < 2)
+ return -ENODEV;
+ alts = &iface->altsetting[1];
+ altsd = get_iface_desc(alts);
+
+ /* must have an isochronous endpoint for streaming */
+ if (altsd->bNumEndpoints < 1)
+ return -ENODEV;
+ epd = get_endpoint(alts, 0);
+ if (!usb_endpoint_xfer_isoc(epd))
+ return -ENODEV;
+
+ /* must have format descriptors */
+ ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_AS_GENERAL);
+ fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+ UAC_FORMAT_TYPE);
+ if (!ashd || ashd->bLength < 7 ||
+ !fmtd || fmtd->bLength < 8)
+ return -ENODEV;
+
+ return create_standard_audio_quirk(chip, iface, driver, NULL);
+}
+
+static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ static const struct snd_usb_audio_quirk yamaha_midi_quirk = {
+ .type = QUIRK_MIDI_YAMAHA
+ };
+ struct usb_midi_in_jack_descriptor *injd;
+ struct usb_midi_out_jack_descriptor *outjd;
+
+ /* must have some valid jack descriptors */
+ injd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, USB_MS_MIDI_IN_JACK);
+ outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+ NULL, USB_MS_MIDI_OUT_JACK);
+ if (!injd && !outjd)
+ return -ENODEV;
+ if (injd && (injd->bLength < 5 ||
+ (injd->bJackType != USB_MS_EMBEDDED &&
+ injd->bJackType != USB_MS_EXTERNAL)))
+ return -ENODEV;
+ if (outjd && (outjd->bLength < 6 ||
+ (outjd->bJackType != USB_MS_EMBEDDED &&
+ outjd->bJackType != USB_MS_EXTERNAL)))
+ return -ENODEV;
+ return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk);
+}
+
+static int create_roland_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ static const struct snd_usb_audio_quirk roland_midi_quirk = {
+ .type = QUIRK_MIDI_ROLAND
+ };
+ u8 *roland_desc = NULL;
+
+ /* might have a vendor-specific descriptor <06 24 F1 02 ...> */
+ for (;;) {
+ roland_desc = snd_usb_find_csint_desc(alts->extra,
+ alts->extralen,
+ roland_desc, 0xf1);
+ if (!roland_desc)
+ return -ENODEV;
+ if (roland_desc[0] < 6 || roland_desc[3] != 2)
+ continue;
+ return create_any_midi_quirk(chip, iface, driver,
+ &roland_midi_quirk);
+ }
+}
+
+static int create_std_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ struct usb_host_interface *alts)
+{
+ struct usb_ms_header_descriptor *mshd;
+ struct usb_ms_endpoint_descriptor *msepd;
+
+ /* must have the MIDIStreaming interface header descriptor*/
+ mshd = (struct usb_ms_header_descriptor *)alts->extra;
+ if (alts->extralen < 7 ||
+ mshd->bLength < 7 ||
+ mshd->bDescriptorType != USB_DT_CS_INTERFACE ||
+ mshd->bDescriptorSubtype != USB_MS_HEADER)
+ return -ENODEV;
+ /* must have the MIDIStreaming endpoint descriptor*/
+ msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra;
+ if (alts->endpoint[0].extralen < 4 ||
+ msepd->bLength < 4 ||
+ msepd->bDescriptorType != USB_DT_CS_ENDPOINT ||
+ msepd->bDescriptorSubtype != UAC_MS_GENERAL ||
+ msepd->bNumEmbMIDIJack < 1 ||
+ msepd->bNumEmbMIDIJack > 16)
+ return -ENODEV;
+
+ return create_any_midi_quirk(chip, iface, driver, NULL);
+}
+
+static int create_auto_midi_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct usb_endpoint_descriptor *epd;
+ int err;
+
+ alts = &iface->altsetting[0];
+ altsd = get_iface_desc(alts);
+
+ /* must have at least one bulk/interrupt endpoint for streaming */
+ if (altsd->bNumEndpoints < 1)
+ return -ENODEV;
+ epd = get_endpoint(alts, 0);
+ if (!usb_endpoint_xfer_bulk(epd) &&
+ !usb_endpoint_xfer_int(epd))
+ return -ENODEV;
+
+ switch (USB_ID_VENDOR(chip->usb_id)) {
+ case 0x0499: /* Yamaha */
+ err = create_yamaha_midi_quirk(chip, iface, driver, alts);
+ if (err != -ENODEV)
+ return err;
+ break;
+ case 0x0582: /* Roland */
+ err = create_roland_midi_quirk(chip, iface, driver, alts);
+ if (err != -ENODEV)
+ return err;
+ break;
+ }
+
+ return create_std_midi_quirk(chip, iface, driver, alts);
+}
+
+static int create_autodetect_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver)
+{
+ int err;
+
+ err = create_auto_pcm_quirk(chip, iface, driver);
+ if (err == -ENODEV)
+ err = create_auto_midi_quirk(chip, iface, driver);
+ return err;
+}
+
+static int create_autodetect_quirks(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
+ int ifcount, ifnum, err;
+
+ err = create_autodetect_quirk(chip, iface, driver);
+ if (err < 0)
+ return err;
+
+ /*
+ * ALSA PCM playback/capture devices cannot be registered in two steps,
+ * so we have to claim the other corresponding interface here.
+ */
+ ifcount = chip->dev->actconfig->desc.bNumInterfaces;
+ for (ifnum = 0; ifnum < ifcount; ifnum++) {
+ if (ifnum == probed_ifnum || quirk->ifnum >= 0)
+ continue;
+ iface = usb_ifnum_to_if(chip->dev, ifnum);
+ if (!iface ||
+ usb_interface_claimed(iface) ||
+ get_iface_desc(iface->altsetting)->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC)
+ continue;
+
+ err = create_autodetect_quirk(chip, iface, driver);
+ if (err >= 0)
+ usb_driver_claim_interface(driver, iface, (void *)-1L);
+ }
+
+ return 0;
+}
+
/*
* Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
* The only way to detect the sample rate is by looking at wMaxPacketSize.
@@ -221,10 +441,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
if (altsd->bNumEndpoints != 1)
return -ENXIO;
- fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+ fp = kmemdup(&ua_format, sizeof(*fp), GFP_KERNEL);
if (!fp)
return -ENOMEM;
- memcpy(fp, &ua_format, sizeof(*fp));
fp->iface = altsd->bInterfaceNumber;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
@@ -245,14 +464,14 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->rate_max = fp->rate_min = 96000;
break;
default:
- snd_printk(KERN_ERR "unknown sample rate\n");
+ usb_audio_err(chip, "unknown sample rate\n");
kfree(fp);
return -ENXIO;
}
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
- err = snd_usb_add_audio_endpoint(chip, stream, fp);
+ err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
kfree(fp);
return err;
@@ -262,10 +481,24 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
}
/*
+ * Create a standard mixer for the specified interface.
+ */
+static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ if (quirk->ifnum < 0)
+ return 0;
+
+ return snd_usb_create_mixer(chip, quirk->ifnum, 0);
+}
+
+/*
* audio-interface quirks
*
* returns zero if no standard audio/MIDI parsing is needed.
- * returns a postive value if standard audio/midi interfaces are parsed
+ * returns a positive value if standard audio/midi interfaces are parsed
* after this.
* returns a negative value at error.
*/
@@ -281,25 +514,29 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
static const quirk_func_t quirk_funcs[] = {
[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
[QUIRK_COMPOSITE] = create_composite_quirk,
+ [QUIRK_AUTODETECT] = create_autodetect_quirks,
[QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
[QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
[QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
+ [QUIRK_MIDI_ROLAND] = create_any_midi_quirk,
[QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
[QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
- [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
+ [QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk,
[QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
[QUIRK_MIDI_CME] = create_any_midi_quirk,
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
+ [QUIRK_MIDI_FTDI] = create_any_midi_quirk,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
- [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
+ [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk,
+ [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
return quirk_funcs[quirk->type](chip, iface, driver, quirk);
} else {
- snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
+ usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
return -ENXIO;
}
}
@@ -318,18 +555,21 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
- snd_printdd("sending Extigy boot sequence...\n");
+ dev_dbg(&dev->dev, "sending Extigy boot sequence...\n");
/* Send message to force it to reconnect with full interface. */
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
- 0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
- if (err < 0) snd_printdd("error sending boot message: %d\n", err);
+ 0x10, 0x43, 0x0001, 0x000a, NULL, 0);
+ if (err < 0)
+ dev_dbg(&dev->dev, "error sending boot message: %d\n", err);
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
&dev->descriptor, sizeof(dev->descriptor));
config = dev->actconfig;
- if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
+ if (err < 0)
+ dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
err = usb_reset_configuration(dev);
- if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
- snd_printdd("extigy_boot: new boot length = %d\n",
+ if (err < 0)
+ dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
+ dev_dbg(&dev->dev, "extigy_boot: new boot length = %d\n",
le16_to_cpu(get_cfg_desc(config)->wTotalLength));
return -ENODEV; /* quit this anyway */
}
@@ -342,16 +582,43 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- 0, 0, &buf, 1, 1000);
+ 0, 0, &buf, 1);
if (buf == 0) {
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
- 1, 2000, NULL, 0, 1000);
+ 1, 2000, NULL, 0);
return -ENODEV;
}
return 0;
}
+static int snd_usb_fasttrackpro_boot_quirk(struct usb_device *dev)
+{
+ int err;
+
+ if (dev->actconfig->desc.bConfigurationValue == 1) {
+ dev_info(&dev->dev,
+ "Fast Track Pro switching to config #2\n");
+ /* This function has to be available by the usb core module.
+ * if it is not avialable the boot quirk has to be left out
+ * and the configuration has to be set by udev or hotplug
+ * rules
+ */
+ err = usb_driver_set_configuration(dev, 2);
+ if (err < 0)
+ dev_dbg(&dev->dev,
+ "error usb_driver_set_configuration: %d\n",
+ err);
+ /* Always return an error, so that we stop creating a device
+ that will just be destroyed and recreated with a new
+ configuration */
+ return -ENODEV;
+ } else
+ dev_info(&dev->dev, "Fast Track Pro config OK\n");
+
+ return 0;
+}
+
/*
* C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
* documented in the device's data sheet.
@@ -365,7 +632,7 @@ static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 valu
buf[3] = reg;
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
- 0, 0, &buf, 4, 1000);
+ 0, 0, &buf, 4);
}
static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
@@ -385,8 +652,8 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
*/
static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
{
- int err, reg;
- int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
+ int err = 0, reg;
+ int val[] = {0x2004, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
@@ -397,6 +664,30 @@ static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
return err;
}
+/* quirk for Plantronics GameCom 780 with CM6302 chip */
+static int snd_usb_gamecon780_boot_quirk(struct usb_device *dev)
+{
+ /* set the initial volume and don't change; other values are either
+ * too loud or silent due to firmware bug (bko#65251)
+ */
+ u8 buf[2] = { 0x74, 0xdc };
+ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ UAC_FU_VOLUME << 8, 9 << 8, buf, 2);
+}
+
+/*
+ * Novation Twitch DJ controller
+ * Focusrite Novation Saffire 6 USB audio card
+ */
+static int snd_usb_novation_boot_quirk(struct usb_device *dev)
+{
+ /* preemptively set up the device because otherwise the
+ * raw MIDI endpoints are not active */
+ usb_set_interface(dev, 0, 1);
+ return 0;
+}
+
/*
* This call will put the synth in "USB send" mode, i.e it will send MIDI
* messages through USB (this is disabled at startup). The synth will
@@ -424,18 +715,165 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
}
/*
+ * Some sound cards from Native Instruments are in fact compliant to the USB
+ * audio standard of version 2 and other approved USB standards, even though
+ * they come up as vendor-specific device when first connected.
+ *
+ * However, they can be told to come up with a new set of descriptors
+ * upon their next enumeration, and the interfaces announced by the new
+ * descriptors will then be handled by the kernel's class drivers. As the
+ * product ID will also change, no further checks are required.
+ */
+
+static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
+{
+ int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 0, NULL, 0, 1000);
+
+ if (ret < 0)
+ return ret;
+
+ usb_reset_device(dev);
+
+ /* return -EAGAIN, so the creation of an audio interface for this
+ * temporary device is aborted. The device will reconnect with a
+ * new product ID */
+ return -EAGAIN;
+}
+
+static void mbox2_setup_48_24_magic(struct usb_device *dev)
+{
+ u8 srate[3];
+ u8 temp[12];
+
+ /* Choose 48000Hz permanently */
+ srate[0] = 0x80;
+ srate[1] = 0xbb;
+ srate[2] = 0x00;
+
+ /* Send the magic! */
+ snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x01, 0x22, 0x0100, 0x0085, &temp, 0x0003);
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x81, 0xa2, 0x0100, 0x0085, &srate, 0x0003);
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x81, 0xa2, 0x0100, 0x0086, &srate, 0x0003);
+ snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x81, 0xa2, 0x0100, 0x0003, &srate, 0x0003);
+ return;
+}
+
+/* Digidesign Mbox 2 needs to load firmware onboard
+ * and driver must wait a few seconds for initialisation.
+ */
+
+#define MBOX2_FIRMWARE_SIZE 646
+#define MBOX2_BOOT_LOADING 0x01 /* Hard coded into the device */
+#define MBOX2_BOOT_READY 0x02 /* Hard coded into the device */
+
+static int snd_usb_mbox2_boot_quirk(struct usb_device *dev)
+{
+ struct usb_host_config *config = dev->actconfig;
+ int err;
+ u8 bootresponse[0x12];
+ int fwsize;
+ int count;
+
+ fwsize = le16_to_cpu(get_cfg_desc(config)->wTotalLength);
+
+ if (fwsize != MBOX2_FIRMWARE_SIZE) {
+ dev_err(&dev->dev, "Invalid firmware size=%d.\n", fwsize);
+ return -ENODEV;
+ }
+
+ dev_dbg(&dev->dev, "Sending Digidesign Mbox 2 boot sequence...\n");
+
+ count = 0;
+ bootresponse[0] = MBOX2_BOOT_LOADING;
+ while ((bootresponse[0] == MBOX2_BOOT_LOADING) && (count < 10)) {
+ msleep(500); /* 0.5 second delay */
+ snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ /* Control magic - load onboard firmware */
+ 0x85, 0xc0, 0x0001, 0x0000, &bootresponse, 0x0012);
+ if (bootresponse[0] == MBOX2_BOOT_READY)
+ break;
+ dev_dbg(&dev->dev, "device not ready, resending boot sequence...\n");
+ count++;
+ }
+
+ if (bootresponse[0] != MBOX2_BOOT_READY) {
+ dev_err(&dev->dev, "Unknown bootresponse=%d, or timed out, ignoring device.\n", bootresponse[0]);
+ return -ENODEV;
+ }
+
+ dev_dbg(&dev->dev, "device initialised!\n");
+
+ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
+ &dev->descriptor, sizeof(dev->descriptor));
+ config = dev->actconfig;
+ if (err < 0)
+ dev_dbg(&dev->dev, "error usb_get_descriptor: %d\n", err);
+
+ err = usb_reset_configuration(dev);
+ if (err < 0)
+ dev_dbg(&dev->dev, "error usb_reset_configuration: %d\n", err);
+ dev_dbg(&dev->dev, "mbox2_boot: new boot length = %d\n",
+ le16_to_cpu(get_cfg_desc(config)->wTotalLength));
+
+ mbox2_setup_48_24_magic(dev);
+
+ dev_info(&dev->dev, "Digidesign Mbox 2: 24bit 48kHz");
+
+ return 0; /* Successful boot */
+}
+
+/*
* Setup quirks
*/
-#define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */
-#define AUDIOPHILE_SET_DTS 0x02 /* if set, enable DTS Digital Output */
-#define AUDIOPHILE_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
-#define AUDIOPHILE_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
-#define AUDIOPHILE_SET_DI 0x10 /* if set, enable Digital Input */
-#define AUDIOPHILE_SET_MASK 0x1F /* bit mask for setup value */
-#define AUDIOPHILE_SET_24B_48K_DI 0x19 /* value for 24bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_24B_48K_NOTDI 0x09 /* value for 24bits+48KHz+No Digital Input */
-#define AUDIOPHILE_SET_16B_48K_DI 0x11 /* value for 16bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_16B_48K_NOTDI 0x01 /* value for 16bits+48KHz+No Digital Input */
+#define MAUDIO_SET 0x01 /* parse device_setup */
+#define MAUDIO_SET_COMPATIBLE 0x80 /* use only "win-compatible" interfaces */
+#define MAUDIO_SET_DTS 0x02 /* enable DTS Digital Output */
+#define MAUDIO_SET_96K 0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
+#define MAUDIO_SET_24B 0x08 /* 24bits sample if set, 16bits otherwise */
+#define MAUDIO_SET_DI 0x10 /* enable Digital Input */
+#define MAUDIO_SET_MASK 0x1f /* bit mask for setup value */
+#define MAUDIO_SET_24B_48K_DI 0x19 /* 24bits+48KHz+Digital Input */
+#define MAUDIO_SET_24B_48K_NOTDI 0x09 /* 24bits+48KHz+No Digital Input */
+#define MAUDIO_SET_16B_48K_DI 0x11 /* 16bits+48KHz+Digital Input */
+#define MAUDIO_SET_16B_48K_NOTDI 0x01 /* 16bits+48KHz+No Digital Input */
+
+static int quattro_skip_setting_quirk(struct snd_usb_audio *chip,
+ int iface, int altno)
+{
+ /* Reset ALL ifaces to 0 altsetting.
+ * Call it for every possible altsetting of every interface.
+ */
+ usb_set_interface(chip->dev, iface, 0);
+ if (chip->setup & MAUDIO_SET) {
+ if (chip->setup & MAUDIO_SET_COMPATIBLE) {
+ if (iface != 1 && iface != 2)
+ return 1; /* skip all interfaces but 1 and 2 */
+ } else {
+ unsigned int mask;
+ if (iface == 1 || iface == 2)
+ return 1; /* skip interfaces 1 and 2 */
+ if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
+ return 1; /* skip this altsetting */
+ mask = chip->setup & MAUDIO_SET_MASK;
+ if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
+ return 1; /* skip this altsetting */
+ if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
+ return 1; /* skip this altsetting */
+ if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 4)
+ return 1; /* skip this altsetting */
+ }
+ }
+ usb_audio_dbg(chip,
+ "using altsetting %d for interface %d config %d\n",
+ altno, iface, chip->setup);
+ return 0; /* keep this altsetting */
+}
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface,
@@ -446,30 +884,64 @@ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
*/
usb_set_interface(chip->dev, iface, 0);
- if (chip->setup & AUDIOPHILE_SET) {
- if ((chip->setup & AUDIOPHILE_SET_DTS)
- && altno != 6)
+ if (chip->setup & MAUDIO_SET) {
+ unsigned int mask;
+ if ((chip->setup & MAUDIO_SET_DTS) && altno != 6)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_96K)
- && altno != 1)
+ if ((chip->setup & MAUDIO_SET_96K) && altno != 1)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_24B_48K_DI && altno != 2)
+ mask = chip->setup & MAUDIO_SET_MASK;
+ if (mask == MAUDIO_SET_24B_48K_DI && altno != 2)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
+ if (mask == MAUDIO_SET_24B_48K_NOTDI && altno != 3)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_16B_48K_DI && altno != 4)
+ if (mask == MAUDIO_SET_16B_48K_DI && altno != 4)
return 1; /* skip this altsetting */
- if ((chip->setup & AUDIOPHILE_SET_MASK) ==
- AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
+ if (mask == MAUDIO_SET_16B_48K_NOTDI && altno != 5)
return 1; /* skip this altsetting */
}
return 0; /* keep this altsetting */
}
+static int fasttrackpro_skip_setting_quirk(struct snd_usb_audio *chip,
+ int iface, int altno)
+{
+ /* Reset ALL ifaces to 0 altsetting.
+ * Call it for every possible altsetting of every interface.
+ */
+ usb_set_interface(chip->dev, iface, 0);
+
+ /* possible configuration where both inputs and only one output is
+ *used is not supported by the current setup
+ */
+ if (chip->setup & (MAUDIO_SET | MAUDIO_SET_24B)) {
+ if (chip->setup & MAUDIO_SET_96K) {
+ if (altno != 3 && altno != 6)
+ return 1;
+ } else if (chip->setup & MAUDIO_SET_DI) {
+ if (iface == 4)
+ return 1; /* no analog input */
+ if (altno != 2 && altno != 5)
+ return 1; /* enable only altsets 2 and 5 */
+ } else {
+ if (iface == 5)
+ return 1; /* disable digialt input */
+ if (altno != 2 && altno != 5)
+ return 1; /* enalbe only altsets 2 and 5 */
+ }
+ } else {
+ /* keep only 16-Bit mode */
+ if (altno != 1)
+ return 1;
+ }
+
+ usb_audio_dbg(chip,
+ "using altsetting %d for interface %d config %d\n",
+ altno, iface, chip->setup);
+ return 0; /* keep this altsetting */
+}
+
int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int iface,
int altno)
@@ -477,6 +949,12 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
/* audiophile usb: skip altsets incompatible with device_setup */
if (chip->usb_id == USB_ID(0x0763, 0x2003))
return audiophile_skip_setting_quirk(chip, iface, altno);
+ /* quattro usb: skip altsets incompatible with device_setup */
+ if (chip->usb_id == USB_ID(0x0763, 0x2001))
+ return quattro_skip_setting_quirk(chip, iface, altno);
+ /* fasttrackpro usb: skip altsets incompatible with device_setup */
+ if (chip->usb_id == USB_ID(0x0763, 0x2012))
+ return fasttrackpro_skip_setting_quirk(chip, iface, altno);
return 0;
}
@@ -488,27 +966,47 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
- /* SB Extigy needs special boot-up sequence */
- /* if more models come, this will go to the quirk list. */
- if (id == USB_ID(0x041e, 0x3000))
+ switch (id) {
+ case USB_ID(0x041e, 0x3000):
+ /* SB Extigy needs special boot-up sequence */
+ /* if more models come, this will go to the quirk list. */
return snd_usb_extigy_boot_quirk(dev, intf);
- /* SB Audigy 2 NX needs its own boot-up magic, too */
- if (id == USB_ID(0x041e, 0x3020))
+ case USB_ID(0x041e, 0x3020):
+ /* SB Audigy 2 NX needs its own boot-up magic, too */
return snd_usb_audigy2nx_boot_quirk(dev);
- /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
- if (id == USB_ID(0x10f5, 0x0200))
+ case USB_ID(0x10f5, 0x0200):
+ /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
return snd_usb_cm106_boot_quirk(dev);
- /* C-Media CM6206 / CM106-Like Sound Device */
- if (id == USB_ID(0x0d8c, 0x0102))
+ case USB_ID(0x0d8c, 0x0102):
+ /* C-Media CM6206 / CM106-Like Sound Device */
+ case USB_ID(0x0ccd, 0x00b1): /* Terratec Aureon 7.1 USB */
return snd_usb_cm6206_boot_quirk(dev);
- /* Access Music VirusTI Desktop */
- if (id == USB_ID(0x133e, 0x0815))
+ case USB_ID(0x0dba, 0x3000):
+ /* Digidesign Mbox 2 */
+ return snd_usb_mbox2_boot_quirk(dev);
+
+ case USB_ID(0x1235, 0x0010): /* Focusrite Novation Saffire 6 USB */
+ case USB_ID(0x1235, 0x0018): /* Focusrite Novation Twitch */
+ return snd_usb_novation_boot_quirk(dev);
+
+ case USB_ID(0x133e, 0x0815):
+ /* Access Music VirusTI Desktop */
return snd_usb_accessmusic_boot_quirk(dev);
+ case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
+ case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
+ case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
+ return snd_usb_nativeinstruments_boot_quirk(dev);
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
+ return snd_usb_fasttrackpro_boot_quirk(dev);
+ case USB_ID(0x047f, 0xc010): /* Plantronics Gamecom 780 */
+ return snd_usb_gamecon780_boot_quirk(dev);
+ }
+
return 0;
}
@@ -517,21 +1015,30 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
*/
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
{
+ /* it depends on altsetting whether the device is big-endian or not */
switch (chip->usb_id) {
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
- if (fp->endpoint & USB_DIR_IN)
+ if (fp->altsetting == 2 || fp->altsetting == 3 ||
+ fp->altsetting == 5 || fp->altsetting == 6)
return 1;
break;
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
if (chip->setup == 0x00 ||
- fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+ fp->altsetting == 1 || fp->altsetting == 2 ||
+ fp->altsetting == 3)
+ return 1;
+ break;
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro */
+ if (fp->altsetting == 2 || fp->altsetting == 3 ||
+ fp->altsetting == 5 || fp->altsetting == 6)
return 1;
+ break;
}
return 0;
}
/*
- * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
+ * For E-Mu 0404USB/0202USB/TrackerPre/0204 sample rate should be set for device,
* not for interface.
*/
@@ -579,6 +1086,7 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs,
break;
}
snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
+ subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
}
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
@@ -588,8 +1096,83 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+ case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
set_format_emu_quirk(subs, fmt);
break;
}
}
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
+{
+ /*
+ * "Playback Design" products send bogus feedback data at the start
+ * of the stream. Ignore them.
+ */
+ if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+ ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
+ ep->skip_packets = 4;
+
+ /*
+ * M-Audio Fast Track C400/C600 - when packets are not skipped, real
+ * world latency varies by approx. +/- 50 frames (at 96KHz) each time
+ * the stream is (re)started. When skipping packets 16 at endpoint
+ * start up, the real world latency is stable within +/- 1 frame (also
+ * across power cycles).
+ */
+ if ((ep->chip->usb_id == USB_ID(0x0763, 0x2030) ||
+ ep->chip->usb_id == USB_ID(0x0763, 0x2031)) &&
+ ep->type == SND_USB_ENDPOINT_TYPE_DATA)
+ ep->skip_packets = 16;
+}
+
+void snd_usb_set_interface_quirk(struct usb_device *dev)
+{
+ /*
+ * "Playback Design" products need a 50ms delay after setting the
+ * USB interface.
+ */
+ if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba)
+ mdelay(50);
+}
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size)
+{
+ /*
+ * "Playback Design" products need a 20ms delay after each
+ * class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ mdelay(20);
+}
+
+/*
+ * snd_usb_interface_dsd_format_quirks() is called from format.c to
+ * augment the PCM format bit-field for DSD types. The UAC standards
+ * don't have a designated bit field to denote DSD-capable interfaces,
+ * hence all hardware that is known to support this format has to be
+ * listed here.
+ */
+u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ unsigned int sample_bytes)
+{
+ /* Playback Designs */
+ if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+ switch (fp->altsetting) {
+ case 1:
+ fp->dsd_dop = true;
+ return SNDRV_PCM_FMTBIT_DSD_U16_LE;
+ case 2:
+ fp->dsd_bitrev = true;
+ return SNDRV_PCM_FMTBIT_DSD_U8;
+ case 3:
+ fp->dsd_bitrev = true;
+ return SNDRV_PCM_FMTBIT_DSD_U16_LE;
+ }
+ }
+
+ return 0;
+}
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 03e5e94098c..665e972a1b4 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -1,6 +1,10 @@
#ifndef __USBAUDIO_QUIRKS_H
#define __USBAUDIO_QUIRKS_H
+struct audioformat;
+struct snd_usb_endpoint;
+struct snd_usb_substream;
+
int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
@@ -20,4 +24,15 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
struct audioformat *fp);
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep);
+
+void snd_usb_set_interface_quirk(struct usb_device *dev);
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size);
+
+u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ unsigned int sample_bytes);
+
#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
new file mode 100644
index 00000000000..310a3822d2b
--- /dev/null
+++ b/sound/usb/stream.c
@@ -0,0 +1,731 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "pcm.h"
+#include "helper.h"
+#include "format.h"
+#include "clock.h"
+#include "stream.h"
+
+/*
+ * free a substream
+ */
+static void free_substream(struct snd_usb_substream *subs)
+{
+ struct audioformat *fp, *n;
+
+ if (!subs->num_formats)
+ return; /* not initialized */
+ list_for_each_entry_safe(fp, n, &subs->fmt_list, list) {
+ kfree(fp->rate_table);
+ kfree(fp->chmap);
+ kfree(fp);
+ }
+ kfree(subs->rate_list.list);
+}
+
+
+/*
+ * free a usb stream instance
+ */
+static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
+{
+ free_substream(&stream->substream[0]);
+ free_substream(&stream->substream[1]);
+ list_del(&stream->list);
+ kfree(stream);
+}
+
+static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_usb_stream *stream = pcm->private_data;
+ if (stream) {
+ stream->pcm = NULL;
+ snd_usb_audio_stream_free(stream);
+ }
+}
+
+/*
+ * initialize the substream instance.
+ */
+
+static void snd_usb_init_substream(struct snd_usb_stream *as,
+ int stream,
+ struct audioformat *fp)
+{
+ struct snd_usb_substream *subs = &as->substream[stream];
+
+ INIT_LIST_HEAD(&subs->fmt_list);
+ spin_lock_init(&subs->lock);
+
+ subs->stream = as;
+ subs->direction = stream;
+ subs->dev = as->chip->dev;
+ subs->txfr_quirk = as->chip->txfr_quirk;
+ subs->speed = snd_usb_get_speed(subs->dev);
+ subs->pkt_offset_adj = 0;
+
+ snd_usb_set_pcm_ops(as->pcm, stream);
+
+ list_add_tail(&fp->list, &subs->fmt_list);
+ subs->formats |= fp->formats;
+ subs->num_formats++;
+ subs->fmt_type = fp->fmt_type;
+ subs->ep_num = fp->endpoint;
+ if (fp->channels > subs->channels_max)
+ subs->channels_max = fp->channels;
+}
+
+/* kctl callbacks for usb-audio channel maps */
+static int usb_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_substream *subs = info->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = subs->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+/* check whether a duplicated entry exists in the audiofmt list */
+static bool have_dup_chmap(struct snd_usb_substream *subs,
+ struct audioformat *fp)
+{
+ struct list_head *p;
+
+ for (p = fp->list.prev; p != &subs->fmt_list; p = p->prev) {
+ struct audioformat *prev;
+ prev = list_entry(p, struct audioformat, list);
+ if (prev->chmap &&
+ !memcmp(prev->chmap, fp->chmap, sizeof(*fp->chmap)))
+ return true;
+ }
+ return false;
+}
+
+static int usb_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_substream *subs = info->private_data;
+ struct audioformat *fp;
+ unsigned int __user *dst;
+ int count = 0;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ int i, ch_bytes;
+
+ if (!fp->chmap)
+ continue;
+ if (have_dup_chmap(subs, fp))
+ continue;
+ /* copy the entry */
+ ch_bytes = fp->chmap->channels * 4;
+ if (size < 8 + ch_bytes)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+ put_user(ch_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ for (i = 0; i < fp->chmap->channels; i++, dst++) {
+ if (put_user(fp->chmap->map[i], dst))
+ return -EFAULT;
+ }
+
+ count += 8 + ch_bytes;
+ size -= 8 + ch_bytes;
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static int usb_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct snd_usb_substream *subs = info->private_data;
+ struct snd_pcm_chmap_elem *chmap = NULL;
+ int i;
+
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (subs->cur_audiofmt)
+ chmap = subs->cur_audiofmt->chmap;
+ if (chmap) {
+ for (i = 0; i < chmap->channels; i++)
+ ucontrol->value.integer.value[i] = chmap->map[i];
+ }
+ return 0;
+}
+
+/* create a chmap kctl assigned to the given USB substream */
+static int add_chmap(struct snd_pcm *pcm, int stream,
+ struct snd_usb_substream *subs)
+{
+ struct audioformat *fp;
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int err;
+
+ list_for_each_entry(fp, &subs->fmt_list, list)
+ if (fp->chmap)
+ goto ok;
+ /* no chmap is found */
+ return 0;
+
+ ok:
+ err = snd_pcm_add_chmap_ctls(pcm, stream, NULL, 0, 0, &chmap);
+ if (err < 0)
+ return err;
+
+ /* override handlers */
+ chmap->private_data = subs;
+ kctl = chmap->kctl;
+ kctl->info = usb_chmap_ctl_info;
+ kctl->get = usb_chmap_ctl_get;
+ kctl->tlv.c = usb_chmap_ctl_tlv;
+
+ return 0;
+}
+
+/* convert from USB ChannelConfig bits to ALSA chmap element */
+static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits,
+ int protocol)
+{
+ static unsigned int uac1_maps[] = {
+ SNDRV_CHMAP_FL, /* left front */
+ SNDRV_CHMAP_FR, /* right front */
+ SNDRV_CHMAP_FC, /* center front */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_SL, /* left surround */
+ SNDRV_CHMAP_SR, /* right surround */
+ SNDRV_CHMAP_FLC, /* left of center */
+ SNDRV_CHMAP_FRC, /* right of center */
+ SNDRV_CHMAP_RC, /* surround */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_TC, /* top */
+ 0 /* terminator */
+ };
+ static unsigned int uac2_maps[] = {
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_RL, /* back left */
+ SNDRV_CHMAP_RR, /* back right */
+ SNDRV_CHMAP_FLC, /* front left of center */
+ SNDRV_CHMAP_FRC, /* front right of center */
+ SNDRV_CHMAP_RC, /* back center */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_TFL, /* top front left */
+ SNDRV_CHMAP_TFC, /* top front center */
+ SNDRV_CHMAP_TFR, /* top front right */
+ SNDRV_CHMAP_TRL, /* top back left */
+ SNDRV_CHMAP_TRC, /* top back center */
+ SNDRV_CHMAP_TRR, /* top back right */
+ SNDRV_CHMAP_TFLC, /* top front left of center */
+ SNDRV_CHMAP_TFRC, /* top front right of center */
+ SNDRV_CHMAP_LLFE, /* left LFE */
+ SNDRV_CHMAP_RLFE, /* right LFE */
+ SNDRV_CHMAP_TSL, /* top side left */
+ SNDRV_CHMAP_TSR, /* top side right */
+ SNDRV_CHMAP_BC, /* bottom center */
+ SNDRV_CHMAP_RLC, /* back left of center */
+ SNDRV_CHMAP_RRC, /* back right of center */
+ 0 /* terminator */
+ };
+ struct snd_pcm_chmap_elem *chmap;
+ const unsigned int *maps;
+ int c;
+
+ if (channels > ARRAY_SIZE(chmap->map))
+ return NULL;
+
+ chmap = kzalloc(sizeof(*chmap), GFP_KERNEL);
+ if (!chmap)
+ return NULL;
+
+ maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps;
+ chmap->channels = channels;
+ c = 0;
+
+ if (bits) {
+ for (; bits && *maps; maps++, bits >>= 1)
+ if (bits & 1)
+ chmap->map[c++] = *maps;
+ } else {
+ /* If we're missing wChannelConfig, then guess something
+ to make sure the channel map is not skipped entirely */
+ if (channels == 1)
+ chmap->map[c++] = SNDRV_CHMAP_MONO;
+ else
+ for (; c < channels && *maps; maps++)
+ chmap->map[c++] = *maps;
+ }
+
+ for (; c < channels; c++)
+ chmap->map[c] = SNDRV_CHMAP_UNKNOWN;
+
+ return chmap;
+}
+
+/*
+ * add this endpoint to the chip instance.
+ * if a stream with the same endpoint already exists, append to it.
+ * if not, create a new pcm stream.
+ */
+int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
+ int stream,
+ struct audioformat *fp)
+{
+ struct snd_usb_stream *as;
+ struct snd_usb_substream *subs;
+ struct snd_pcm *pcm;
+ int err;
+
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ if (as->fmt_type != fp->fmt_type)
+ continue;
+ subs = &as->substream[stream];
+ if (subs->ep_num == fp->endpoint) {
+ list_add_tail(&fp->list, &subs->fmt_list);
+ subs->num_formats++;
+ subs->formats |= fp->formats;
+ return 0;
+ }
+ }
+ /* look for an empty stream */
+ list_for_each_entry(as, &chip->pcm_list, list) {
+ if (as->fmt_type != fp->fmt_type)
+ continue;
+ subs = &as->substream[stream];
+ if (subs->ep_num)
+ continue;
+ err = snd_pcm_new_stream(as->pcm, stream, 1);
+ if (err < 0)
+ return err;
+ snd_usb_init_substream(as, stream, fp);
+ return add_chmap(as->pcm, stream, subs);
+ }
+
+ /* create a new pcm */
+ as = kzalloc(sizeof(*as), GFP_KERNEL);
+ if (!as)
+ return -ENOMEM;
+ as->pcm_index = chip->pcm_devs;
+ as->chip = chip;
+ as->fmt_type = fp->fmt_type;
+ err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
+ stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
+ stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
+ &pcm);
+ if (err < 0) {
+ kfree(as);
+ return err;
+ }
+ as->pcm = pcm;
+ pcm->private_data = as;
+ pcm->private_free = snd_usb_audio_pcm_free;
+ pcm->info_flags = 0;
+ if (chip->pcm_devs > 0)
+ sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+ else
+ strcpy(pcm->name, "USB Audio");
+
+ snd_usb_init_substream(as, stream, fp);
+
+ list_add(&as->list, &chip->pcm_list);
+ chip->pcm_devs++;
+
+ snd_usb_proc_pcm_format_add(as);
+
+ return add_chmap(pcm, stream, &as->substream[stream]);
+}
+
+static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
+ struct usb_host_interface *alts,
+ int protocol, int iface_no)
+{
+ /* parsed with a v1 header here. that's ok as we only look at the
+ * header first which is the same for both versions */
+ struct uac_iso_endpoint_descriptor *csep;
+ struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+ int attributes = 0;
+
+ csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
+
+ /* Creamware Noah has this descriptor after the 2nd endpoint */
+ if (!csep && altsd->bNumEndpoints >= 2)
+ csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
+
+ /*
+ * If we can't locate the USB_DT_CS_ENDPOINT descriptor in the extra
+ * bytes after the first endpoint, go search the entire interface.
+ * Some devices have it directly *before* the standard endpoint.
+ */
+ if (!csep)
+ csep = snd_usb_find_desc(alts->extra, alts->extralen, NULL, USB_DT_CS_ENDPOINT);
+
+ if (!csep || csep->bLength < 7 ||
+ csep->bDescriptorSubtype != UAC_EP_GENERAL) {
+ usb_audio_warn(chip,
+ "%u:%d : no or invalid class specific endpoint descriptor\n",
+ iface_no, altsd->bAlternateSetting);
+ return 0;
+ }
+
+ if (protocol == UAC_VERSION_1) {
+ attributes = csep->bmAttributes;
+ } else {
+ struct uac2_iso_endpoint_descriptor *csep2 =
+ (struct uac2_iso_endpoint_descriptor *) csep;
+
+ attributes = csep->bmAttributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+ /* emulate the endpoint attributes of a v1 device */
+ if (csep2->bmControls & UAC2_CONTROL_PITCH)
+ attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
+ }
+
+ return attributes;
+}
+
+/* find an input terminal descriptor (either UAC1 or UAC2) with the given
+ * terminal id
+ */
+static void *
+snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+ int terminal_id)
+{
+ struct uac2_input_terminal_descriptor *term = NULL;
+
+ while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+ ctrl_iface->extralen,
+ term, UAC_INPUT_TERMINAL))) {
+ if (term->bTerminalID == terminal_id)
+ return term;
+ }
+
+ return NULL;
+}
+
+static struct uac2_output_terminal_descriptor *
+ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
+ int terminal_id)
+{
+ struct uac2_output_terminal_descriptor *term = NULL;
+
+ while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
+ ctrl_iface->extralen,
+ term, UAC_OUTPUT_TERMINAL))) {
+ if (term->bTerminalID == terminal_id)
+ return term;
+ }
+
+ return NULL;
+}
+
+int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
+{
+ struct usb_device *dev;
+ struct usb_interface *iface;
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ int i, altno, err, stream;
+ unsigned int format = 0, num_channels = 0;
+ struct audioformat *fp = NULL;
+ int num, protocol, clock = 0;
+ struct uac_format_type_i_continuous_descriptor *fmt;
+ unsigned int chconfig;
+
+ dev = chip->dev;
+
+ /* parse the interface's altsettings */
+ iface = usb_ifnum_to_if(dev, iface_no);
+
+ num = iface->num_altsetting;
+
+ /*
+ * Dallas DS4201 workaround: It presents 5 altsettings, but the last
+ * one misses syncpipe, and does not produce any sound.
+ */
+ if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+ num = 4;
+
+ for (i = 0; i < num; i++) {
+ alts = &iface->altsetting[i];
+ altsd = get_iface_desc(alts);
+ protocol = altsd->bInterfaceProtocol;
+ /* skip invalid one */
+ if (((altsd->bInterfaceClass != USB_CLASS_AUDIO ||
+ (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
+ altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) &&
+ altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ altsd->bNumEndpoints < 1 ||
+ le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
+ continue;
+ /* must be isochronous */
+ if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+ USB_ENDPOINT_XFER_ISOC)
+ continue;
+ /* check direction */
+ stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
+ SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ altno = altsd->bAlternateSetting;
+
+ if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
+ continue;
+
+ /*
+ * Roland audio streaming interfaces are marked with protocols
+ * 0/1/2, but are UAC 1 compatible.
+ */
+ if (USB_ID_VENDOR(chip->usb_id) == 0x0582 &&
+ altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+ protocol <= 2)
+ protocol = UAC_VERSION_1;
+
+ chconfig = 0;
+ /* get audio formats */
+ switch (protocol) {
+ default:
+ dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
+ iface_no, altno, protocol);
+ protocol = UAC_VERSION_1;
+ /* fall through */
+
+ case UAC_VERSION_1: {
+ struct uac1_as_header_descriptor *as =
+ snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ struct uac_input_terminal_descriptor *iterm;
+
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ continue;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ continue;
+ }
+
+ format = le16_to_cpu(as->wFormatTag); /* remember the format value */
+
+ iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (iterm) {
+ num_channels = iterm->bNrChannels;
+ chconfig = le16_to_cpu(iterm->wChannelConfig);
+ }
+
+ break;
+ }
+
+ case UAC_VERSION_2: {
+ struct uac2_input_terminal_descriptor *input_term;
+ struct uac2_output_terminal_descriptor *output_term;
+ struct uac2_as_header_descriptor *as =
+ snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+
+ if (!as) {
+ dev_err(&dev->dev,
+ "%u:%d : UAC_AS_GENERAL descriptor not found\n",
+ iface_no, altno);
+ continue;
+ }
+
+ if (as->bLength < sizeof(*as)) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_AS_GENERAL desc\n",
+ iface_no, altno);
+ continue;
+ }
+
+ num_channels = as->bNrChannels;
+ format = le32_to_cpu(as->bmFormats);
+ chconfig = le32_to_cpu(as->bmChannelConfig);
+
+ /* lookup the terminal associated to this interface
+ * to extract the clock */
+ input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (input_term) {
+ clock = input_term->bCSourceID;
+ if (!chconfig && (num_channels == input_term->bNrChannels))
+ chconfig = le32_to_cpu(input_term->bmChannelConfig);
+ break;
+ }
+
+ output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
+ as->bTerminalLink);
+ if (output_term) {
+ clock = output_term->bCSourceID;
+ break;
+ }
+
+ dev_err(&dev->dev,
+ "%u:%d : bogus bTerminalLink %d\n",
+ iface_no, altno, as->bTerminalLink);
+ continue;
+ }
+ }
+
+ /* get format type */
+ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
+ if (!fmt) {
+ dev_err(&dev->dev,
+ "%u:%d : no UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
+ if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
+ ((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
+ dev_err(&dev->dev,
+ "%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+ iface_no, altno);
+ continue;
+ }
+
+ /*
+ * Blue Microphones workaround: The last altsetting is identical
+ * with the previous one, except for a larger packet size, but
+ * is actually a mislabeled two-channel setting; ignore it.
+ */
+ if (fmt->bNrChannels == 1 &&
+ fmt->bSubframeSize == 2 &&
+ altno == 2 && num == 3 &&
+ fp && fp->altsetting == 1 && fp->channels == 1 &&
+ fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+ protocol == UAC_VERSION_1 &&
+ le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+ fp->maxpacksize * 2)
+ continue;
+
+ fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+ if (! fp) {
+ dev_err(&dev->dev, "cannot malloc\n");
+ return -ENOMEM;
+ }
+
+ fp->iface = iface_no;
+ fp->altsetting = altno;
+ fp->altset_idx = i;
+ fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+ fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+ fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+ fp->protocol = protocol;
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ fp->channels = num_channels;
+ if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+ fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+ * (fp->maxpacksize & 0x7ff);
+ fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
+ fp->clock = clock;
+
+ /* some quirks for attributes here */
+
+ switch (chip->usb_id) {
+ case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
+ /* Optoplay sets the sample rate attribute although
+ * it seems not supporting it in fact.
+ */
+ fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
+ break;
+ case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
+ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+ /* doesn't set the sample rate attribute, but supports it */
+ fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
+ break;
+ case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
+ case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
+ case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
+ case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
+ an older model 77d:223) */
+ /*
+ * plantronics headset and Griffin iMic have set adaptive-in
+ * although it's really not...
+ */
+ fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
+ else
+ fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
+ break;
+ }
+
+ /* ok, let's parse further... */
+ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ fp = NULL;
+ continue;
+ }
+
+ /* Create chmap */
+ if (fp->channels != num_channels)
+ chconfig = 0;
+ fp->chmap = convert_chmap(fp->channels, chconfig, protocol);
+
+ dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
+ err = snd_usb_add_audio_stream(chip, stream, fp);
+ if (err < 0) {
+ kfree(fp->rate_table);
+ kfree(fp->chmap);
+ kfree(fp);
+ return err;
+ }
+ /* try to set the interface... */
+ usb_set_interface(chip->dev, iface_no, altno);
+ snd_usb_init_pitch(chip, iface_no, alts, fp);
+ snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
+ }
+ return 0;
+}
+
diff --git a/sound/usb/stream.h b/sound/usb/stream.h
new file mode 100644
index 00000000000..c97f679fc84
--- /dev/null
+++ b/sound/usb/stream.h
@@ -0,0 +1,12 @@
+#ifndef __USBAUDIO_STREAM_H
+#define __USBAUDIO_STREAM_H
+
+int snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
+ int iface_no);
+
+int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
+ int stream,
+ struct audioformat *fp);
+
+#endif /* __USBAUDIO_STREAM_H */
+
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
deleted file mode 100644
index de607d4411a..00000000000
--- a/sound/usb/urb.c
+++ /dev/null
@@ -1,995 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/usb/audio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-
-#include "usbaudio.h"
-#include "helper.h"
-#include "card.h"
-#include "urb.h"
-#include "pcm.h"
-
-/*
- * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
- * this will overflow at approx 524 kHz
- */
-static inline unsigned get_usb_full_speed_rate(unsigned int rate)
-{
- return ((rate << 13) + 62) / 125;
-}
-
-/*
- * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
- * this will overflow at approx 4 MHz
- */
-static inline unsigned get_usb_high_speed_rate(unsigned int rate)
-{
- return ((rate << 10) + 62) / 125;
-}
-
-/*
- * unlink active urbs.
- */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
-{
- struct snd_usb_audio *chip = subs->stream->chip;
- unsigned int i;
- int async;
-
- subs->running = 0;
-
- if (!force && subs->stream->chip->shutdown) /* to be sure... */
- return -EBADFD;
-
- async = !can_sleep && chip->async_unlink;
-
- if (!async && in_interrupt())
- return 0;
-
- for (i = 0; i < subs->nurbs; i++) {
- if (test_bit(i, &subs->active_mask)) {
- if (!test_and_set_bit(i, &subs->unlink_mask)) {
- struct urb *u = subs->dataurb[i].urb;
- if (async)
- usb_unlink_urb(u);
- else
- usb_kill_urb(u);
- }
- }
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (test_bit(i+16, &subs->active_mask)) {
- if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
- struct urb *u = subs->syncurb[i].urb;
- if (async)
- usb_unlink_urb(u);
- else
- usb_kill_urb(u);
- }
- }
- }
- }
- return 0;
-}
-
-
-/*
- * release a urb data
- */
-static void release_urb_ctx(struct snd_urb_ctx *u)
-{
- if (u->urb) {
- if (u->buffer_size)
- usb_free_coherent(u->subs->dev, u->buffer_size,
- u->urb->transfer_buffer,
- u->urb->transfer_dma);
- usb_free_urb(u->urb);
- u->urb = NULL;
- }
-}
-
-/*
- * wait until all urbs are processed.
- */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
-{
- unsigned long end_time = jiffies + msecs_to_jiffies(1000);
- unsigned int i;
- int alive;
-
- do {
- alive = 0;
- for (i = 0; i < subs->nurbs; i++) {
- if (test_bit(i, &subs->active_mask))
- alive++;
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (test_bit(i + 16, &subs->active_mask))
- alive++;
- }
- }
- if (! alive)
- break;
- schedule_timeout_uninterruptible(1);
- } while (time_before(jiffies, end_time));
- if (alive)
- snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
- return 0;
-}
-
-/*
- * release a substream
- */
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
-{
- int i;
-
- /* stop urbs (to be sure) */
- deactivate_urbs(subs, force, 1);
- wait_clear_urbs(subs);
-
- for (i = 0; i < MAX_URBS; i++)
- release_urb_ctx(&subs->dataurb[i]);
- for (i = 0; i < SYNC_URBS; i++)
- release_urb_ctx(&subs->syncurb[i]);
- usb_free_coherent(subs->dev, SYNC_URBS * 4,
- subs->syncbuf, subs->sync_dma);
- subs->syncbuf = NULL;
- subs->nurbs = 0;
-}
-
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
- struct snd_urb_ctx *ctx = urb->context;
- struct snd_usb_substream *subs = ctx->subs;
- struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
- int err = 0;
-
- if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
- !subs->running || /* can be stopped during retire callback */
- (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
- (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- clear_bit(ctx->index, &subs->active_mask);
- if (err < 0) {
- snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
- }
-}
-
-
-/*
- * complete callback from sync urb
- */
-static void snd_complete_sync_urb(struct urb *urb)
-{
- struct snd_urb_ctx *ctx = urb->context;
- struct snd_usb_substream *subs = ctx->subs;
- struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
- int err = 0;
-
- if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
- !subs->running || /* can be stopped during retire callback */
- (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
- (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- clear_bit(ctx->index + 16, &subs->active_mask);
- if (err < 0) {
- snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
- }
-}
-
-
-/*
- * initialize a substream for plaback/capture
- */
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
- unsigned int period_bytes,
- unsigned int rate,
- unsigned int frame_bits)
-{
- unsigned int maxsize, i;
- int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- unsigned int urb_packs, total_packs, packs_per_ms;
- struct snd_usb_audio *chip = subs->stream->chip;
-
- /* calculate the frequency in 16.16 format */
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
- subs->freqn = get_usb_full_speed_rate(rate);
- else
- subs->freqn = get_usb_high_speed_rate(rate);
- subs->freqm = subs->freqn;
- /* calculate max. frequency */
- if (subs->maxpacksize) {
- /* whatever fits into a max. size packet */
- maxsize = subs->maxpacksize;
- subs->freqmax = (maxsize / (frame_bits >> 3))
- << (16 - subs->datainterval);
- } else {
- /* no max. packet size: just take 25% higher than nominal */
- subs->freqmax = subs->freqn + (subs->freqn >> 2);
- maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
- >> (16 - subs->datainterval);
- }
- subs->phase = 0;
-
- if (subs->fill_max)
- subs->curpacksize = subs->maxpacksize;
- else
- subs->curpacksize = maxsize;
-
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
- packs_per_ms = 8 >> subs->datainterval;
- else
- packs_per_ms = 1;
-
- if (is_playback) {
- urb_packs = max(chip->nrpacks, 1);
- urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
- } else
- urb_packs = 1;
- urb_packs *= packs_per_ms;
- if (subs->syncpipe)
- urb_packs = min(urb_packs, 1U << subs->syncinterval);
-
- /* decide how many packets to be used */
- if (is_playback) {
- unsigned int minsize, maxpacks;
- /* determine how small a packet can be */
- minsize = (subs->freqn >> (16 - subs->datainterval))
- * (frame_bits >> 3);
- /* with sync from device, assume it can be 12% lower */
- if (subs->syncpipe)
- minsize -= minsize >> 3;
- minsize = max(minsize, 1u);
- total_packs = (period_bytes + minsize - 1) / minsize;
- /* we need at least two URBs for queueing */
- if (total_packs < 2) {
- total_packs = 2;
- } else {
- /* and we don't want too long a queue either */
- maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
- total_packs = min(total_packs, maxpacks);
- }
- } else {
- while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
- urb_packs >>= 1;
- total_packs = MAX_URBS * urb_packs;
- }
- subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
- if (subs->nurbs > MAX_URBS) {
- /* too much... */
- subs->nurbs = MAX_URBS;
- total_packs = MAX_URBS * urb_packs;
- } else if (subs->nurbs < 2) {
- /* too little - we need at least two packets
- * to ensure contiguous playback/capture
- */
- subs->nurbs = 2;
- }
-
- /* allocate and initialize data urbs */
- for (i = 0; i < subs->nurbs; i++) {
- struct snd_urb_ctx *u = &subs->dataurb[i];
- u->index = i;
- u->subs = subs;
- u->packets = (i + 1) * total_packs / subs->nurbs
- - i * total_packs / subs->nurbs;
- u->buffer_size = maxsize * u->packets;
- if (subs->fmt_type == UAC_FORMAT_TYPE_II)
- u->packets++; /* for transfer delimiter */
- u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
- if (!u->urb)
- goto out_of_memory;
- u->urb->transfer_buffer =
- usb_alloc_coherent(subs->dev, u->buffer_size,
- GFP_KERNEL, &u->urb->transfer_dma);
- if (!u->urb->transfer_buffer)
- goto out_of_memory;
- u->urb->pipe = subs->datapipe;
- u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- u->urb->interval = 1 << subs->datainterval;
- u->urb->context = u;
- u->urb->complete = snd_complete_urb;
- }
-
- if (subs->syncpipe) {
- /* allocate and initialize sync urbs */
- subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
- GFP_KERNEL, &subs->sync_dma);
- if (!subs->syncbuf)
- goto out_of_memory;
- for (i = 0; i < SYNC_URBS; i++) {
- struct snd_urb_ctx *u = &subs->syncurb[i];
- u->index = i;
- u->subs = subs;
- u->packets = 1;
- u->urb = usb_alloc_urb(1, GFP_KERNEL);
- if (!u->urb)
- goto out_of_memory;
- u->urb->transfer_buffer = subs->syncbuf + i * 4;
- u->urb->transfer_dma = subs->sync_dma + i * 4;
- u->urb->transfer_buffer_length = 4;
- u->urb->pipe = subs->syncpipe;
- u->urb->transfer_flags = URB_ISO_ASAP |
- URB_NO_TRANSFER_DMA_MAP;
- u->urb->number_of_packets = 1;
- u->urb->interval = 1 << subs->syncinterval;
- u->urb->context = u;
- u->urb->complete = snd_complete_sync_urb;
- }
- }
- return 0;
-
-out_of_memory:
- snd_usb_release_substream_urbs(subs, 0);
- return -ENOMEM;
-}
-
-/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
- */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = urb->context;
-
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 3;
- urb->iso_frame_desc[0].offset = 0;
- cp[0] = subs->freqn >> 2;
- cp[1] = subs->freqn >> 10;
- cp[2] = subs->freqn >> 18;
- return 0;
-}
-
-/*
- * prepare urb for high speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
- */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = urb->context;
-
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 4;
- urb->iso_frame_desc[0].offset = 0;
- cp[0] = subs->freqn;
- cp[1] = subs->freqn >> 8;
- cp[2] = subs->freqn >> 16;
- cp[3] = subs->freqn >> 24;
- return 0;
-}
-
-/*
- * process after capture sync complete
- * - nothing to do
- */
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- return 0;
-}
-
-/*
- * prepare urb for capture data pipe
- *
- * fill the offset and length of each descriptor.
- *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly... the data is thus copied
- * later at complete callback to the global buffer.
- */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- int i, offs;
- struct snd_urb_ctx *ctx = urb->context;
-
- offs = 0;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- for (i = 0; i < ctx->packets; i++) {
- urb->iso_frame_desc[i].offset = offs;
- urb->iso_frame_desc[i].length = subs->curpacksize;
- offs += subs->curpacksize;
- }
- urb->transfer_buffer_length = offs;
- urb->number_of_packets = ctx->packets;
- return 0;
-}
-
-/*
- * process after capture complete
- *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
- */
-static int retire_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned long flags;
- unsigned char *cp;
- int i;
- unsigned int stride, frames, bytes, oldptr;
- int period_elapsed = 0;
-
- stride = runtime->frame_bits >> 3;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- if (urb->iso_frame_desc[i].status) {
- snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
- // continue;
- }
- bytes = urb->iso_frame_desc[i].actual_length;
- frames = bytes / stride;
- if (!subs->txfr_quirk)
- bytes = frames * stride;
- if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int oldbytes = bytes;
-#endif
- bytes = frames * stride;
- snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
- oldbytes, bytes);
- }
- /* update the current pointer */
- spin_lock_irqsave(&subs->lock, flags);
- oldptr = subs->hwptr_done;
- subs->hwptr_done += bytes;
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
- frames = (bytes + (oldptr % stride)) / stride;
- subs->transfer_done += frames;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- period_elapsed = 1;
- }
- spin_unlock_irqrestore(&subs->lock, flags);
- /* copy a data chunk */
- if (oldptr + bytes > runtime->buffer_size * stride) {
- unsigned int bytes1 =
- runtime->buffer_size * stride - oldptr;
- memcpy(runtime->dma_area + oldptr, cp, bytes1);
- memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
- } else {
- memcpy(runtime->dma_area + oldptr, cp, bytes);
- }
- }
- if (period_elapsed)
- snd_pcm_period_elapsed(subs->pcm_substream);
- return 0;
-}
-
-/*
- * Process after capture complete when paused. Nothing to do.
- */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- return 0;
-}
-
-
-/*
- * prepare urb for full speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- struct snd_urb_ctx *ctx = urb->context;
-
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 3;
- urb->iso_frame_desc[0].offset = 0;
- return 0;
-}
-
-/*
- * prepare urb for high speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- struct snd_urb_ctx *ctx = urb->context;
-
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->iso_frame_desc[0].length = 4;
- urb->iso_frame_desc[0].offset = 0;
- return 0;
-}
-
-/*
- * process after full speed playback sync complete
- *
- * retrieve the current 10.14 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned int f;
- unsigned long flags;
-
- if (urb->iso_frame_desc[0].status == 0 &&
- urb->iso_frame_desc[0].actual_length == 3) {
- f = combine_triple((u8*)urb->transfer_buffer) << 2;
- if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
- spin_lock_irqsave(&subs->lock, flags);
- subs->freqm = f;
- spin_unlock_irqrestore(&subs->lock, flags);
- }
- }
-
- return 0;
-}
-
-/*
- * process after high speed playback sync complete
- *
- * retrieve the current 12.13 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned int f;
- unsigned long flags;
-
- if (urb->iso_frame_desc[0].status == 0 &&
- urb->iso_frame_desc[0].actual_length == 4) {
- f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
- if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
- spin_lock_irqsave(&subs->lock, flags);
- subs->freqm = f;
- spin_unlock_irqrestore(&subs->lock, flags);
- }
- }
-
- return 0;
-}
-
-/*
- * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
- *
- * These devices return the number of samples per packet instead of the number
- * of samples per microframe.
- */
-static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned int f;
- unsigned long flags;
-
- if (urb->iso_frame_desc[0].status == 0 &&
- urb->iso_frame_desc[0].actual_length == 4) {
- f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
- f >>= subs->datainterval;
- if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
- spin_lock_irqsave(&subs->lock, flags);
- subs->freqm = f;
- spin_unlock_irqrestore(&subs->lock, flags);
- }
- }
-
- return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
- if (subs->fill_max)
- return subs->maxframesize;
- else {
- subs->phase = (subs->phase & 0xffff)
- + (subs->freqm << subs->datainterval);
- return min(subs->phase >> 16, subs->maxframesize);
- }
-}
-
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned int i, offs, counts;
- struct snd_urb_ctx *ctx = urb->context;
- int stride = runtime->frame_bits >> 3;
-
- offs = 0;
- urb->dev = ctx->subs->dev;
- for (i = 0; i < ctx->packets; ++i) {
- counts = snd_usb_audio_next_packet_size(subs);
- urb->iso_frame_desc[i].offset = offs * stride;
- urb->iso_frame_desc[i].length = counts * stride;
- offs += counts;
- }
- urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * stride;
- memset(urb->transfer_buffer,
- runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
- offs * stride);
- return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- int i, stride;
- unsigned int counts, frames, bytes;
- unsigned long flags;
- int period_elapsed = 0;
- struct snd_urb_ctx *ctx = urb->context;
-
- stride = runtime->frame_bits >> 3;
-
- frames = 0;
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
- urb->number_of_packets = 0;
- spin_lock_irqsave(&subs->lock, flags);
- for (i = 0; i < ctx->packets; i++) {
- counts = snd_usb_audio_next_packet_size(subs);
- /* set up descriptor */
- urb->iso_frame_desc[i].offset = frames * stride;
- urb->iso_frame_desc[i].length = counts * stride;
- frames += counts;
- urb->number_of_packets++;
- subs->transfer_done += counts;
- if (subs->transfer_done >= runtime->period_size) {
- subs->transfer_done -= runtime->period_size;
- period_elapsed = 1;
- if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
- if (subs->transfer_done > 0) {
- /* FIXME: fill-max mode is not
- * supported yet */
- frames -= subs->transfer_done;
- counts -= subs->transfer_done;
- urb->iso_frame_desc[i].length =
- counts * stride;
- subs->transfer_done = 0;
- }
- i++;
- if (i < ctx->packets) {
- /* add a transfer delimiter */
- urb->iso_frame_desc[i].offset =
- frames * stride;
- urb->iso_frame_desc[i].length = 0;
- urb->number_of_packets++;
- }
- break;
- }
- }
- if (period_elapsed) /* finish at the period boundary */
- break;
- }
- bytes = frames * stride;
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
- } else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
- }
- subs->hwptr_done += bytes;
- if (subs->hwptr_done >= runtime->buffer_size * stride)
- subs->hwptr_done -= runtime->buffer_size * stride;
- runtime->delay += frames;
- spin_unlock_irqrestore(&subs->lock, flags);
- urb->transfer_buffer_length = bytes;
- if (period_elapsed)
- snd_pcm_period_elapsed(subs->pcm_substream);
- return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
-{
- unsigned long flags;
- int stride = runtime->frame_bits >> 3;
- int processed = urb->transfer_buffer_length / stride;
-
- spin_lock_irqsave(&subs->lock, flags);
- if (processed > runtime->delay)
- runtime->delay = 0;
- else
- runtime->delay -= processed;
- spin_unlock_irqrestore(&subs->lock, flags);
- return 0;
-}
-
-static const char *usb_error_string(int err)
-{
- switch (err) {
- case -ENODEV:
- return "no device";
- case -ENOENT:
- return "endpoint not enabled";
- case -EPIPE:
- return "endpoint stalled";
- case -ENOSPC:
- return "not enough bandwidth";
- case -ESHUTDOWN:
- return "device disabled";
- case -EHOSTUNREACH:
- return "device suspended";
- case -EINVAL:
- case -EAGAIN:
- case -EFBIG:
- case -EMSGSIZE:
- return "internal error";
- default:
- return "unknown error";
- }
-}
-
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
- unsigned int i;
- int err;
-
- if (subs->stream->chip->shutdown)
- return -EBADFD;
-
- for (i = 0; i < subs->nurbs; i++) {
- if (snd_BUG_ON(!subs->dataurb[i].urb))
- return -EINVAL;
- if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
- snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
- goto __error;
- }
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- if (snd_BUG_ON(!subs->syncurb[i].urb))
- return -EINVAL;
- if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
- snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
- goto __error;
- }
- }
- }
-
- subs->active_mask = 0;
- subs->unlink_mask = 0;
- subs->running = 1;
- for (i = 0; i < subs->nurbs; i++) {
- err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
- if (err < 0) {
- snd_printk(KERN_ERR "cannot submit datapipe "
- "for urb %d, error %d: %s\n",
- i, err, usb_error_string(err));
- goto __error;
- }
- set_bit(i, &subs->active_mask);
- }
- if (subs->syncpipe) {
- for (i = 0; i < SYNC_URBS; i++) {
- err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
- if (err < 0) {
- snd_printk(KERN_ERR "cannot submit syncpipe "
- "for urb %d, error %d: %s\n",
- i, err, usb_error_string(err));
- goto __error;
- }
- set_bit(i + 16, &subs->active_mask);
- }
- }
- return 0;
-
- __error:
- // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
- deactivate_urbs(subs, 0, 0);
- return -EPIPE;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
- {
- .prepare = prepare_nodata_playback_urb,
- .retire = retire_playback_urb,
- .prepare_sync = prepare_playback_sync_urb,
- .retire_sync = retire_playback_sync_urb,
- },
- {
- .prepare = prepare_capture_urb,
- .retire = retire_capture_urb,
- .prepare_sync = prepare_capture_sync_urb,
- .retire_sync = retire_capture_sync_urb,
- },
-};
-
-static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
- {
- .prepare = prepare_nodata_playback_urb,
- .retire = retire_playback_urb,
- .prepare_sync = prepare_playback_sync_urb_hs,
- .retire_sync = retire_playback_sync_urb_hs,
- },
- {
- .prepare = prepare_capture_urb,
- .retire = retire_capture_urb,
- .prepare_sync = prepare_capture_sync_urb_hs,
- .retire_sync = retire_capture_sync_urb,
- },
-};
-
-/*
- * initialize the substream instance.
- */
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
- int stream, struct audioformat *fp)
-{
- struct snd_usb_substream *subs = &as->substream[stream];
-
- INIT_LIST_HEAD(&subs->fmt_list);
- spin_lock_init(&subs->lock);
-
- subs->stream = as;
- subs->direction = stream;
- subs->dev = as->chip->dev;
- subs->txfr_quirk = as->chip->txfr_quirk;
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
- subs->ops = audio_urb_ops[stream];
- } else {
- subs->ops = audio_urb_ops_high_speed[stream];
- switch (as->chip->usb_id) {
- case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
- case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
- case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
- subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
- break;
- case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
- case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
- subs->ops.prepare_sync = prepare_playback_sync_urb;
- subs->ops.retire_sync = retire_playback_sync_urb;
- break;
- }
- }
-
- snd_usb_set_pcm_ops(as->pcm, stream);
-
- list_add_tail(&fp->list, &subs->fmt_list);
- subs->formats |= fp->formats;
- subs->endpoint = fp->endpoint;
- subs->num_formats++;
- subs->fmt_type = fp->fmt_type;
-}
-
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_usb_substream *subs = substream->runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- subs->ops.prepare = prepare_playback_urb;
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- return deactivate_urbs(subs, 0, 0);
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- subs->ops.prepare = prepare_nodata_playback_urb;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_usb_substream *subs = substream->runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- subs->ops.retire = retire_capture_urb;
- return start_urbs(subs, substream->runtime);
- case SNDRV_PCM_TRIGGER_STOP:
- return deactivate_urbs(subs, 0, 0);
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- subs->ops.retire = retire_paused_capture_urb;
- return 0;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- subs->ops.retire = retire_capture_urb;
- return 0;
- }
-
- return -EINVAL;
-}
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime)
-{
- /* clear urbs (to be sure) */
- deactivate_urbs(subs, 0, 1);
- wait_clear_urbs(subs);
-
- /* for playback, submit the URBs now; otherwise, the first hwptr_done
- * updates for all URBs would happen at the same time when starting */
- if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
- subs->ops.prepare = prepare_nodata_playback_urb;
- return start_urbs(subs, runtime);
- }
-
- return 0;
-}
-
diff --git a/sound/usb/urb.h b/sound/usb/urb.h
deleted file mode 100644
index 888da38079c..00000000000
--- a/sound/usb/urb.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef __USBAUDIO_URB_H
-#define __USBAUDIO_URB_H
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
- int stream,
- struct audioformat *fp);
-
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
- unsigned int period_bytes,
- unsigned int rate,
- unsigned int frame_bits);
-
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime);
-
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
-
-#endif /* __USBAUDIO_URB_H */
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 24d3319cc34..91d0380431b 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -34,13 +34,21 @@ struct snd_usb_audio {
int index;
struct usb_device *dev;
struct snd_card *card;
+ struct usb_interface *pm_intf;
u32 usb_id;
- int shutdown;
+ struct mutex mutex;
+ struct rw_semaphore shutdown_rwsem;
+ unsigned int shutdown:1;
+ unsigned int probing:1;
+ unsigned int in_pm:1;
+ unsigned int autosuspended:1;
unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+
int num_interfaces;
int num_suspended_intf;
struct list_head pcm_list; /* list of pcm streams */
+ struct list_head ep_list; /* list of audio-related endpoints */
int pcm_devs;
struct list_head midi_list; /* list of midi interfaces */
@@ -48,12 +56,20 @@ struct snd_usb_audio {
struct list_head mixer_list; /* list of mixer interfaces */
int setup; /* from the 'device_setup' module param */
- int nrpacks; /* from the 'nrpacks' module param */
- int async_unlink; /* from the 'async_unlink' module param */
+ bool autoclock; /* from the 'autoclock' module param */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
};
+#define usb_audio_err(chip, fmt, args...) \
+ dev_err(&(chip)->dev->dev, fmt, ##args)
+#define usb_audio_warn(chip, fmt, args...) \
+ dev_warn(&(chip)->dev->dev, fmt, ##args)
+#define usb_audio_info(chip, fmt, args...) \
+ dev_info(&(chip)->dev->dev, fmt, ##args)
+#define usb_audio_dbg(chip, fmt, args...) \
+ dev_dbg(&(chip)->dev->dev, fmt, ##args)
+
/*
* Information about devices with broken descriptors
*/
@@ -65,20 +81,24 @@ struct snd_usb_audio {
enum quirk_type {
QUIRK_IGNORE_INTERFACE,
QUIRK_COMPOSITE,
+ QUIRK_AUTODETECT,
QUIRK_MIDI_STANDARD_INTERFACE,
QUIRK_MIDI_FIXED_ENDPOINT,
QUIRK_MIDI_YAMAHA,
+ QUIRK_MIDI_ROLAND,
QUIRK_MIDI_MIDIMAN,
QUIRK_MIDI_NOVATION,
- QUIRK_MIDI_FASTLANE,
+ QUIRK_MIDI_RAW_BYTES,
QUIRK_MIDI_EMAGIC,
QUIRK_MIDI_CME,
QUIRK_MIDI_AKAI,
QUIRK_MIDI_US122L,
+ QUIRK_MIDI_FTDI,
QUIRK_AUDIO_STANDARD_INTERFACE,
QUIRK_AUDIO_FIXED_ENDPOINT,
QUIRK_AUDIO_EDIROL_UAXX,
QUIRK_AUDIO_ALIGN_TRANSFER,
+ QUIRK_AUDIO_STANDARD_MIXER,
QUIRK_TYPE_COUNT
};
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index 6ef68e42138..cf5dc33f4a6 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include <sound/pcm.h>
@@ -36,7 +37,7 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
/* Enable this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
@@ -261,7 +262,9 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
}
area->vm_ops = &usb_stream_hwdep_vm_ops;
- area->vm_flags |= VM_RESERVED;
+ area->vm_flags |= VM_DONTDUMP;
+ if (!read)
+ area->vm_flags |= VM_DONTEXPAND;
area->vm_private_data = us122l;
atomic_inc(&us122l->mmap_count);
out:
@@ -273,29 +276,26 @@ static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
struct file *file, poll_table *wait)
{
struct us122l *us122l = hw->private_data;
- struct usb_stream *s = us122l->sk.s;
unsigned *polled;
unsigned int mask;
poll_wait(file, &us122l->sk.sleep, wait);
- switch (s->state) {
- case usb_stream_ready:
- if (us122l->first == file)
- polled = &s->periods_polled;
- else
- polled = &us122l->second_periods_polled;
- if (*polled != s->periods_done) {
- *polled = s->periods_done;
- mask = POLLIN | POLLOUT | POLLWRNORM;
- break;
+ mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
+ if (mutex_trylock(&us122l->mutex)) {
+ struct usb_stream *s = us122l->sk.s;
+ if (s && s->state == usb_stream_ready) {
+ if (us122l->first == file)
+ polled = &s->periods_polled;
+ else
+ polled = &us122l->second_periods_polled;
+ if (*polled != s->periods_done) {
+ *polled = s->periods_done;
+ mask = POLLIN | POLLOUT | POLLWRNORM;
+ } else
+ mask = 0;
}
- /* Fall through */
- mask = 0;
- break;
- default:
- mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
- break;
+ mutex_unlock(&us122l->mutex);
}
return mask;
}
@@ -381,6 +381,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
{
struct usb_stream_config *cfg;
struct us122l *us122l = hw->private_data;
+ struct usb_stream *s;
unsigned min_period_frames;
int err = 0;
bool high_speed;
@@ -426,18 +427,18 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
mutex_lock(&us122l->mutex);
+ s = us122l->sk.s;
if (!us122l->master)
us122l->master = file;
else if (us122l->master != file) {
- if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) {
+ if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg))) {
err = -EIO;
goto unlock;
}
us122l->slave = file;
}
- if (!us122l->sk.s ||
- memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) ||
- us122l->sk.s->state == usb_stream_xrun) {
+ if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg)) ||
+ s->state == usb_stream_xrun) {
us122l_stop(us122l);
if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
err = -EIO;
@@ -448,6 +449,7 @@ unlock:
mutex_unlock(&us122l->mutex);
free:
kfree(cfg);
+ wake_up_all(&us122l->sk.sleep);
return err;
}
@@ -533,7 +535,9 @@ static void snd_us122l_free(struct snd_card *card)
snd_us122l_card_used[index] = 0;
}
-static int usx2y_create_card(struct usb_device *device, struct snd_card **cardp)
+static int usx2y_create_card(struct usb_device *device,
+ struct usb_interface *intf,
+ struct snd_card **cardp)
{
int dev;
struct snd_card *card;
@@ -544,8 +548,8 @@ static int usx2y_create_card(struct usb_device *device, struct snd_card **cardp)
break;
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct us122l), &card);
+ err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct us122l), &card);
if (err < 0)
return err;
snd_us122l_card_used[US122L(card)->card_index = dev] = 1;
@@ -576,11 +580,10 @@ static int us122l_usb_probe(struct usb_interface *intf,
struct snd_card *card;
int err;
- err = usx2y_create_card(device, &card);
+ err = usx2y_create_card(device, intf, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, &intf->dev);
if (!us122l_create_card(card)) {
snd_card_free(card);
return -EINVAL;
@@ -772,16 +775,4 @@ static struct usb_driver snd_us122l_usb_driver = {
.supports_autosuspend = 1
};
-
-static int __init snd_us122l_module_init(void)
-{
- return usb_register(&snd_us122l_usb_driver);
-}
-
-static void __exit snd_us122l_module_exit(void)
-{
- usb_deregister(&snd_us122l_usb_driver);
-}
-
-module_init(snd_us122l_module_init)
-module_exit(snd_us122l_module_exit)
+module_usb_driver(snd_us122l_usb_driver);
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 04aafb43a13..0b34dbc8f30 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -82,7 +82,7 @@ static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct v
us428->us428ctls_sharedmem->CtlSnapShotLast = -2;
}
area->vm_ops = &us428ctls_vm_ops;
- area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
area->vm_private_data = hw->private_data;
return 0;
}
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index c400ade3ff0..bf618e1500a 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -69,7 +69,6 @@ static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize,
++u, transfer += transfer_length) {
struct urb *urb = urbs[u];
struct usb_iso_packet_descriptor *desc;
- urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = transfer;
urb->dev = dev;
urb->pipe = pipe;
@@ -674,7 +673,7 @@ dotry:
inurb->transfer_buffer_length =
inurb->number_of_packets *
inurb->iso_frame_desc[0].length;
- preempt_disable();
+
if (u == 0) {
int now;
struct usb_device *dev = inurb->dev;
@@ -686,19 +685,17 @@ dotry:
}
err = usb_submit_urb(inurb, GFP_ATOMIC);
if (err < 0) {
- preempt_enable();
snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])"
" returned %i\n", u, err);
return err;
}
err = usb_submit_urb(outurb, GFP_ATOMIC);
if (err < 0) {
- preempt_enable();
snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])"
" returned %i\n", u, err);
return err;
}
- preempt_enable();
+
if (inurb->start_frame != outurb->start_frame) {
snd_printd(KERN_DEBUG
"u[%i] start_frames differ in:%u out:%u\n",
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index cbd37f2c76d..91e0e2a4808 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -150,11 +150,11 @@
MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}");
+MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604),"NAME_ALLCAPS"(0x8001)(0x8005)(0x8007)}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
@@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S)
{
int i;
for (i = 0; i < URBS_AsyncSeq; ++i) {
- if (S[i].urb) {
- usb_kill_urb(S->urb[i]);
- usb_free_urb(S->urb[i]);
- S->urb[i] = NULL;
- }
+ usb_kill_urb(S->urb[i]);
+ usb_free_urb(S->urb[i]);
+ S->urb[i] = NULL;
}
kfree(S->buffer);
}
@@ -334,7 +332,9 @@ static struct usb_device_id snd_usX2Y_usb_id_table[] = {
{ /* terminator */ }
};
-static int usX2Y_create_card(struct usb_device *device, struct snd_card **cardp)
+static int usX2Y_create_card(struct usb_device *device,
+ struct usb_interface *intf,
+ struct snd_card **cardp)
{
int dev;
struct snd_card * card;
@@ -345,15 +345,15 @@ static int usX2Y_create_card(struct usb_device *device, struct snd_card **cardp)
break;
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct usX2Ydev), &card);
+ err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct usX2Ydev), &card);
if (err < 0)
return err;
snd_usX2Y_card_used[usX2Y(card)->card_index = dev] = 1;
card->private_free = snd_usX2Y_card_private_free;
usX2Y(card)->dev = device;
init_waitqueue_head(&usX2Y(card)->prepare_wait_queue);
- mutex_init(&usX2Y(card)->prepare_mutex);
+ mutex_init(&usX2Y(card)->pcm_mutex);
INIT_LIST_HEAD(&usX2Y(card)->midi_list);
strcpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
@@ -384,10 +384,9 @@ static int usX2Y_usb_probe(struct usb_device *device,
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428))
return -EINVAL;
- err = usX2Y_create_card(device, &card);
+ err = usX2Y_create_card(device, intf, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, &intf->dev);
if ((err = usX2Y_hwdep_new(card, device)) < 0 ||
(err = snd_card_register(card)) < 0) {
snd_card_free(card);
@@ -459,15 +458,4 @@ static void usX2Y_usb_disconnect(struct usb_device *device, void* ptr)
}
}
-static int __init snd_usX2Y_module_init(void)
-{
- return usb_register(&snd_usX2Y_usb_driver);
-}
-
-static void __exit snd_usX2Y_module_exit(void)
-{
- usb_deregister(&snd_usX2Y_usb_driver);
-}
-
-module_init(snd_usX2Y_module_init)
-module_exit(snd_usX2Y_module_exit)
+module_usb_driver(snd_usX2Y_usb_driver);
diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h
index e43c0a86441..6ae6b080693 100644
--- a/sound/usb/usx2y/usbusx2y.h
+++ b/sound/usb/usx2y/usbusx2y.h
@@ -36,7 +36,7 @@ struct usX2Ydev {
unsigned int rate,
format;
int chip_status;
- struct mutex prepare_mutex;
+ struct mutex pcm_mutex;
struct us428ctls_sharedmem *us428ctls_sharedmem;
int wait_iso_frame;
wait_queue_head_t us428ctls_wait_queue_head;
diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
index 5d37d1ccf81..a63330dd140 100644
--- a/sound/usb/usx2y/usbusx2yaudio.c
+++ b/sound/usb/usx2y/usbusx2yaudio.c
@@ -34,6 +34,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
@@ -79,7 +80,7 @@ static int usX2Y_urb_capt_retire(struct snd_usX2Y_substream *subs)
cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
snd_printk(KERN_ERR "active frame status %i. "
- "Most propably some hardware problem.\n",
+ "Most probably some hardware problem.\n",
urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status;
}
@@ -272,7 +273,11 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y)
struct snd_usX2Y_substream *subs = usX2Y->subs[s];
if (subs) {
if (atomic_read(&subs->state) >= state_PRERUNNING) {
+ unsigned long flags;
+
+ snd_pcm_stream_lock_irqsave(subs->pcm_substream, flags);
snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(subs->pcm_substream, flags);
}
for (u = 0; u < NRURBS; u++) {
struct urb *urb = subs->urb[u];
@@ -294,19 +299,6 @@ static void usX2Y_error_urb_status(struct usX2Ydev *usX2Y,
usX2Y_clients_stop(usX2Y);
}
-static void usX2Y_error_sequence(struct usX2Ydev *usX2Y,
- struct snd_usX2Y_substream *subs, struct urb *urb)
-{
- snd_printk(KERN_ERR
-"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"
-"Most propably some urb of usb-frame %i is still missing.\n"
-"Cause could be too long delays in usb-hcd interrupt handling.\n",
- usb_get_current_frame_number(usX2Y->dev),
- subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
- usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);
- usX2Y_clients_stop(usX2Y);
-}
-
static void i_usX2Y_urb_complete(struct urb *urb)
{
struct snd_usX2Y_substream *subs = urb->context;
@@ -323,12 +315,9 @@ static void i_usX2Y_urb_complete(struct urb *urb)
usX2Y_error_urb_status(usX2Y, subs, urb);
return;
}
- if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
- subs->completed_urb = urb;
- else {
- usX2Y_error_sequence(usX2Y, subs, urb);
- return;
- }
+
+ subs->completed_urb = urb;
+
{
struct snd_usX2Y_substream *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE],
*playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
@@ -502,7 +491,6 @@ static int usX2Y_urbs_start(struct snd_usX2Y_substream *subs)
if (0 == i)
atomic_set(&subs->state, state_STARTING3);
urb->dev = usX2Y->dev;
- urb->transfer_flags = URB_ISO_ASAP;
for (pack = 0; pack < nr_of_packs(); pack++) {
urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack;
urb->iso_frame_desc[pack].length = subs->maxpacksize;
@@ -695,9 +683,6 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate)
((char*)(usbdata + i))[1] = ra[i].c2;
usb_fill_bulk_urb(us->urb[i], usX2Y->dev, usb_sndbulkpipe(usX2Y->dev, 4),
usbdata + i, 2, i_usX2Y_04Int, usX2Y);
-#ifdef OLD_USB
- us->urb[i]->transfer_flags = USB_QUEUE_BULK;
-#endif
}
us->submitted = 0;
us->len = NOOF_SETRATE_URBS;
@@ -767,36 +752,44 @@ static int snd_usX2Y_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int rate = params_rate(hw_params);
snd_pcm_format_t format = params_format(hw_params);
struct snd_card *card = substream->pstr->pcm->card;
- struct list_head *list;
+ struct usX2Ydev *dev = usX2Y(card);
+ int i;
+ mutex_lock(&usX2Y(card)->pcm_mutex);
snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params);
- // all pcm substreams off one usX2Y have to operate at the same rate & format
- list_for_each(list, &card->devices) {
- struct snd_device *dev;
- struct snd_pcm *pcm;
- int s;
- dev = snd_device(list);
- if (dev->type != SNDRV_DEV_PCM)
+ /* all pcm substreams off one usX2Y have to operate at the same
+ * rate & format
+ */
+ for (i = 0; i < dev->pcm_devs * 2; i++) {
+ struct snd_usX2Y_substream *subs = dev->subs[i];
+ struct snd_pcm_substream *test_substream;
+
+ if (!subs)
+ continue;
+ test_substream = subs->pcm_substream;
+ if (!test_substream || test_substream == substream ||
+ !test_substream->runtime)
continue;
- pcm = dev->device_data;
- for (s = 0; s < 2; ++s) {
- struct snd_pcm_substream *test_substream;
- test_substream = pcm->streams[s].substream;
- if (test_substream && test_substream != substream &&
- test_substream->runtime &&
- ((test_substream->runtime->format &&
- test_substream->runtime->format != format) ||
- (test_substream->runtime->rate &&
- test_substream->runtime->rate != rate)))
- return -EINVAL;
+ if ((test_substream->runtime->format &&
+ test_substream->runtime->format != format) ||
+ (test_substream->runtime->rate &&
+ test_substream->runtime->rate != rate)) {
+ err = -EINVAL;
+ goto error;
}
}
- if (0 > (err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)))) {
+
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0) {
snd_printk(KERN_ERR "snd_pcm_lib_malloc_pages(%p, %i) returned %i\n",
substream, params_buffer_bytes(hw_params), err);
- return err;
+ goto error;
}
- return 0;
+
+ error:
+ mutex_unlock(&usX2Y(card)->pcm_mutex);
+ return err;
}
/*
@@ -806,7 +799,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usX2Y_substream *subs = runtime->private_data;
- mutex_lock(&subs->usX2Y->prepare_mutex);
+ mutex_lock(&subs->usX2Y->pcm_mutex);
snd_printdd("snd_usX2Y_hw_free(%p)\n", substream);
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
@@ -827,7 +820,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream)
usX2Y_urbs_release(subs);
}
}
- mutex_unlock(&subs->usX2Y->prepare_mutex);
+ mutex_unlock(&subs->usX2Y->pcm_mutex);
return snd_pcm_lib_free_pages(substream);
}
/*
@@ -844,7 +837,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream)
int err = 0;
snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
- mutex_lock(&usX2Y->prepare_mutex);
+ mutex_lock(&usX2Y->pcm_mutex);
usX2Y_subs_prepare(subs);
// Start hardware streams
// SyncStream first....
@@ -864,7 +857,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream)
err = usX2Y_urbs_start(subs);
up_prepare_mutex:
- mutex_unlock(&usX2Y->prepare_mutex);
+ mutex_unlock(&usX2Y->pcm_mutex);
return err;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 2a528e56afd..90766a92e7f 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -20,7 +20,7 @@
at standard samplerates,
what led to this part of the usx2y module:
It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
- The pair uses a hardware dependant alsa-device for mmaped pcm transport.
+ The pair uses a hardware dependent alsa-device for mmaped pcm transport.
Advantage achieved:
The usb_hc moves pcm data from/into memory via DMA.
That memory is mmaped by jack's usx2y driver.
@@ -36,9 +36,9 @@
plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
cost of easier triggered i.e. aeolus xruns (128 or 256frames,
2periods works but is useless cause of crackling).
-
+
This is a first "proof of concept" implementation.
- Later, funcionalities should migrate to more apropriate places:
+ Later, functionalities should migrate to more appropriate places:
Userland:
- The jackd could mmap its float-pcm buffers directly from alsa-lib.
- alsa-lib could provide power of 2 period sized shaping combined with int/float
@@ -54,7 +54,7 @@
#include <linux/gfp.h>
#include "usbusx2yaudio.c"
-#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
+#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
#include <sound/hwdep.h>
@@ -74,7 +74,7 @@ static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
}
for (i = 0; i < nr_of_packs(); i++) {
if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
- snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
+ snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
return urb->iso_frame_desc[i].status;
}
lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
@@ -244,13 +244,8 @@ static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
usX2Y_error_urb_status(usX2Y, subs, urb);
return;
}
- if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
- subs->completed_urb = urb;
- else {
- usX2Y_error_sequence(usX2Y, subs, urb);
- return;
- }
+ subs->completed_urb = urb;
capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
@@ -363,7 +358,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usX2Y_substream *subs = runtime->private_data,
*cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
- mutex_lock(&subs->usX2Y->prepare_mutex);
+ mutex_lock(&subs->usX2Y->pcm_mutex);
snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
@@ -392,7 +387,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
usX2Y_usbpcm_urbs_release(cap_subs2);
}
}
- mutex_unlock(&subs->usX2Y->prepare_mutex);
+ mutex_unlock(&subs->usX2Y->pcm_mutex);
return snd_pcm_lib_free_pages(substream);
}
@@ -443,7 +438,6 @@ static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
if (0 == u)
atomic_set(&subs->state, state_STARTING3);
urb->dev = usX2Y->dev;
- urb->transfer_flags = URB_ISO_ASAP;
for (pack = 0; pack < nr_of_packs(); pack++) {
urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
urb->iso_frame_desc[pack].length = subs->maxpacksize;
@@ -499,7 +493,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
}
- mutex_lock(&usX2Y->prepare_mutex);
+ mutex_lock(&usX2Y->pcm_mutex);
usX2Y_subs_prepare(subs);
// Start hardware streams
// SyncStream first....
@@ -540,7 +534,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
up_prepare_mutex:
- mutex_unlock(&usX2Y->prepare_mutex);
+ mutex_unlock(&usX2Y->pcm_mutex);
return err;
}
@@ -606,59 +600,30 @@ static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
};
-static int usX2Y_pcms_lock_check(struct snd_card *card)
+static int usX2Y_pcms_busy_check(struct snd_card *card)
{
- struct list_head *list;
- struct snd_device *dev;
- struct snd_pcm *pcm;
- int err = 0;
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (dev->type != SNDRV_DEV_PCM)
- continue;
- pcm = dev->device_data;
- mutex_lock(&pcm->open_mutex);
- }
- list_for_each(list, &card->devices) {
- int s;
- dev = snd_device(list);
- if (dev->type != SNDRV_DEV_PCM)
- continue;
- pcm = dev->device_data;
- for (s = 0; s < 2; ++s) {
- struct snd_pcm_substream *substream;
- substream = pcm->streams[s].substream;
- if (substream && SUBSTREAM_BUSY(substream))
- err = -EBUSY;
- }
- }
- return err;
-}
-
+ struct usX2Ydev *dev = usX2Y(card);
+ int i;
-static void usX2Y_pcms_unlock(struct snd_card *card)
-{
- struct list_head *list;
- struct snd_device *dev;
- struct snd_pcm *pcm;
- list_for_each(list, &card->devices) {
- dev = snd_device(list);
- if (dev->type != SNDRV_DEV_PCM)
- continue;
- pcm = dev->device_data;
- mutex_unlock(&pcm->open_mutex);
+ for (i = 0; i < dev->pcm_devs * 2; i++) {
+ struct snd_usX2Y_substream *subs = dev->subs[i];
+ if (subs && subs->pcm_substream &&
+ SUBSTREAM_BUSY(subs->pcm_substream))
+ return -EBUSY;
}
+ return 0;
}
-
static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
{
- // we need to be the first
struct snd_card *card = hw->card;
- int err = usX2Y_pcms_lock_check(card);
- if (0 == err)
+ int err;
+
+ mutex_lock(&usX2Y(card)->pcm_mutex);
+ err = usX2Y_pcms_busy_check(card);
+ if (!err)
usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
- usX2Y_pcms_unlock(card);
+ mutex_unlock(&usX2Y(card)->pcm_mutex);
return err;
}
@@ -666,10 +631,13 @@ static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
{
struct snd_card *card = hw->card;
- int err = usX2Y_pcms_lock_check(card);
- if (0 == err)
+ int err;
+
+ mutex_lock(&usX2Y(card)->pcm_mutex);
+ err = usX2Y_pcms_busy_check(card);
+ if (!err)
usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
- usX2Y_pcms_unlock(card);
+ mutex_unlock(&usX2Y(card)->pcm_mutex);
return err;
}
@@ -723,7 +691,7 @@ static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, st
return -ENODEV;
}
area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
- area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
area->vm_private_data = hw->private_data;
return 0;
}