From patchwork Sun Mar 15 16:01:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Sakamoto X-Patchwork-Id: 6013451 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 3FDF89F318 for ; Sun, 15 Mar 2015 16:04:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 27F4820274 for ; Sun, 15 Mar 2015 16:04:49 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 90BB120114 for ; Sun, 15 Mar 2015 16:04:47 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id C7139261A7E; Sun, 15 Mar 2015 17:04:46 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id DEE9426129A; Sun, 15 Mar 2015 17:01:38 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 07DDC26128A; Sun, 15 Mar 2015 17:01:36 +0100 (CET) Received: from smtp303.phy.lolipop.jp (smtp303.phy.lolipop.jp [210.157.22.87]) by alsa0.perex.cz (Postfix) with ESMTP id EE432260867 for ; Sun, 15 Mar 2015 17:01:20 +0100 (CET) Received: from smtp303.phy.lolipop.lan (HELO smtp303.phy.lolipop.jp) (172.17.1.87) (smtp-auth username m12129643-o-takashi, mechanism plain) by smtp303.phy.lolipop.jp (qpsmtpd/0.82) with ESMTPA; Mon, 16 Mar 2015 01:01:15 +0900 Received: from 127.0.0.1 (127.0.0.1) by smtp303.phy.lolipop.jp (LOLIPOP-Fsecure); Mon, 16 Mar 2015 01:01:10 +0900 (JST) X-Virus-Status: clean(LOLIPOP-Fsecure) From: Takashi Sakamoto To: clemens@ladisch.de, tiwai@suse.de, robin@gareus.org, damien@zamaudio.com Date: Mon, 16 Mar 2015 01:01:06 +0900 Message-Id: <1426435269-17059-9-git-send-email-o-takashi@sakamocchi.jp> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1426435269-17059-1-git-send-email-o-takashi@sakamocchi.jp> References: <1426435269-17059-1-git-send-email-o-takashi@sakamocchi.jp> Cc: alsa-devel@alsa-project.org, ffado-devel@lists.sf.net Subject: [alsa-devel] [PATCH 08/11] ALSA: digi00x: support MIDI ports for device control X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP Digi 002/003 family uses asynchronous transactions for MIDI device control message. The address to transfer is stored on a certain address, while the address to receive is 0xffffe0000040. This commit supports MIDI ports for this purpose. For capture MIDI message, the handler of notification is extended. For playback MIDI message, a workqueue is used because Linux FireWire subsystem uses 'complete' to wait response event and this context should be able to sleep. Signed-off-by: Takashi Sakamoto --- sound/firewire/digi00x/digi00x-midi.c | 68 +++++++++++++++++++------ sound/firewire/digi00x/digi00x-protocol.c | 82 ++++++++++++++++++++++++++++++- sound/firewire/digi00x/digi00x.h | 7 +++ 3 files changed, 139 insertions(+), 18 deletions(-) diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 460f8eb..c4990ad 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -13,6 +13,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; int err; + /* This port is for Asynchronous transaction. */ + if (substream->number == 0) + return 0; + err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) return err; @@ -32,6 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; int err; + /* This port is for Asynchronous transaction. */ + if (substream->number == 0) + return 0; + err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) return err; @@ -50,6 +58,9 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; + if (substream->number == 0) + return 0; + mutex_lock(&dg00x->mutex); dg00x->capture_substreams--; snd_dg00x_stream_stop_duplex(dg00x); @@ -63,6 +74,9 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data; + if (substream->number == 0) + return 0; + mutex_lock(&dg00x->mutex); dg00x->playback_substreams--; snd_dg00x_stream_stop_duplex(dg00x); @@ -79,12 +93,19 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dg00x->lock, flags); - if (up) - amdtp_stream_midi_trigger(&dg00x->tx_stream, - substrm->number, substrm); - else - amdtp_stream_midi_trigger(&dg00x->tx_stream, - substrm->number, NULL); + if (substrm->number == 0) { + if (up) + dg00x->in_control = substrm; + else + dg00x->in_control = NULL; + } else { + if (up) + amdtp_stream_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, substrm); + else + amdtp_stream_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, NULL); + } spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -96,12 +117,21 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dg00x->lock, flags); - if (up) - amdtp_stream_midi_trigger(&dg00x->rx_stream, - substrm->number, substrm); - else - amdtp_stream_midi_trigger(&dg00x->rx_stream, - substrm->number, NULL); + if (substrm->number == 0) { + if (up) { + dg00x->out_control = substrm; + snd_dg00x_protocol_queue_midi_message(dg00x); + } else { + dg00x->out_control = NULL; + } + } else { + if (up) + amdtp_stream_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, substrm); + else + amdtp_stream_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, NULL); + } spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -124,9 +154,15 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - dg00x->card->shortname, subs->number + 1); + /* This port is for device control. */ + if (subs->number == 0) { + snprintf(subs->name, sizeof(subs->name), + "%s control", dg00x->card->shortname); + } else { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, subs->number + 1); + } } } @@ -137,7 +173,7 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) int err; err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, - 1, 2, &rmidi); + 3, 2, &rmidi); if (err < 0) return err; diff --git a/sound/firewire/digi00x/digi00x-protocol.c b/sound/firewire/digi00x/digi00x-protocol.c index b19708d..3e5b3bec 100644 --- a/sound/firewire/digi00x/digi00x-protocol.c +++ b/sound/firewire/digi00x/digi00x-protocol.c @@ -8,6 +8,43 @@ #include "digi00x.h" +struct workqueue_struct *midi_wq; + +static void send_midi_control(struct work_struct *work) +{ + struct snd_dg00x *dg00x = + container_of(work, struct snd_dg00x, midi_control); + struct fw_device *device = fw_parent_device(dg00x->unit); + + unsigned int len; + __be32 buf = 0; + u8 *b = (u8 *)&buf; + + /* Send MIDI control. */ + if (!dg00x->out_control) + return; + + do { + len = snd_rawmidi_transmit(dg00x->out_control, b + 1, 2); + if (len > 0) { + b[0] = 0x80; + b[3] = 0xc0 | len; + + /* Don't check transaction status. */ + fw_run_transaction(device->card, + TCODE_WRITE_QUADLET_REQUEST, + device->node_id, device->generation, + device->max_speed, + 0xffffe0000400, &buf, sizeof(buf)); + } + } while (len > 0); +} + +void snd_dg00x_protocol_queue_midi_message(struct snd_dg00x *dg00x) +{ + queue_work(midi_wq, &dg00x->midi_control); +} + static struct snd_dg00x *instances[SNDRV_CARDS]; static DEFINE_SPINLOCK(instances_lock); @@ -17,6 +54,26 @@ static void handle_unknown_message(struct snd_dg00x *dg00x, snd_printk(KERN_INFO"%08llx: %08x\n", offset, be32_to_cpu(*buf)); } +static void handle_midi_control(struct snd_dg00x *dg00x, u32 *buf, + unsigned int length) +{ + unsigned int i; + unsigned int len; + u8 *b; + + if (dg00x->in_control == NULL) + return; + + length /= 4; + + for (i = 0; i < length; i++) { + b = (u8 *)&buf[i]; + len = b[3] & 0xf; + if (len > 0) + snd_rawmidi_receive(dg00x->in_control, b + 1, len); + } +} + static void handle_message(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, unsigned long long offset, @@ -43,6 +100,8 @@ static void handle_message(struct fw_card *card, struct fw_request *request, if (offset == 0xffffe0000000) handle_unknown_message(dg00x, offset, buf); + else if (offset == 0xffffe0000004) + handle_midi_control(dg00x, buf, length); spin_unlock_irq(&instances_lock); fw_send_response(card, request, RCODE_COMPLETE); @@ -70,6 +129,15 @@ int snd_dg00x_protocol_add_instance(struct snd_dg00x *dg00x) if (err < 0) return err; + /* Asynchronous transactions for MIDI control message. 8 bytes. */ + data[0] = cpu_to_be32((device->card->node_id << 16) | + (async_handler.offset >> 32)); + data[1] = cpu_to_be32(async_handler.offset + 4); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, + 0xffffe0000008ull, &data, sizeof(data), 0); + if (err < 0) + return err; + spin_lock_irq(&instances_lock); for (i = 0; i < SNDRV_CARDS; i++) { if (instances[i] != NULL) @@ -79,6 +147,8 @@ int snd_dg00x_protocol_add_instance(struct snd_dg00x *dg00x) } spin_unlock_irq(&instances_lock); + INIT_WORK(&dg00x->midi_control, send_midi_control); + return 0; } @@ -104,19 +174,27 @@ int snd_dg00x_protocol_register(void) }; int err; - async_handler.length = 4; + midi_wq = alloc_workqueue("snd-digi00x", + WQ_SYSFS | WQ_POWER_EFFICIENT, 0); + if (midi_wq == NULL) + return -ENOMEM; + + async_handler.length = 12; async_handler.address_callback = handle_message; async_handler.callback_data = NULL; err = fw_core_add_address_handler(&async_handler, &resp_register_region); - if (err < 0) + if (err < 0) { + destroy_workqueue(midi_wq); return err; + } return 0; } void snd_dg00x_protocol_unregister(void) { + destroy_workqueue(midi_wq); fw_core_remove_address_handler(&async_handler); } diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 85cfb39..20b178f 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,11 @@ struct snd_dg00x { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + + /* For asynchronous MIDI controls. */ + struct work_struct midi_control; + struct snd_rawmidi_substream *in_control; + struct snd_rawmidi_substream *out_control; }; /* values for SND_DG00X_ADDR_OFFSET_RATE */ @@ -78,6 +84,7 @@ enum snd_dg00x_optical_mode { SND_DG00X_OPTICAL_MODE_SPDIF, }; +void snd_dg00x_protocol_queue_midi_message(struct snd_dg00x *dg00x); int snd_dg00x_protocol_add_instance(struct snd_dg00x *dg00x); void snd_dg00x_protocol_remove_instance(struct snd_dg00x *dg00x); int snd_dg00x_protocol_register(void);