From 03dd4cb26d967f9588437b0fc9cc0e8353322bb7 Mon Sep 17 00:00:00 2001 From: AndrĂ© Fabian Silva Delgado Date: Fri, 25 Mar 2016 03:53:42 -0300 Subject: Linux-libre 4.5-gnu --- sound/firewire/Kconfig | 12 +- sound/firewire/Makefile | 2 - sound/firewire/dice/dice-transaction.c | 123 ++++--- sound/firewire/dice/dice.c | 227 ++++++------ sound/firewire/dice/dice.h | 3 + sound/firewire/digi00x/amdtp-dot.c | 2 +- sound/firewire/fireworks/fireworks.h | 4 +- sound/firewire/fireworks/fireworks_midi.c | 16 +- sound/firewire/fireworks/fireworks_pcm.c | 28 +- sound/firewire/fireworks/fireworks_stream.c | 32 +- sound/firewire/oxfw/Makefile | 4 +- sound/firewire/oxfw/oxfw-control.c | 283 --------------- sound/firewire/oxfw/oxfw-scs1x.c | 406 +++++++++++++++++++++ sound/firewire/oxfw/oxfw-spkr.c | 319 +++++++++++++++++ sound/firewire/oxfw/oxfw.c | 110 ++++-- sound/firewire/oxfw/oxfw.h | 23 +- sound/firewire/scs1x.c | 530 ---------------------------- sound/firewire/tascam/tascam-transaction.c | 6 + sound/firewire/tascam/tascam.c | 12 +- sound/firewire/tascam/tascam.h | 4 - 20 files changed, 1077 insertions(+), 1069 deletions(-) delete mode 100644 sound/firewire/oxfw/oxfw-control.c create mode 100644 sound/firewire/oxfw/oxfw-scs1x.c create mode 100644 sound/firewire/oxfw/oxfw-spkr.c delete mode 100644 sound/firewire/scs1x.c (limited to 'sound/firewire') 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-control.c b/sound/firewire/oxfw/oxfw-control.c deleted file mode 100644 index 02a1cb90f..000000000 --- a/sound/firewire/oxfw/oxfw-control.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * oxfw_stream.c - a part of driver for OXFW970/971 based devices - * - * Copyright (c) Clemens Ladisch - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include "oxfw.h" - -enum control_action { CTL_READ, CTL_WRITE }; -enum control_attribute { - CTL_MIN = 0x02, - CTL_MAX = 0x03, - CTL_CURRENT = 0x10, -}; - -static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(11, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - 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[5] = 0x10; /* control attribute: current */ - buf[6] = 0x02; /* selector length */ - buf[7] = 0x00; /* audio channel number */ - buf[8] = 0x01; /* control selector: mute */ - buf[9] = 0x01; /* control data length */ - if (action == CTL_READ) - buf[10] = 0xff; - else - buf[10] = *value ? 0x70 : 0x60; - - err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); - if (err < 0) - goto error; - if (err < 11) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "mute command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = buf[10] == 0x70; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, - unsigned int channel, - enum control_attribute attribute, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(12, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - 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[5] = attribute; /* control attribute */ - buf[6] = 0x02; /* selector length */ - buf[7] = channel; /* audio channel number */ - buf[8] = 0x02; /* control selector: volume */ - buf[9] = 0x02; /* control data length */ - if (action == CTL_READ) { - buf[10] = 0xff; - buf[11] = 0xff; - } else { - buf[10] = *value >> 8; - buf[11] = *value; - } - - err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); - if (err < 0) - goto error; - if (err < 12) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "volume command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = (buf[10] << 8) | buf[11]; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_mute_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - - value->value.integer.value[0] = !oxfw->mute; - - return 0; -} - -static int oxfw_mute_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - bool mute; - int err; - - mute = !value->value.integer.value[0]; - - if (mute == oxfw->mute) - return 0; - - err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); - if (err < 0) - return err; - oxfw->mute = mute; - - return 1; -} - -static int oxfw_volume_info(struct snd_kcontrol *control, - struct snd_ctl_elem_info *info) -{ - struct snd_oxfw *oxfw = control->private_data; - - 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; - - return 0; -} - -static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; - -static int oxfw_volume_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - unsigned int i; - - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - value->value.integer.value[channel_map[i]] = oxfw->volume[i]; - - return 0; -} - -static int oxfw_volume_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - 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) - return -EINVAL; - if (value->value.integer.value[i] != - value->value.integer.value[0]) - equal_values = false; - } - - changed_channels = 0; - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - if (value->value.integer.value[channel_map[i]] != - oxfw->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) { - 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); - if (err < 0) - return err; - } - if (i > 0) - oxfw->volume[i - 1] = volume; - } - - return changed_channels != 0; -} - -int snd_oxfw_create_mixer(struct snd_oxfw *oxfw) -{ - 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, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .info = oxfw_volume_info, - .get = oxfw_volume_get, - .put = oxfw_volume_put, - }, - }; - unsigned int i, first_ch; - int err; - - err = oxfw_volume_command(oxfw, &oxfw->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); - if (err < 0) - return err; - - err = oxfw_mute_command(oxfw, &oxfw->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); - if (err < 0) - return err; - } - - for (i = 0; i < ARRAY_SIZE(controls); ++i) { - err = snd_ctl_add(oxfw->card, - snd_ctl_new1(&controls[i], oxfw)); - if (err < 0) - return err; - } - - return 0; -} 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 + * Copyright (c) 2015 Takashi Sakamoto + * + * 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-spkr.c b/sound/firewire/oxfw/oxfw-spkr.c new file mode 100644 index 000000000..cb905af06 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-spkr.c @@ -0,0 +1,319 @@ +/* + * oxfw-spkr.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#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, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +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; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +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] = !spkr->mute; + + return 0; +} + +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 == spkr->mute) + return 0; + + err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute, + CTL_WRITE); + if (err < 0) + return err; + spkr->mute = mute; + + return 1; +} + +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 = 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 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 < spkr->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = spkr->volume[i]; + + return 0; +} + +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 < 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]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < spkr->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + spkr->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + 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 = avc_audio_feature_volume(oxfw->unit, + spkr->volume_fb_id, &volume, + i, CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + spkr->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +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 = spkr_mute_get, + .put = spkr_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = spkr_volume_info, + .get = spkr_volume_get, + .put = spkr_volume_put, + }, + }; + struct fw_spkr *spkr; + unsigned int i, first_ch; + int err; + + 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 = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id, + &spkr->volume_max, 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute, + CTL_READ); + if (err < 0) + return err; + + 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; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(oxfw->card, + snd_ctl_new1(&controls[i], oxfw)); + if (err < 0) + return err; + } + + return 0; +} 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 "); 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 - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 "); -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) ®, 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 -- cgit v1.2.3-54-g00ecf