summaryrefslogtreecommitdiff
path: root/sound/firewire
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-03-25 03:53:42 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-03-25 03:53:42 -0300
commit03dd4cb26d967f9588437b0fc9cc0e8353322bb7 (patch)
treefa581f6dc1c0596391690d1f67eceef3af8246dc /sound/firewire
parentd4e493caf788ef44982e131ff9c786546904d934 (diff)
Linux-libre 4.5-gnu
Diffstat (limited to 'sound/firewire')
-rw-r--r--sound/firewire/Kconfig12
-rw-r--r--sound/firewire/Makefile2
-rw-r--r--sound/firewire/dice/dice-transaction.c123
-rw-r--r--sound/firewire/dice/dice.c227
-rw-r--r--sound/firewire/dice/dice.h3
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c2
-rw-r--r--sound/firewire/fireworks/fireworks.h4
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c16
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c28
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c32
-rw-r--r--sound/firewire/oxfw/Makefile4
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c406
-rw-r--r--sound/firewire/oxfw/oxfw-spkr.c (renamed from sound/firewire/oxfw/oxfw-control.c)142
-rw-r--r--sound/firewire/oxfw/oxfw.c110
-rw-r--r--sound/firewire/oxfw/oxfw.h23
-rw-r--r--sound/firewire/scs1x.c530
-rw-r--r--sound/firewire/tascam/tascam-transaction.c6
-rw-r--r--sound/firewire/tascam/tascam.c12
-rw-r--r--sound/firewire/tascam/tascam.h4
19 files changed, 847 insertions, 839 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index e92a6d949..2a779c2f6 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -39,6 +39,7 @@ config SND_OXFW
* Mackie(Loud) d.2 pro/d.4 pro
* Mackie(Loud) U.420/U.420d
* TASCAM FireOne
+ * Stanton Controllers & Systems 1 Deck/Mixer
To compile this driver as a module, choose M here: the module
will be called snd-oxfw.
@@ -53,17 +54,6 @@ config SND_ISIGHT
To compile this driver as a module, choose M here: the module
will be called snd-isight.
-config SND_SCS1X
- tristate "Stanton Control System 1 MIDI"
- select SND_FIREWIRE_LIB
- help
- Say Y here to include support for the MIDI ports of the Stanton
- SCS.1d/SCS.1m DJ controllers. (SCS.1m audio is still handled
- by FFADO.)
-
- To compile this driver as a module, choose M here: the module
- will be called snd-scs1x.
-
config SND_FIREWORKS
tristate "Echo Fireworks board module support"
select SND_FIREWIRE_LIB
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index f5fb62551..003c09029 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,13 +1,11 @@
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
-snd-scs1x-objs := scs1x.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_DICE) += dice/
obj-$(CONFIG_SND_OXFW) += oxfw/
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
-obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
index aee746187..a4ff4e0bc 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -9,7 +9,7 @@
#include "dice.h"
-#define NOTIFICATION_TIMEOUT_MS 100
+#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
u64 offset)
@@ -65,16 +65,15 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
static int set_clock_info(struct snd_dice *dice,
unsigned int rate, unsigned int source)
{
- unsigned int retries = 3;
unsigned int i;
__be32 info;
u32 mask;
u32 clock;
int err;
-retry:
+
err = get_clock_info(dice, &info);
if (err < 0)
- goto end;
+ return err;
clock = be32_to_cpu(info);
if (source != UINT_MAX) {
@@ -87,10 +86,8 @@ retry:
if (snd_dice_rates[i] == rate)
break;
}
- if (i == ARRAY_SIZE(snd_dice_rates)) {
- err = -EINVAL;
- goto end;
- }
+ if (i == ARRAY_SIZE(snd_dice_rates))
+ return -EINVAL;
mask = CLOCK_RATE_MASK;
clock &= ~mask;
@@ -104,25 +101,13 @@ retry:
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
&info, 4);
if (err < 0)
- goto end;
+ return err;
- /* Timeout means it's invalid request, probably bus reset occurred. */
if (wait_for_completion_timeout(&dice->clock_accepted,
- msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
- if (retries-- == 0) {
- err = -ETIMEDOUT;
- goto end;
- }
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
+ return -ETIMEDOUT;
- err = snd_dice_transaction_reinit(dice);
- if (err < 0)
- goto end;
-
- msleep(500); /* arbitrary */
- goto retry;
- }
-end:
- return err;
+ return 0;
}
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
@@ -331,39 +316,60 @@ int snd_dice_transaction_reinit(struct snd_dice *dice)
return register_notification_address(dice, false);
}
-int snd_dice_transaction_init(struct snd_dice *dice)
+static int get_subaddrs(struct snd_dice *dice)
{
- struct fw_address_handler *handler = &dice->notification_handler;
+ static const int min_values[10] = {
+ 10, 0x64 / 4,
+ 10, 0x18 / 4,
+ 10, 0x18 / 4,
+ 0, 0,
+ 0, 0,
+ };
__be32 *pointers;
+ __be32 version;
+ u32 data;
+ unsigned int i;
int err;
- /* Use the same way which dice_interface_check() does. */
- pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
+ pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
+ GFP_KERNEL);
if (pointers == NULL)
return -ENOMEM;
- /* Get offsets for sub-addresses */
+ /*
+ * Check that the sub address spaces exist and are located inside the
+ * private address space. The minimum values are chosen so that all
+ * minimally required registers are included.
+ */
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE,
- pointers, sizeof(__be32) * 10, 0);
+ DICE_PRIVATE_SPACE, pointers,
+ sizeof(__be32) * ARRAY_SIZE(min_values), 0);
if (err < 0)
goto end;
- /* Allocation callback in address space over host controller */
- handler->length = 4;
- handler->address_callback = dice_notification;
- handler->callback_data = dice;
- err = fw_core_add_address_handler(handler, &fw_high_memory_region);
- if (err < 0) {
- handler->callback_data = NULL;
- goto end;
+ for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
+ data = be32_to_cpu(pointers[i]);
+ if (data < min_values[i] || data >= 0x40000) {
+ err = -ENODEV;
+ goto end;
+ }
}
- /* Register the address space */
- err = register_notification_address(dice, true);
- if (err < 0) {
- fw_core_remove_address_handler(handler);
- handler->callback_data = NULL;
+ /*
+ * Check that the implemented DICE driver specification major version
+ * number matches.
+ */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+ DICE_PRIVATE_SPACE +
+ be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+ &version, sizeof(version), 0);
+ if (err < 0)
+ goto end;
+
+ if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
+ dev_err(&dice->unit->device,
+ "unknown DICE version: 0x%08x\n", be32_to_cpu(version));
+ err = -ENODEV;
goto end;
}
@@ -380,3 +386,32 @@ end:
kfree(pointers);
return err;
}
+
+int snd_dice_transaction_init(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+ int err;
+
+ err = get_subaddrs(dice);
+ if (err < 0)
+ return err;
+
+ /* Allocation callback in address space over host controller */
+ handler->length = 4;
+ handler->address_callback = dice_notification;
+ handler->callback_data = dice;
+ err = fw_core_add_address_handler(handler, &fw_high_memory_region);
+ if (err < 0) {
+ handler->callback_data = NULL;
+ return err;
+ }
+
+ /* Register the address space */
+ err = register_notification_address(dice, true);
+ if (err < 0) {
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+ }
+
+ return err;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 0cda05c72..b91b3739c 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -18,27 +18,14 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10
-static int dice_interface_check(struct fw_unit *unit)
+#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+
+static int check_dice_category(struct fw_unit *unit)
{
- static const int min_values[10] = {
- 10, 0x64 / 4,
- 10, 0x18 / 4,
- 10, 0x18 / 4,
- 0, 0,
- 0, 0,
- };
struct fw_device *device = fw_parent_device(unit);
struct fw_csr_iterator it;
- int key, val, vendor = -1, model = -1, err;
- unsigned int category, i;
- __be32 *pointers;
- u32 value;
- __be32 version;
-
- pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
- GFP_KERNEL);
- if (pointers == NULL)
- return -ENOMEM;
+ int key, val, vendor = -1, model = -1;
+ unsigned int category;
/*
* Check that GUID and unit directory are constructed according to DICE
@@ -64,51 +51,10 @@ static int dice_interface_check(struct fw_unit *unit)
else
category = DICE_CATEGORY_ID;
if (device->config_rom[3] != ((vendor << 8) | category) ||
- device->config_rom[4] >> 22 != model) {
- err = -ENODEV;
- goto end;
- }
-
- /*
- * Check that the sub address spaces exist and are located inside the
- * private address space. The minimum values are chosen so that all
- * minimally required registers are included.
- */
- err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
- DICE_PRIVATE_SPACE, pointers,
- sizeof(__be32) * ARRAY_SIZE(min_values), 0);
- if (err < 0) {
- err = -ENODEV;
- goto end;
- }
- for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
- value = be32_to_cpu(pointers[i]);
- if (value < min_values[i] || value >= 0x40000) {
- err = -ENODEV;
- goto end;
- }
- }
+ device->config_rom[4] >> 22 != model)
+ return -ENODEV;
- /*
- * Check that the implemented DICE driver specification major version
- * number matches.
- */
- err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
- DICE_PRIVATE_SPACE +
- be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
- &version, 4, 0);
- if (err < 0) {
- err = -ENODEV;
- goto end;
- }
- if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
- dev_err(&unit->device,
- "unknown DICE version: 0x%08x\n", be32_to_cpu(version));
- err = -ENODEV;
- goto end;
- }
-end:
- return err;
+ return 0;
}
static int highest_supported_mode_rate(struct snd_dice *dice,
@@ -231,6 +177,16 @@ static void dice_card_strings(struct snd_dice *dice)
strcpy(card->mixername, "DICE");
}
+static void dice_free(struct snd_dice *dice)
+{
+ snd_dice_stream_destroy_duplex(dice);
+ snd_dice_transaction_destroy(dice);
+ fw_unit_put(dice->unit);
+
+ mutex_destroy(&dice->mutex);
+ kfree(dice);
+}
+
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
@@ -239,39 +195,21 @@ static void dice_card_strings(struct snd_dice *dice)
*/
static void dice_card_free(struct snd_card *card)
{
- struct snd_dice *dice = card->private_data;
-
- snd_dice_stream_destroy_duplex(dice);
- snd_dice_transaction_destroy(dice);
- fw_unit_put(dice->unit);
-
- mutex_destroy(&dice->mutex);
+ dice_free(card->private_data);
}
-static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
+static void do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_dice *dice;
+ struct snd_dice *dice = container_of(work, struct snd_dice, dwork.work);
int err;
- err = dice_interface_check(unit);
- if (err < 0)
- goto end;
+ if (dice->registered)
+ return;
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*dice), &card);
+ err = snd_card_new(&dice->unit->device, -1, NULL, THIS_MODULE, 0,
+ &dice->card);
if (err < 0)
- goto end;
-
- dice = card->private_data;
- dice->card = card;
- dice->unit = fw_unit_get(unit);
- card->private_free = dice_card_free;
-
- spin_lock_init(&dice->lock);
- mutex_init(&dice->mutex);
- init_completion(&dice->clock_accepted);
- init_waitqueue_head(&dice->hwdep_wait);
+ return;
err = snd_dice_transaction_init(dice);
if (err < 0)
@@ -283,56 +221,131 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice_card_strings(dice);
+ snd_dice_create_proc(dice);
+
err = snd_dice_create_pcm(dice);
if (err < 0)
goto error;
- err = snd_dice_create_hwdep(dice);
+ err = snd_dice_create_midi(dice);
if (err < 0)
goto error;
- snd_dice_create_proc(dice);
-
- err = snd_dice_create_midi(dice);
+ err = snd_dice_create_hwdep(dice);
if (err < 0)
goto error;
- err = snd_dice_stream_init_duplex(dice);
+ err = snd_card_register(dice->card);
if (err < 0)
goto error;
- err = snd_card_register(card);
+ /*
+ * After registered, dice instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ dice->card->private_free = dice_card_free;
+ dice->card->private_data = dice;
+ dice->registered = true;
+
+ return;
+error:
+ snd_dice_transaction_destroy(dice);
+ snd_card_free(dice->card);
+ dev_info(&dice->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static void schedule_registration(struct snd_dice *dice)
+{
+ struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
+ u64 now, delay;
+
+ now = get_jiffies_64();
+ delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
+
+ if (time_after64(delay, now))
+ delay -= now;
+ else
+ delay = 0;
+
+ mod_delayed_work(system_wq, &dice->dwork, delay);
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
+{
+ struct snd_dice *dice;
+ int err;
+
+ err = check_dice_category(unit);
+ if (err < 0)
+ return -ENODEV;
+
+ /* Allocate this independent of sound card instance. */
+ dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL);
+ if (dice == NULL)
+ return -ENOMEM;
+
+ dice->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dice);
+
+ spin_lock_init(&dice->lock);
+ mutex_init(&dice->mutex);
+ init_completion(&dice->clock_accepted);
+ init_waitqueue_head(&dice->hwdep_wait);
+
+ err = snd_dice_stream_init_duplex(dice);
if (err < 0) {
- snd_dice_stream_destroy_duplex(dice);
- goto error;
+ dice_free(dice);
+ return err;
}
- dev_set_drvdata(&unit->device, dice);
-end:
- return err;
-error:
- snd_card_free(card);
- return err;
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
+ schedule_registration(dice);
+
+ return 0;
}
static void dice_remove(struct fw_unit *unit)
{
struct snd_dice *dice = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(dice->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&dice->dwork);
+
+ if (dice->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dice->card);
+ } else {
+ /* Don't forget this case. */
+ dice_free(dice);
+ }
}
static void dice_bus_reset(struct fw_unit *unit)
{
struct snd_dice *dice = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!dice->registered)
+ schedule_registration(dice);
+
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice);
- mutex_lock(&dice->mutex);
- snd_dice_stream_update_duplex(dice);
- mutex_unlock(&dice->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (dice->registered) {
+ mutex_lock(&dice->mutex);
+ snd_dice_stream_update_duplex(dice);
+ mutex_unlock(&dice->mutex);
+ }
}
#define DICE_INTERFACE 0x000001
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 101550ac1..3d5ebebe6 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -45,6 +45,9 @@ struct snd_dice {
spinlock_t lock;
struct mutex mutex;
+ bool registered;
+ struct delayed_work dwork;
+
/* Offsets for sub-addresses */
unsigned int global_offset;
unsigned int rx_offset;
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index b02a5e8ca..0ac92aba5 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -63,7 +63,7 @@ struct amdtp_dot {
#define BYTE_PER_SAMPLE (4)
#define MAGIC_DOT_BYTE (2)
#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
-static const u8 dot_scrt(const u8 idx, const unsigned int off)
+static u8 dot_scrt(const u8 idx, const unsigned int off)
{
/*
* the length of the added pattern only depends on the lower nibble
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index c7cb7deaf..96c4e0c6a 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -86,8 +86,8 @@ struct snd_efw {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
- atomic_t capture_substreams;
- atomic_t playback_substreams;
+ unsigned int capture_substreams;
+ unsigned int playback_substreams;
/* hardware metering parameters */
unsigned int phys_out;
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index fba01bbba..3e8c4cf9f 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&efw->capture_substreams);
+ mutex_lock(&efw->mutex);
+ efw->capture_substreams++;
err = snd_efw_stream_start_duplex(efw, 0);
+ mutex_unlock(&efw->mutex);
if (err < 0)
snd_efw_stream_lock_release(efw);
@@ -35,8 +37,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&efw->playback_substreams);
+ mutex_lock(&efw->mutex);
+ efw->playback_substreams++;
err = snd_efw_stream_start_duplex(efw, 0);
+ mutex_unlock(&efw->mutex);
if (err < 0)
snd_efw_stream_lock_release(efw);
end:
@@ -47,8 +51,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
- atomic_dec(&efw->capture_substreams);
+ mutex_lock(&efw->mutex);
+ efw->capture_substreams--;
snd_efw_stream_stop_duplex(efw);
+ mutex_unlock(&efw->mutex);
snd_efw_stream_lock_release(efw);
return 0;
@@ -58,8 +64,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_efw *efw = substream->rmidi->private_data;
- atomic_dec(&efw->playback_substreams);
+ mutex_lock(&efw->mutex);
+ efw->playback_substreams--;
snd_efw_stream_stop_duplex(efw);
+ mutex_unlock(&efw->mutex);
snd_efw_stream_lock_release(efw);
return 0;
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index d27135bac..f4fbf75ed 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -251,8 +251,11 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&efw->capture_substreams);
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&efw->mutex);
+ efw->capture_substreams++;
+ mutex_unlock(&efw->mutex);
+ }
amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
@@ -269,8 +272,11 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&efw->playback_substreams);
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&efw->mutex);
+ efw->playback_substreams++;
+ mutex_unlock(&efw->mutex);
+ }
amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
@@ -281,8 +287,11 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&efw->capture_substreams);
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&efw->mutex);
+ efw->capture_substreams--;
+ mutex_unlock(&efw->mutex);
+ }
snd_efw_stream_stop_duplex(efw);
@@ -292,8 +301,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_efw *efw = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&efw->playback_substreams);
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&efw->mutex);
+ efw->playback_substreams--;
+ mutex_unlock(&efw->mutex);
+ }
snd_efw_stream_stop_duplex(efw);
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 759f6e3ed..968a40a1b 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -209,16 +209,13 @@ end:
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
{
struct amdtp_stream *master, *slave;
- atomic_t *slave_substreams;
+ unsigned int slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
- mutex_lock(&efw->mutex);
-
/* Need no substreams */
- if ((atomic_read(&efw->playback_substreams) == 0) &&
- (atomic_read(&efw->capture_substreams) == 0))
+ if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
goto end;
err = get_sync_mode(efw, &sync_mode);
@@ -227,11 +224,11 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &efw->tx_stream;
slave = &efw->rx_stream;
- slave_substreams = &efw->playback_substreams;
+ slave_substreams = efw->playback_substreams;
} else {
master = &efw->rx_stream;
slave = &efw->tx_stream;
- slave_substreams = &efw->capture_substreams;
+ slave_substreams = efw->capture_substreams;
}
/*
@@ -277,7 +274,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
}
/* start slave if needed */
- if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+ if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
err = start_stream(efw, slave, rate);
if (err < 0) {
dev_err(&efw->unit->device,
@@ -286,37 +283,32 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
}
}
end:
- mutex_unlock(&efw->mutex);
return err;
}
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
struct amdtp_stream *master, *slave;
- atomic_t *master_substreams, *slave_substreams;
+ unsigned int master_substreams, slave_substreams;
if (efw->master == &efw->rx_stream) {
slave = &efw->tx_stream;
master = &efw->rx_stream;
- slave_substreams = &efw->capture_substreams;
- master_substreams = &efw->playback_substreams;
+ slave_substreams = efw->capture_substreams;
+ master_substreams = efw->playback_substreams;
} else {
slave = &efw->rx_stream;
master = &efw->tx_stream;
- slave_substreams = &efw->playback_substreams;
- master_substreams = &efw->capture_substreams;
+ slave_substreams = efw->playback_substreams;
+ master_substreams = efw->capture_substreams;
}
- mutex_lock(&efw->mutex);
-
- if (atomic_read(slave_substreams) == 0) {
+ if (slave_substreams == 0) {
stop_stream(efw, slave);
- if (atomic_read(master_substreams) == 0)
+ if (master_substreams == 0)
stop_stream(efw, master);
}
-
- mutex_unlock(&efw->mutex);
}
void snd_efw_stream_update_duplex(struct snd_efw *efw)
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
index 06ff50f4e..b474da7c6 100644
--- a/sound/firewire/oxfw/Makefile
+++ b/sound/firewire/oxfw/Makefile
@@ -1,3 +1,3 @@
-snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
- oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
+snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
+ oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o
obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c
new file mode 100644
index 000000000..bb53eb357
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -0,0 +1,406 @@
+/*
+ * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "oxfw.h"
+
+#define HSS1394_ADDRESS 0xc007dedadadaULL
+#define HSS1394_MAX_PACKET_SIZE 64
+#define HSS1394_TAG_USER_DATA 0x00
+#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
+
+struct fw_scs1x {
+ struct fw_address_handler hss_handler;
+ u8 input_escape_count;
+ struct snd_rawmidi_substream *input;
+
+ /* For MIDI playback. */
+ struct snd_rawmidi_substream *output;
+ bool output_idle;
+ u8 output_status;
+ u8 output_bytes;
+ bool output_escaped;
+ bool output_escape_high_nibble;
+ struct tasklet_struct tasklet;
+ wait_queue_head_t idle_wait;
+ u8 buffer[HSS1394_MAX_PACKET_SIZE];
+ bool transaction_running;
+ struct fw_transaction transaction;
+ struct fw_device *fw_dev;
+};
+
+static const u8 sysex_escape_prefix[] = {
+ 0xf0, /* SysEx begin */
+ 0x00, 0x01, 0x60, /* Stanton DJ */
+ 0x48, 0x53, 0x53, /* "HSS" */
+};
+
+static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
+ u8 byte)
+{
+ u8 nibbles[2];
+
+ nibbles[0] = byte >> 4;
+ nibbles[1] = byte & 0x0f;
+ snd_rawmidi_receive(stream, nibbles, 2);
+}
+
+static void midi_input_byte(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream, u8 byte)
+{
+ const u8 eox = 0xf7;
+
+ if (scs->input_escape_count > 0) {
+ midi_input_escaped_byte(stream, byte);
+ scs->input_escape_count--;
+ if (scs->input_escape_count == 0)
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ } else if (byte == 0xf9) {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ midi_input_escaped_byte(stream, 0x00);
+ midi_input_escaped_byte(stream, 0xf9);
+ scs->input_escape_count = 3;
+ } else {
+ snd_rawmidi_receive(stream, &byte, 1);
+ }
+}
+
+static void midi_input_packet(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream,
+ const u8 *data, unsigned int bytes)
+{
+ unsigned int i;
+ const u8 eox = 0xf7;
+
+ if (data[0] == HSS1394_TAG_USER_DATA) {
+ for (i = 1; i < bytes; ++i)
+ midi_input_byte(scs, stream, data[i]);
+ } else {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ for (i = 0; i < bytes; ++i)
+ midi_input_escaped_byte(stream, data[i]);
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ }
+}
+
+static void handle_hss(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+ struct snd_rawmidi_substream *stream;
+ int rcode;
+
+ if (offset != scs->hss_handler.offset) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
+ tcode != TCODE_WRITE_BLOCK_REQUEST) {
+ rcode = RCODE_TYPE_ERROR;
+ goto end;
+ }
+
+ if (length >= 1) {
+ stream = ACCESS_ONCE(scs->input);
+ if (stream)
+ midi_input_packet(scs, stream, data, length);
+ }
+
+ rcode = RCODE_COMPLETE;
+end:
+ fw_send_response(card, request, rcode);
+}
+
+static void scs_write_callback(struct fw_card *card, int rcode,
+ void *data, size_t length, void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+
+ if (rcode == RCODE_GENERATION)
+ ; /* TODO: retry this packet */
+
+ scs->transaction_running = false;
+ tasklet_schedule(&scs->tasklet);
+}
+
+static bool is_valid_running_status(u8 status)
+{
+ return status >= 0x80 && status <= 0xef;
+}
+
+static bool is_one_byte_cmd(u8 status)
+{
+ return status == 0xf6 ||
+ status >= 0xf8;
+}
+
+static bool is_two_bytes_cmd(u8 status)
+{
+ return (status >= 0xc0 && status <= 0xdf) ||
+ status == 0xf1 ||
+ status == 0xf3;
+}
+
+static bool is_three_bytes_cmd(u8 status)
+{
+ return (status >= 0x80 && status <= 0xbf) ||
+ (status >= 0xe0 && status <= 0xef) ||
+ status == 0xf2;
+}
+
+static bool is_invalid_cmd(u8 status)
+{
+ return status == 0xf4 ||
+ status == 0xf5 ||
+ status == 0xf9 ||
+ status == 0xfd;
+}
+
+static void scs_output_tasklet(unsigned long data)
+{
+ struct fw_scs1x *scs = (struct fw_scs1x *)data;
+ struct snd_rawmidi_substream *stream;
+ unsigned int i;
+ u8 byte;
+ int generation;
+
+ if (scs->transaction_running)
+ return;
+
+ stream = ACCESS_ONCE(scs->output);
+ if (!stream) {
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+
+ i = scs->output_bytes;
+ for (;;) {
+ if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+ scs->output_bytes = i;
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+ /*
+ * Convert from real MIDI to what I think the device expects (no
+ * running status, one command per packet, unescaped SysExs).
+ */
+ if (scs->output_escaped && byte < 0x80) {
+ if (scs->output_escape_high_nibble) {
+ if (i < HSS1394_MAX_PACKET_SIZE) {
+ scs->buffer[i] = byte << 4;
+ scs->output_escape_high_nibble = false;
+ }
+ } else {
+ scs->buffer[i++] |= byte & 0x0f;
+ scs->output_escape_high_nibble = true;
+ }
+ } else if (byte < 0x80) {
+ if (i == 1) {
+ if (!is_valid_running_status(
+ scs->output_status))
+ continue;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = scs->output_status;
+ }
+ scs->buffer[i++] = byte;
+ if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
+ (i == 4 && is_three_bytes_cmd(scs->output_status)))
+ break;
+ if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
+ !memcmp(scs->buffer + 1, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix))) {
+ scs->output_escaped = true;
+ scs->output_escape_high_nibble = true;
+ i = 0;
+ }
+ if (i >= HSS1394_MAX_PACKET_SIZE)
+ i = 1;
+ } else if (byte == 0xf7) {
+ if (scs->output_escaped) {
+ if (i >= 1 && scs->output_escape_high_nibble &&
+ scs->buffer[0] !=
+ HSS1394_TAG_CHANGE_ADDRESS)
+ break;
+ } else {
+ if (i > 1 && scs->output_status == 0xf0) {
+ scs->buffer[i++] = 0xf7;
+ break;
+ }
+ }
+ i = 1;
+ scs->output_escaped = false;
+ } else if (!is_invalid_cmd(byte) && byte < 0xf8) {
+ i = 1;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = byte;
+ scs->output_status = byte;
+ scs->output_escaped = false;
+ if (is_one_byte_cmd(byte))
+ break;
+ }
+ }
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+
+ scs->transaction_running = true;
+ generation = scs->fw_dev->generation;
+ smp_rmb(); /* node_id vs. generation */
+ fw_send_request(scs->fw_dev->card, &scs->transaction,
+ TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
+ generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
+ scs->buffer, i, scs_write_callback, scs);
+}
+
+static int midi_capture_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->input_escape_count = 0;
+ ACCESS_ONCE(scs->input) = stream;
+ } else {
+ ACCESS_ONCE(scs->input) = NULL;
+ }
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static int midi_playback_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->output_status = 0;
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+ scs->output_idle = false;
+
+ ACCESS_ONCE(scs->output) = stream;
+ tasklet_schedule(&scs->tasklet);
+ } else {
+ ACCESS_ONCE(scs->output) = NULL;
+ }
+}
+static void midi_playback_drain(struct snd_rawmidi_substream *stream)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ wait_event(scs->idle_wait, scs->output_idle);
+}
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ .drain = midi_playback_drain,
+};
+static int register_address(struct snd_oxfw *oxfw)
+{
+ struct fw_scs1x *scs = oxfw->spec;
+ __be64 data;
+
+ data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
+ scs->hss_handler.offset);
+ return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
+ HSS1394_ADDRESS, &data, sizeof(data), 0);
+}
+
+static void remove_scs1x(struct snd_rawmidi *rmidi)
+{
+ struct fw_scs1x *scs = rmidi->private_data;
+
+ fw_core_remove_address_handler(&scs->hss_handler);
+}
+
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
+{
+ register_address(oxfw);
+}
+
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
+{
+ struct snd_rawmidi *rmidi;
+ struct fw_scs1x *scs;
+ int err;
+
+ scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL);
+ if (scs == NULL)
+ return -ENOMEM;
+ scs->fw_dev = fw_parent_device(oxfw->unit);
+ oxfw->spec = scs;
+
+ /* Allocate own handler for imcoming asynchronous transaction. */
+ scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
+ scs->hss_handler.address_callback = handle_hss;
+ scs->hss_handler.callback_data = scs;
+ err = fw_core_add_address_handler(&scs->hss_handler,
+ &fw_high_memory_region);
+ if (err < 0)
+ return err;
+
+ err = register_address(oxfw);
+ if (err < 0)
+ goto err_allocated;
+
+ /* Use unique name for backward compatibility to scs1x module. */
+ err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
+ if (err < 0)
+ goto err_allocated;
+ rmidi->private_data = scs;
+ rmidi->private_free = remove_scs1x;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
+ init_waitqueue_head(&scs->idle_wait);
+ scs->output_idle = true;
+
+ return 0;
+err_allocated:
+ fw_core_remove_address_handler(&scs->hss_handler);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-spkr.c
index 02a1cb90f..cb905af06 100644
--- a/sound/firewire/oxfw/oxfw-control.c
+++ b/sound/firewire/oxfw/oxfw-spkr.c
@@ -1,5 +1,5 @@
/*
- * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* Licensed under the terms of the GNU General Public License, version 2.
@@ -7,6 +7,17 @@
#include "oxfw.h"
+struct fw_spkr {
+ bool mute;
+ s16 volume[6];
+ s16 volume_min;
+ s16 volume_max;
+
+ unsigned int mixer_channels;
+ u8 mute_fb_id;
+ u8 volume_fb_id;
+};
+
enum control_action { CTL_READ, CTL_WRITE };
enum control_attribute {
CTL_MIN = 0x02,
@@ -14,8 +25,8 @@ enum control_attribute {
CTL_CURRENT = 0x10,
};
-static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
- enum control_action action)
+static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
+ enum control_action action)
{
u8 *buf;
u8 response_ok;
@@ -35,7 +46,7 @@ static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
buf[1] = 0x08; /* audio unit 0 */
buf[2] = 0xb8; /* FUNCTION BLOCK */
buf[3] = 0x81; /* function block type: feature */
- buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */
+ buf[4] = fb_id; /* function block ID */
buf[5] = 0x10; /* control attribute: current */
buf[6] = 0x02; /* selector length */
buf[7] = 0x00; /* audio channel number */
@@ -46,16 +57,16 @@ static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value,
else
buf[10] = *value ? 0x70 : 0x60;
- err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe);
+ err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
if (err < 0)
goto error;
if (err < 11) {
- dev_err(&oxfw->unit->device, "short FCP response\n");
+ dev_err(&unit->device, "short FCP response\n");
err = -EIO;
goto error;
}
if (buf[0] != response_ok) {
- dev_err(&oxfw->unit->device, "mute command failed\n");
+ dev_err(&unit->device, "mute command failed\n");
err = -EIO;
goto error;
}
@@ -70,10 +81,10 @@ error:
return err;
}
-static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
- unsigned int channel,
- enum control_attribute attribute,
- enum control_action action)
+static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
+ unsigned int channel,
+ enum control_attribute attribute,
+ enum control_action action)
{
u8 *buf;
u8 response_ok;
@@ -93,7 +104,7 @@ static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
buf[1] = 0x08; /* audio unit 0 */
buf[2] = 0xb8; /* FUNCTION BLOCK */
buf[3] = 0x81; /* function block type: feature */
- buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */
+ buf[4] = fb_id; /* function block ID */
buf[5] = attribute; /* control attribute */
buf[6] = 0x02; /* selector length */
buf[7] = channel; /* audio channel number */
@@ -107,16 +118,16 @@ static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value,
buf[11] = *value;
}
- err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe);
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
if (err < 0)
goto error;
if (err < 12) {
- dev_err(&oxfw->unit->device, "short FCP response\n");
+ dev_err(&unit->device, "short FCP response\n");
err = -EIO;
goto error;
}
if (buf[0] != response_ok) {
- dev_err(&oxfw->unit->device, "volume command failed\n");
+ dev_err(&unit->device, "volume command failed\n");
err = -EIO;
goto error;
}
@@ -131,75 +142,81 @@ error:
return err;
}
-static int oxfw_mute_get(struct snd_kcontrol *control,
+static int spkr_mute_get(struct snd_kcontrol *control,
struct snd_ctl_elem_value *value)
{
struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
- value->value.integer.value[0] = !oxfw->mute;
+ value->value.integer.value[0] = !spkr->mute;
return 0;
}
-static int oxfw_mute_put(struct snd_kcontrol *control,
+static int spkr_mute_put(struct snd_kcontrol *control,
struct snd_ctl_elem_value *value)
{
struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
bool mute;
int err;
mute = !value->value.integer.value[0];
- if (mute == oxfw->mute)
+ if (mute == spkr->mute)
return 0;
- err = oxfw_mute_command(oxfw, &mute, CTL_WRITE);
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
+ CTL_WRITE);
if (err < 0)
return err;
- oxfw->mute = mute;
+ spkr->mute = mute;
return 1;
}
-static int oxfw_volume_info(struct snd_kcontrol *control,
+static int spkr_volume_info(struct snd_kcontrol *control,
struct snd_ctl_elem_info *info)
{
struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = oxfw->device_info->mixer_channels;
- info->value.integer.min = oxfw->volume_min;
- info->value.integer.max = oxfw->volume_max;
+ info->count = spkr->mixer_channels;
+ info->value.integer.min = spkr->volume_min;
+ info->value.integer.max = spkr->volume_max;
return 0;
}
static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
-static int oxfw_volume_get(struct snd_kcontrol *control,
+static int spkr_volume_get(struct snd_kcontrol *control,
struct snd_ctl_elem_value *value)
{
struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
unsigned int i;
- for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
- value->value.integer.value[channel_map[i]] = oxfw->volume[i];
+ for (i = 0; i < spkr->mixer_channels; ++i)
+ value->value.integer.value[channel_map[i]] = spkr->volume[i];
return 0;
}
-static int oxfw_volume_put(struct snd_kcontrol *control,
+static int spkr_volume_put(struct snd_kcontrol *control,
struct snd_ctl_elem_value *value)
{
struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
unsigned int i, changed_channels;
bool equal_values = true;
s16 volume;
int err;
- for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
- if (value->value.integer.value[i] < oxfw->volume_min ||
- value->value.integer.value[i] > oxfw->volume_max)
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ if (value->value.integer.value[i] < spkr->volume_min ||
+ value->value.integer.value[i] > spkr->volume_max)
return -EINVAL;
if (value->value.integer.value[i] !=
value->value.integer.value[0])
@@ -207,67 +224,86 @@ static int oxfw_volume_put(struct snd_kcontrol *control,
}
changed_channels = 0;
- for (i = 0; i < oxfw->device_info->mixer_channels; ++i)
+ for (i = 0; i < spkr->mixer_channels; ++i)
if (value->value.integer.value[channel_map[i]] !=
- oxfw->volume[i])
+ spkr->volume[i])
changed_channels |= 1 << (i + 1);
if (equal_values && changed_channels != 0)
changed_channels = 1 << 0;
- for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) {
+ for (i = 0; i <= spkr->mixer_channels; ++i) {
volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
if (changed_channels & (1 << i)) {
- err = oxfw_volume_command(oxfw, &volume, i,
- CTL_CURRENT, CTL_WRITE);
+ err = avc_audio_feature_volume(oxfw->unit,
+ spkr->volume_fb_id, &volume,
+ i, CTL_CURRENT, CTL_WRITE);
if (err < 0)
return err;
}
if (i > 0)
- oxfw->volume[i - 1] = volume;
+ spkr->volume[i - 1] = volume;
}
return changed_channels != 0;
}
-int snd_oxfw_create_mixer(struct snd_oxfw *oxfw)
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
{
static const struct snd_kcontrol_new controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.info = snd_ctl_boolean_mono_info,
- .get = oxfw_mute_get,
- .put = oxfw_mute_put,
+ .get = spkr_mute_get,
+ .put = spkr_mute_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
- .info = oxfw_volume_info,
- .get = oxfw_volume_get,
- .put = oxfw_volume_put,
+ .info = spkr_volume_info,
+ .get = spkr_volume_get,
+ .put = spkr_volume_put,
},
};
+ struct fw_spkr *spkr;
unsigned int i, first_ch;
int err;
- err = oxfw_volume_command(oxfw, &oxfw->volume_min,
- 0, CTL_MIN, CTL_READ);
+ spkr = kzalloc(sizeof(struct fw_spkr), GFP_KERNEL);
+ if (spkr == NULL)
+ return -ENOMEM;
+ oxfw->spec = spkr;
+
+ if (is_lacie) {
+ spkr->mixer_channels = 1;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x01;
+ } else {
+ spkr->mixer_channels = 6;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x02;
+ }
+
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_min, 0, CTL_MIN, CTL_READ);
if (err < 0)
return err;
- err = oxfw_volume_command(oxfw, &oxfw->volume_max,
- 0, CTL_MAX, CTL_READ);
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_max, 0, CTL_MAX, CTL_READ);
if (err < 0)
return err;
- err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ);
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
+ CTL_READ);
if (err < 0)
return err;
- first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1;
- for (i = 0; i < oxfw->device_info->mixer_channels; ++i) {
- err = oxfw_volume_command(oxfw, &oxfw->volume[i],
- first_ch + i, CTL_CURRENT, CTL_READ);
+ first_ch = spkr->mixer_channels == 1 ? 0 : 1;
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume[i], first_ch + i,
+ CTL_CURRENT, CTL_READ);
if (err < 0)
return err;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 588b93f20..abedc2207 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -19,6 +19,7 @@
#define VENDOR_BEHRINGER 0x001564
#define VENDOR_LACIE 0x00d04b
#define VENDOR_TASCAM 0x00022e
+#define OUI_STANTON 0x001260
#define MODEL_SATELLITE 0x00200f
@@ -29,6 +30,13 @@ MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("snd-firewire-speakers");
+MODULE_ALIAS("snd-scs1x");
+
+struct compat_info {
+ const char *driver_name;
+ const char *vendor_name;
+ const char *model_name;
+};
static bool detect_loud_models(struct fw_unit *unit)
{
@@ -59,6 +67,7 @@ static bool detect_loud_models(struct fw_unit *unit)
static int name_card(struct snd_oxfw *oxfw)
{
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ const struct compat_info *info;
char vendor[24];
char model[32];
const char *d, *v, *m;
@@ -84,10 +93,12 @@ static int name_card(struct snd_oxfw *oxfw)
be32_to_cpus(&firmware);
/* to apply card definitions */
- if (oxfw->device_info) {
- d = oxfw->device_info->driver_name;
- v = oxfw->device_info->vendor_name;
- m = oxfw->device_info->model_name;
+ if (oxfw->entry->vendor_id == VENDOR_GRIFFIN ||
+ oxfw->entry->vendor_id == VENDOR_LACIE) {
+ info = (const struct compat_info *)oxfw->entry->driver_data;
+ d = info->driver_name;
+ v = info->vendor_name;
+ m = info->model_name;
} else {
d = "OXFW";
v = vendor;
@@ -129,16 +140,51 @@ static void oxfw_card_free(struct snd_card *card)
kfree(oxfw->rx_stream_formats[i]);
}
+ kfree(oxfw->spec);
mutex_destroy(&oxfw->mutex);
}
-static void detect_quirks(struct snd_oxfw *oxfw)
+static int detect_quirks(struct snd_oxfw *oxfw)
{
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
struct fw_csr_iterator it;
int key, val;
int vendor, model;
+ /*
+ * Add ALSA control elements for two models to keep compatibility to
+ * old firewire-speaker module.
+ */
+ if (oxfw->entry->vendor_id == VENDOR_GRIFFIN)
+ return snd_oxfw_add_spkr(oxfw, false);
+ if (oxfw->entry->vendor_id == VENDOR_LACIE)
+ return snd_oxfw_add_spkr(oxfw, true);
+
+ /*
+ * Stanton models supports asynchronous transactions for unique MIDI
+ * messages.
+ */
+ if (oxfw->entry->vendor_id == OUI_STANTON) {
+ /* No physical MIDI ports. */
+ oxfw->midi_input_ports = 0;
+ oxfw->midi_output_ports = 0;
+
+ /* Output stream exists but no data channels are useful. */
+ oxfw->has_output = false;
+
+ return snd_oxfw_scs1x_add(oxfw);
+ }
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (oxfw->entry->vendor_id == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ return 0;
+ }
+
/* Seek from Root Directory of Config ROM. */
vendor = model = 0;
fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
@@ -156,24 +202,17 @@ static void detect_quirks(struct snd_oxfw *oxfw)
if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
oxfw->wrong_dbs = true;
- /*
- * TASCAM FireOne has physical control and requires a pair of additional
- * MIDI ports.
- */
- if (vendor == VENDOR_TASCAM) {
- oxfw->midi_input_ports++;
- oxfw->midi_output_ports++;
- }
+ return 0;
}
static int oxfw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *id)
+ const struct ieee1394_device_id *entry)
{
struct snd_card *card;
struct snd_oxfw *oxfw;
int err;
- if ((id->vendor_id == VENDOR_LOUD) && !detect_loud_models(unit))
+ if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
return -ENODEV;
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
@@ -186,7 +225,7 @@ static int oxfw_probe(struct fw_unit *unit,
oxfw->card = card;
mutex_init(&oxfw->mutex);
oxfw->unit = fw_unit_get(unit);
- oxfw->device_info = (const struct device_info *)id->driver_data;
+ oxfw->entry = entry;
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
@@ -194,21 +233,17 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- detect_quirks(oxfw);
-
err = name_card(oxfw);
if (err < 0)
goto error;
- err = snd_oxfw_create_pcm(oxfw);
+ err = detect_quirks(oxfw);
if (err < 0)
goto error;
- if (oxfw->device_info) {
- err = snd_oxfw_create_mixer(oxfw);
- if (err < 0)
- goto error;
- }
+ err = snd_oxfw_create_pcm(oxfw);
+ if (err < 0)
+ goto error;
snd_oxfw_proc_init(oxfw);
@@ -257,6 +292,9 @@ static void oxfw_bus_reset(struct fw_unit *unit)
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
mutex_unlock(&oxfw->mutex);
+
+ if (oxfw->entry->vendor_id == OUI_STANTON)
+ snd_oxfw_scs1x_update(oxfw);
}
static void oxfw_remove(struct fw_unit *unit)
@@ -267,22 +305,16 @@ static void oxfw_remove(struct fw_unit *unit)
snd_card_free_when_closed(oxfw->card);
}
-static const struct device_info griffin_firewave = {
+static const struct compat_info griffin_firewave = {
.driver_name = "FireWave",
.vendor_name = "Griffin",
.model_name = "FireWave",
- .mixer_channels = 6,
- .mute_fb_id = 0x01,
- .volume_fb_id = 0x02,
};
-static const struct device_info lacie_speakers = {
+static const struct compat_info lacie_speakers = {
.driver_name = "FWSpeakers",
.vendor_name = "LaCie",
.model_name = "FireWire Speakers",
- .mixer_channels = 1,
- .mute_fb_id = 0x01,
- .volume_fb_id = 0x01,
};
static const struct ieee1394_device_id oxfw_id_table[] = {
@@ -340,6 +372,20 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
.vendor_id = VENDOR_TASCAM,
.model_id = 0x800007,
},
+ /* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_STANTON,
+ .model_id = 0x001000,
+ },
+ /* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_STANTON,
+ .model_id = 0x002000,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 8392c424a..9beecc214 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -31,15 +31,6 @@
#include "../amdtp-am824.h"
#include "../cmp.h"
-struct device_info {
- const char *driver_name;
- const char *vendor_name;
- const char *model_name;
- unsigned int mixer_channels;
- u8 mute_fb_id;
- u8 volume_fb_id;
-};
-
/* This is an arbitrary number for convinience. */
#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
struct snd_oxfw {
@@ -64,14 +55,12 @@ struct snd_oxfw {
unsigned int midi_input_ports;
unsigned int midi_output_ports;
- bool mute;
- s16 volume[6];
- s16 volume_min;
- s16 volume_max;
-
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
+
+ const struct ieee1394_device_id *entry;
+ void *spec;
};
/*
@@ -138,10 +127,12 @@ void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
-int snd_oxfw_create_mixer(struct snd_oxfw *oxfw);
-
void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
+
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie);
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw);
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw);
diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c
deleted file mode 100644
index 2dba848a7..000000000
--- a/sound/firewire/scs1x.c
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Stanton Control System 1 MIDI driver
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/device.h>
-#include <linux/firewire.h>
-#include <linux/firewire-constants.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/wait.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/rawmidi.h>
-#include "lib.h"
-
-#define OUI_STANTON 0x001260
-#define MODEL_SCS_1M 0x001000
-#define MODEL_SCS_1D 0x002000
-
-#define HSS1394_ADDRESS 0xc007dedadadaULL
-#define HSS1394_MAX_PACKET_SIZE 64
-
-#define HSS1394_TAG_USER_DATA 0x00
-#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
-
-struct scs {
- struct snd_card *card;
- struct fw_unit *unit;
- struct fw_address_handler hss_handler;
- struct fw_transaction transaction;
- bool transaction_running;
- bool output_idle;
- u8 output_status;
- u8 output_bytes;
- bool output_escaped;
- bool output_escape_high_nibble;
- u8 input_escape_count;
- struct snd_rawmidi_substream *output;
- struct snd_rawmidi_substream *input;
- struct tasklet_struct tasklet;
- wait_queue_head_t idle_wait;
- u8 *buffer;
-};
-
-static const u8 sysex_escape_prefix[] = {
- 0xf0, /* SysEx begin */
- 0x00, 0x01, 0x60, /* Stanton DJ */
- 0x48, 0x53, 0x53, /* "HSS" */
-};
-
-static int scs_output_open(struct snd_rawmidi_substream *stream)
-{
- struct scs *scs = stream->rmidi->private_data;
-
- scs->output_status = 0;
- scs->output_bytes = 1;
- scs->output_escaped = false;
-
- return 0;
-}
-
-static int scs_output_close(struct snd_rawmidi_substream *stream)
-{
- return 0;
-}
-
-static void scs_output_trigger(struct snd_rawmidi_substream *stream, int up)
-{
- struct scs *scs = stream->rmidi->private_data;
-
- ACCESS_ONCE(scs->output) = up ? stream : NULL;
- if (up) {
- scs->output_idle = false;
- tasklet_schedule(&scs->tasklet);
- }
-}
-
-static void scs_write_callback(struct fw_card *card, int rcode,
- void *data, size_t length, void *callback_data)
-{
- struct scs *scs = callback_data;
-
- if (rcode == RCODE_GENERATION) {
- /* TODO: retry this packet */
- }
-
- scs->transaction_running = false;
- tasklet_schedule(&scs->tasklet);
-}
-
-static bool is_valid_running_status(u8 status)
-{
- return status >= 0x80 && status <= 0xef;
-}
-
-static bool is_one_byte_cmd(u8 status)
-{
- return status == 0xf6 ||
- status >= 0xf8;
-}
-
-static bool is_two_bytes_cmd(u8 status)
-{
- return (status >= 0xc0 && status <= 0xdf) ||
- status == 0xf1 ||
- status == 0xf3;
-}
-
-static bool is_three_bytes_cmd(u8 status)
-{
- return (status >= 0x80 && status <= 0xbf) ||
- (status >= 0xe0 && status <= 0xef) ||
- status == 0xf2;
-}
-
-static bool is_invalid_cmd(u8 status)
-{
- return status == 0xf4 ||
- status == 0xf5 ||
- status == 0xf9 ||
- status == 0xfd;
-}
-
-static void scs_output_tasklet(unsigned long data)
-{
- struct scs *scs = (void *)data;
- struct snd_rawmidi_substream *stream;
- unsigned int i;
- u8 byte;
- struct fw_device *dev;
- int generation;
-
- if (scs->transaction_running)
- return;
-
- stream = ACCESS_ONCE(scs->output);
- if (!stream) {
- scs->output_idle = true;
- wake_up(&scs->idle_wait);
- return;
- }
-
- i = scs->output_bytes;
- for (;;) {
- if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
- scs->output_bytes = i;
- scs->output_idle = true;
- wake_up(&scs->idle_wait);
- return;
- }
- /*
- * Convert from real MIDI to what I think the device expects (no
- * running status, one command per packet, unescaped SysExs).
- */
- if (scs->output_escaped && byte < 0x80) {
- if (scs->output_escape_high_nibble) {
- if (i < HSS1394_MAX_PACKET_SIZE) {
- scs->buffer[i] = byte << 4;
- scs->output_escape_high_nibble = false;
- }
- } else {
- scs->buffer[i++] |= byte & 0x0f;
- scs->output_escape_high_nibble = true;
- }
- } else if (byte < 0x80) {
- if (i == 1) {
- if (!is_valid_running_status(scs->output_status))
- continue;
- scs->buffer[0] = HSS1394_TAG_USER_DATA;
- scs->buffer[i++] = scs->output_status;
- }
- scs->buffer[i++] = byte;
- if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
- (i == 4 && is_three_bytes_cmd(scs->output_status)))
- break;
- if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
- !memcmp(scs->buffer + 1, sysex_escape_prefix,
- ARRAY_SIZE(sysex_escape_prefix))) {
- scs->output_escaped = true;
- scs->output_escape_high_nibble = true;
- i = 0;
- }
- if (i >= HSS1394_MAX_PACKET_SIZE)
- i = 1;
- } else if (byte == 0xf7) {
- if (scs->output_escaped) {
- if (i >= 1 && scs->output_escape_high_nibble &&
- scs->buffer[0] != HSS1394_TAG_CHANGE_ADDRESS)
- break;
- } else {
- if (i > 1 && scs->output_status == 0xf0) {
- scs->buffer[i++] = 0xf7;
- break;
- }
- }
- i = 1;
- scs->output_escaped = false;
- } else if (!is_invalid_cmd(byte) &&
- byte < 0xf8) {
- i = 1;
- scs->buffer[0] = HSS1394_TAG_USER_DATA;
- scs->buffer[i++] = byte;
- scs->output_status = byte;
- scs->output_escaped = false;
- if (is_one_byte_cmd(byte))
- break;
- }
- }
- scs->output_bytes = 1;
- scs->output_escaped = false;
-
- scs->transaction_running = true;
- dev = fw_parent_device(scs->unit);
- generation = dev->generation;
- smp_rmb(); /* node_id vs. generation */
- fw_send_request(dev->card, &scs->transaction, TCODE_WRITE_BLOCK_REQUEST,
- dev->node_id, generation, dev->max_speed,
- HSS1394_ADDRESS, scs->buffer, i,
- scs_write_callback, scs);
-}
-
-static void scs_output_drain(struct snd_rawmidi_substream *stream)
-{
- struct scs *scs = stream->rmidi->private_data;
-
- wait_event(scs->idle_wait, scs->output_idle);
-}
-
-static struct snd_rawmidi_ops output_ops = {
- .open = scs_output_open,
- .close = scs_output_close,
- .trigger = scs_output_trigger,
- .drain = scs_output_drain,
-};
-
-static int scs_input_open(struct snd_rawmidi_substream *stream)
-{
- struct scs *scs = stream->rmidi->private_data;
-
- scs->input_escape_count = 0;
-
- return 0;
-}
-
-static int scs_input_close(struct snd_rawmidi_substream *stream)
-{
- return 0;
-}
-
-static void scs_input_trigger(struct snd_rawmidi_substream *stream, int up)
-{
- struct scs *scs = stream->rmidi->private_data;
-
- ACCESS_ONCE(scs->input) = up ? stream : NULL;
-}
-
-static void scs_input_escaped_byte(struct snd_rawmidi_substream *stream,
- u8 byte)
-{
- u8 nibbles[2];
-
- nibbles[0] = byte >> 4;
- nibbles[1] = byte & 0x0f;
- snd_rawmidi_receive(stream, nibbles, 2);
-}
-
-static void scs_input_midi_byte(struct scs *scs,
- struct snd_rawmidi_substream *stream,
- u8 byte)
-{
- if (scs->input_escape_count > 0) {
- scs_input_escaped_byte(stream, byte);
- scs->input_escape_count--;
- if (scs->input_escape_count == 0)
- snd_rawmidi_receive(stream, (const u8[]) { 0xf7 }, 1);
- } else if (byte == 0xf9) {
- snd_rawmidi_receive(stream, sysex_escape_prefix,
- ARRAY_SIZE(sysex_escape_prefix));
- scs_input_escaped_byte(stream, 0x00);
- scs_input_escaped_byte(stream, 0xf9);
- scs->input_escape_count = 3;
- } else {
- snd_rawmidi_receive(stream, &byte, 1);
- }
-}
-
-static void scs_input_packet(struct scs *scs,
- struct snd_rawmidi_substream *stream,
- const u8 *data, unsigned int bytes)
-{
- unsigned int i;
-
- if (data[0] == HSS1394_TAG_USER_DATA) {
- for (i = 1; i < bytes; ++i)
- scs_input_midi_byte(scs, stream, data[i]);
- } else {
- snd_rawmidi_receive(stream, sysex_escape_prefix,
- ARRAY_SIZE(sysex_escape_prefix));
- for (i = 0; i < bytes; ++i)
- scs_input_escaped_byte(stream, data[i]);
- snd_rawmidi_receive(stream, (const u8[]) { 0xf7 }, 1);
- }
-}
-
-static struct snd_rawmidi_ops input_ops = {
- .open = scs_input_open,
- .close = scs_input_close,
- .trigger = scs_input_trigger,
-};
-
-static int scs_create_midi(struct scs *scs)
-{
- struct snd_rawmidi *rmidi;
- int err;
-
- err = snd_rawmidi_new(scs->card, "SCS.1x", 0, 1, 1, &rmidi);
- if (err < 0)
- return err;
- snprintf(rmidi->name, sizeof(rmidi->name),
- "%s MIDI", scs->card->shortname);
- rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
- SNDRV_RAWMIDI_INFO_INPUT |
- SNDRV_RAWMIDI_INFO_DUPLEX;
- rmidi->private_data = scs;
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &output_ops);
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &input_ops);
-
- return 0;
-}
-
-static void handle_hss(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source, int generation,
- unsigned long long offset, void *data, size_t length,
- void *callback_data)
-{
- struct scs *scs = callback_data;
- struct snd_rawmidi_substream *stream;
-
- if (offset != scs->hss_handler.offset) {
- fw_send_response(card, request, RCODE_ADDRESS_ERROR);
- return;
- }
- if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
- tcode != TCODE_WRITE_BLOCK_REQUEST) {
- fw_send_response(card, request, RCODE_TYPE_ERROR);
- return;
- }
-
- if (length >= 1) {
- stream = ACCESS_ONCE(scs->input);
- if (stream)
- scs_input_packet(scs, stream, data, length);
- }
-
- fw_send_response(card, request, RCODE_COMPLETE);
-}
-
-static int scs_init_hss_address(struct scs *scs)
-{
- __be64 data;
- int err;
-
- data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
- scs->hss_handler.offset);
- err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
- HSS1394_ADDRESS, &data, 8, 0);
- if (err < 0)
- dev_err(&scs->unit->device, "HSS1394 communication failed\n");
-
- return err;
-}
-
-static void scs_card_free(struct snd_card *card)
-{
- struct scs *scs = card->private_data;
-
- fw_core_remove_address_handler(&scs->hss_handler);
- kfree(scs->buffer);
-}
-
-static int scs_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
-{
- struct fw_device *fw_dev = fw_parent_device(unit);
- struct snd_card *card;
- struct scs *scs;
- int err;
-
- err = snd_card_new(&unit->device, -16, NULL, THIS_MODULE,
- sizeof(*scs), &card);
- if (err < 0)
- return err;
-
- scs = card->private_data;
- scs->card = card;
- scs->unit = unit;
- tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
- init_waitqueue_head(&scs->idle_wait);
- scs->output_idle = true;
-
- scs->buffer = kmalloc(HSS1394_MAX_PACKET_SIZE, GFP_KERNEL);
- if (!scs->buffer) {
- err = -ENOMEM;
- goto err_card;
- }
-
- scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
- scs->hss_handler.address_callback = handle_hss;
- scs->hss_handler.callback_data = scs;
- err = fw_core_add_address_handler(&scs->hss_handler,
- &fw_high_memory_region);
- if (err < 0)
- goto err_buffer;
-
- card->private_free = scs_card_free;
-
- strcpy(card->driver, "SCS.1x");
- strcpy(card->shortname, "SCS.1x");
- fw_csr_string(unit->directory, CSR_MODEL,
- card->shortname, sizeof(card->shortname));
- snprintf(card->longname, sizeof(card->longname),
- "Stanton DJ %s (GUID %08x%08x) at %s, S%d",
- card->shortname, fw_dev->config_rom[3], fw_dev->config_rom[4],
- dev_name(&unit->device), 100 << fw_dev->max_speed);
- strcpy(card->mixername, card->shortname);
-
- err = scs_init_hss_address(scs);
- if (err < 0)
- goto err_card;
-
- err = scs_create_midi(scs);
- if (err < 0)
- goto err_card;
-
- err = snd_card_register(card);
- if (err < 0)
- goto err_card;
-
- dev_set_drvdata(&unit->device, scs);
-
- return 0;
-
-err_buffer:
- kfree(scs->buffer);
-err_card:
- snd_card_free(card);
- return err;
-}
-
-static void scs_update(struct fw_unit *unit)
-{
- struct scs *scs = dev_get_drvdata(&unit->device);
- int generation;
- __be64 data;
-
- data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
- scs->hss_handler.offset);
- generation = fw_parent_device(unit)->generation;
- smp_rmb(); /* node_id vs. generation */
- snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST,
- HSS1394_ADDRESS, &data, 8,
- FW_FIXED_GENERATION | generation);
-}
-
-static void scs_remove(struct fw_unit *unit)
-{
- struct scs *scs = dev_get_drvdata(&unit->device);
-
- snd_card_disconnect(scs->card);
-
- ACCESS_ONCE(scs->output) = NULL;
- ACCESS_ONCE(scs->input) = NULL;
-
- wait_event(scs->idle_wait, scs->output_idle);
-
- tasklet_kill(&scs->tasklet);
-
- snd_card_free_when_closed(scs->card);
-}
-
-static const struct ieee1394_device_id scs_id_table[] = {
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = OUI_STANTON,
- .model_id = MODEL_SCS_1M,
- },
- {
- .match_flags = IEEE1394_MATCH_VENDOR_ID |
- IEEE1394_MATCH_MODEL_ID,
- .vendor_id = OUI_STANTON,
- .model_id = MODEL_SCS_1D,
- },
- {}
-};
-MODULE_DEVICE_TABLE(ieee1394, scs_id_table);
-
-MODULE_DESCRIPTION("SCS.1x MIDI driver");
-MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_LICENSE("GPL v2");
-
-static struct fw_driver scs_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = KBUILD_MODNAME,
- .bus = &fw_bus_type,
- },
- .probe = scs_probe,
- .update = scs_update,
- .remove = scs_remove,
- .id_table = scs_id_table,
-};
-
-static int __init alsa_scs1x_init(void)
-{
- return driver_register(&scs_driver.driver);
-}
-
-static void __exit alsa_scs1x_exit(void)
-{
- driver_unregister(&scs_driver.driver);
-}
-
-module_init(alsa_scs1x_init);
-module_exit(alsa_scs1x_exit);
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
index 904ce0329..040a96d1b 100644
--- a/sound/firewire/tascam/tascam-transaction.c
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -230,6 +230,7 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm)
return err;
error:
fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
return err;
}
@@ -276,6 +277,9 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
__be32 reg;
unsigned int i;
+ if (tscm->async_handler.callback_data == NULL)
+ return;
+
/* Turn off FireWire LED. */
reg = cpu_to_be32(0x0000008e);
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
@@ -297,6 +301,8 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
&reg, sizeof(reg), 0);
fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
+
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
index ee0bc1839..e281c338e 100644
--- a/sound/firewire/tascam/tascam.c
+++ b/sound/firewire/tascam/tascam.c
@@ -21,7 +21,6 @@ static struct snd_tscm_spec model_specs[] = {
.pcm_playback_analog_channels = 8,
.midi_capture_ports = 4,
.midi_playback_ports = 4,
- .is_controller = true,
},
{
.name = "FW-1082",
@@ -31,9 +30,16 @@ static struct snd_tscm_spec model_specs[] = {
.pcm_playback_analog_channels = 2,
.midi_capture_ports = 2,
.midi_playback_ports = 2,
- .is_controller = true,
},
- /* FW-1804 may be supported. */
+ {
+ .name = "FW-1804",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 4,
+ },
};
static int identify_model(struct snd_tscm *tscm)
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index 2d028d2bd..30ab77e92 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -39,7 +39,6 @@ struct snd_tscm_spec {
unsigned int pcm_playback_analog_channels;
unsigned int midi_capture_ports;
unsigned int midi_playback_ports;
- bool is_controller;
};
#define TSCM_MIDI_IN_PORT_MAX 4
@@ -72,9 +71,6 @@ struct snd_tscm {
struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
-
- /* For control messages. */
- struct snd_firewire_tascam_status *status;
};
#define TSCM_ADDR_BASE 0xffff00000000ull